--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.call/src/com/nokia/carbide/cpp/pi/call/GfcTrace.java Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:
+ *
+ */
+
+package com.nokia.carbide.cpp.pi.call;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import com.nokia.carbide.cpp.internal.pi.analyser.NpiInstanceRepository;
+import com.nokia.carbide.cpp.internal.pi.model.Function;
+import com.nokia.carbide.cpp.internal.pi.model.FunctionResolver;
+import com.nokia.carbide.cpp.internal.pi.model.GenericSample;
+import com.nokia.carbide.cpp.internal.pi.model.GenericSampledTraceWithFunctions;
+import com.nokia.carbide.cpp.internal.pi.utils.QuickSortImpl;
+import com.nokia.carbide.cpp.internal.pi.utils.Sortable;
+import com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph;
+
+
+public class GfcTrace extends GenericSampledTraceWithFunctions
+{
+ private static final long serialVersionUID = -8144591614894549185L;
+
+ private long previousSample;
+
+ private Hashtable parsedGfcTrace;
+ private Vector completeGfcTrace;
+ private int firstSample;
+ private int lastSample;
+
+ private transient CallVisualiser callVisualiser;
+
+ private transient boolean completeTrace = true;
+ private transient boolean completeSamples = true;
+ private transient int samplingInterval;
+
+ public static final int SORT_BY_TOTAL_LOAD = 1;
+ public static final int SORT_BY_CALLED_LOAD = 2;
+ public static final int SORT_BY_RECURSIVE_LOAD = 3;
+
+ public GfcTrace(int size)
+ {
+ this.completeGfcTrace = new Vector(size);
+ this.samples = new Vector(size);
+ previousSample = 0;
+ }
+
+ public void addSample(GfcSample sample, Long[] element)
+ {
+ this.samples.add(sample);
+
+ long sampleNumber = sample.sampleSynchTime;
+
+ if (previousSample != 0)
+ {
+ if (sampleNumber != previousSample + samplingInterval)
+ System.out.println(Messages.getString("GfcTrace.missingSample1") + previousSample + Messages.getString("GfcTrace.missingSample2") + sample); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ previousSample = sampleNumber;
+
+ if (this.firstSample == -1)
+ this.firstSample = (int)sampleNumber;
+ else
+ this.lastSample = (int)sampleNumber;
+ /*
+ System.out.print("\n\nSample: "+sample+" pc: "+programCounter+" lr: "+linkRegister);
+ System.out.print("\nSample: "+sample+
+ "\npc: "+symbolParser.getFunctionForAddress(programCounter).functionName+
+ "\nlr: "+symbolParser.getFunctionForAddress(linkRegister).functionName);
+ */
+ this.completeGfcTrace.add(element);
+ }
+
+ public Hashtable parseEntries(int startSample, int endSample)
+ {
+ // code to avoid stack overflow in saving analysis,
+ // with parameters -20,-20 , the parsedGfcTrace field is cleared,
+ // and it is restored with values -10,-10
+ if (startSample == -20 && endSample == -20)
+ {
+ if (this.parsedGfcTrace == null)
+ {
+ return null;
+ }
+ else
+ {
+ this.parsedGfcTrace = null;
+ return new Hashtable();
+ }
+ }
+ else if (startSample == -10 && endSample == -10)
+ {
+ this.parseEntries(this.firstSample, this.lastSample);
+ return null;
+ }
+
+ int samplingInterval = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.samplingInterval"); //$NON-NLS-1$
+
+ if (startSample < 1)
+ startSample = 0;
+
+ this.firstSample = startSample / samplingInterval;
+ this.lastSample = endSample / samplingInterval;
+
+ this.parsedGfcTrace = new Hashtable();
+
+ if (this.completeTrace) {
+ // adjust the sample search
+ if (startSample > this.completeGfcTrace.size())
+ startSample = this.completeGfcTrace.size();
+ if (endSample > this.completeGfcTrace.size())
+ endSample = this.completeGfcTrace.size();
+
+ for (int i = startSample; i < endSample; i++) {
+ Long[] data = (Long[])this.completeGfcTrace.elementAt(i);
+ this.parseOneEntry(data[0], data[1], data[2]);
+ }
+ } else if (endSample >= samplingInterval) {
+ // sample is incomplete, so find the sample nearest but after the start
+ int nearest = 0;
+ if (startSample >= samplingInterval) {
+ int last = 0;
+ int half = this.completeGfcTrace.size() / 2;
+ nearest = this.completeGfcTrace.size() - 1;
+ long time = ((Long[]) this.completeGfcTrace.get(nearest))[0];
+
+ while (time != startSample && half > 0) {
+ if (time < startSample)
+ nearest = nearest + half;
+ else
+ nearest = nearest - half;
+ half /= 2;
+ time = ((Long[]) this.completeGfcTrace.get(nearest))[0];
+ }
+
+ while (nearest > 0 && time > startSample) {
+ nearest--;
+ time = ((Long[]) this.completeGfcTrace.get(nearest))[0];
+ }
+
+ while (nearest < this.completeGfcTrace.size() - 1 && time + samplingInterval < startSample) {
+ nearest++;
+ time = ((Long[]) this.completeGfcTrace.get(nearest))[0];
+ }
+ }
+
+ // we're starting at the right spot, so collect data (don't worry that we get the first one twice - we only parse it once
+ Long[] data = (Long[])this.completeGfcTrace.get(nearest);
+ for ( ; data[0].longValue() <= endSample && nearest < this.completeGfcTrace.size(); nearest++) {
+ data = (Long[])this.completeGfcTrace.get(nearest);
+ this.parseOneEntry(data[0], data[1], data[2]);
+ }
+ }
+
+ countRecursiveLoad();
+
+ return this.parsedGfcTrace;
+ }
+
+ private class CallerCallee {
+ String functionName;
+ Long startAdress;
+ String binaryName;
+ boolean symbolParsed;
+ }
+
+ private void parseOneEntry(long sample, long programCounter, long linkRegister)
+ {
+ GfcSample gSam = null;
+
+ if (this.completeSamples)
+ {
+ // find it directly
+ if (sample <= this.getLastSampleNumber())
+ gSam = (GfcSample)this.samples.elementAt((int) (sample - 1));
+ } else {
+ // find it the slow way
+ if (this.getLastSampleNumber() <= sample + 1 || this.getFirstSampleNumber() > sample)
+ return;
+
+ GenericSample[] samples = this.getSamplesForTime(sample);
+ if ((samples != null) && (samples.length != 0))
+ gSam = (GfcSample)samples[0];
+ }
+
+ if (gSam == null) {
+ System.out.println(Messages.getString("GfcTrace.sampleNotFound") + sample); //$NON-NLS-1$
+ return;
+ }
+
+ CallerCallee caller = new CallerCallee();
+ CallerCallee callee = new CallerCallee();
+
+ if ( (gSam.callerFunctionSym == null || gSam.callerFunctionSym.functionName.endsWith(Messages.getString("GfcTrace.functionNotFound"))) //$NON-NLS-1$
+ && gSam.callerFunctionItt != null) //$NON-NLS-1$
+ {
+ caller.functionName = gSam.callerFunctionItt.functionName;
+ caller.startAdress = gSam.callerFunctionItt.startAddress;
+ caller.binaryName = gSam.callerFunctionItt.functionBinary.binaryName;
+ caller.symbolParsed = false;
+ }
+ else
+ {
+ caller.functionName = gSam.callerFunctionSym.functionName;
+ caller.startAdress = gSam.callerFunctionSym.startAddress;
+ caller.binaryName = gSam.callerFunctionSym.functionBinary.binaryName;
+ caller.symbolParsed = Boolean.valueOf(true);
+ }
+
+ if ( (gSam.currentFunctionSym == null || gSam.currentFunctionSym.functionName.endsWith(Messages.getString("GfcTrace.functionNotFound"))) //$NON-NLS-1$
+ && gSam.currentFunctionItt != null) //$NON-NLS-1$
+ {
+ callee.functionName = gSam.currentFunctionItt.functionName;
+ callee.startAdress = gSam.currentFunctionItt.startAddress;
+ callee.binaryName = gSam.currentFunctionItt.functionBinary.binaryName;
+ callee.symbolParsed = Boolean.valueOf(false);
+ }
+ else
+ {
+ callee.functionName = gSam.currentFunctionSym.functionName;
+ callee.startAdress = gSam.currentFunctionSym.startAddress;
+ callee.binaryName = gSam.currentFunctionSym.functionBinary.binaryName;
+ callee.symbolParsed = Boolean.valueOf(true);
+ }
+
+ this.updateEntryLists(sample, programCounter, linkRegister, caller, callee);
+ }
+
+ private void updateEntryLists(long sample, long programCounter, long linkRegister, CallerCallee callerObj, CallerCallee calleeObj)
+ {
+ String callerName = callerObj.functionName;
+ Long callerStart = callerObj.startAdress;
+ String callerDllName = callerObj.binaryName;
+ boolean callerSymbolParsed = callerObj.symbolParsed;
+
+ String calleeName = calleeObj.functionName;
+ Long calleeStart = calleeObj.startAdress;
+ String calleeDllName = calleeObj.binaryName;
+ boolean calleeSymbolParsed = calleeObj.symbolParsed;
+
+ GfcFunctionItem caller;
+ GfcFunctionItem callee;
+
+ // is the callee in the list
+ if (parsedGfcTrace.containsKey(calleeStart))
+ {
+ // the callee is in the list
+ callee = (GfcFunctionItem)this.parsedGfcTrace.get(calleeStart);
+ }
+ else
+ {
+ // the callee is not in the list
+ callee = new GfcFunctionItem(calleeName, calleeStart.longValue(), calleeDllName, calleeSymbolParsed);
+ this.parsedGfcTrace.put(calleeStart, callee);
+ }
+
+ // is the caller in the list
+ if (this.parsedGfcTrace.containsKey(callerStart))
+ {
+ // the caller is in the list
+ caller = (GfcFunctionItem)this.parsedGfcTrace.get(callerStart);
+ }
+ else
+ {
+ // no, we have to add the caller to the list as well
+ caller = new GfcFunctionItem(callerName, callerStart.longValue(), callerDllName, callerSymbolParsed);
+ this.parsedGfcTrace.put(callerStart, caller);
+ }
+
+ if (!caller.equals(callee))
+ {
+ callee.addCaller(caller, new Integer((int)sample));
+ caller.addCallee(callee, new Integer((int)sample));
+ }
+ }
+
+ public void countRecursiveLoad()
+ {
+ //if (exclude == null)
+ ArrayList exclude = new ArrayList();
+
+ GfcFunctionItem[] sorted = this.getEntriesSorted(SORT_BY_CALLED_LOAD);
+
+ if (sorted.length == 0)
+ return;
+
+ int index = sorted.length - 1;
+ int lastExclude = 0;
+
+ // add the load of this function to the accumulated load
+ for (int i = 0; i < sorted.length; i++)
+ {
+ GfcFunctionItem gfi = sorted[i];
+ gfi.addRecursiveLoad(gfi.countSamplesThatCallThisFunction());
+ gfi.setRecursiveLoad(0);
+ }
+
+ while (true)
+ {
+ GfcFunctionItem item = sorted[index];
+ item.storePercents(-1, -1, -1, this.firstSample, this.lastSample);
+
+ index--;
+ if (index == -1)
+ {
+ index = sorted.length - 1;
+ if (lastExclude == exclude.size())
+ {
+ break;
+ }
+ else
+ {
+ lastExclude = exclude.size();
+ }
+ }
+
+ if (!exclude.contains(item))
+ {
+ double load = item.countSamplesThatCallThisFunction();
+
+ GfcFunctionItem[] callers = item.getCallerList();
+ Double[] percentages = item.getCallerPercentages();
+
+ for (int i = 0; i < callers.length; i++)
+ {
+ //System.out.println(item.name+" load:"+load+" Adding "+load*(percentages[i].doubleValue()/100)+" to"+callers[i].name);
+ callers[i].addRecursiveLoad(load*(percentages[i].doubleValue()/100));
+
+ // there is some accumulated recursive load to distribute
+ // between the callers
+ if (item.getRecursiveLoad() > 0)
+ {
+ double recursiveLoadToShare = item.getRecursiveLoad()*(percentages[i].doubleValue()/100);
+
+ // add recursive load only if the caller is not the function itself
+ if (!callers[i].equals(item))
+ {
+ callers[i].addRecursiveLoad(recursiveLoadToShare);
+ }
+ }
+ }
+ // clear the recursive load
+ item.setRecursiveLoad(0);
+ }
+
+ // this function does not call any other functions so it can be added
+ // to the excluded functions list
+ if (item.getCalleeList().length == 0)
+ {
+ if (!exclude.contains(item))
+ exclude.add(item);
+ //sorted.remove(item);
+ }
+ else
+ {
+ GfcFunctionItem[] callees = item.getCalleeList();
+ int i;
+ for (i = 0; i < callees.length; i++)
+ {
+ // break if any item is found that is not excluded
+ if (!exclude.contains(callees[i]))
+ break;
+ }
+
+ // there are only excluded items in this function's call list,
+ // thus it can also be excluded
+ if (i == callees.length)
+ {
+ if (!exclude.contains(item))
+ exclude.add(item);
+ //sorted.remove(item);
+ }
+ }
+ }
+ }
+
+ public GfcFunctionItem[] getEntriesSorted(final int sortMethod)
+ {
+ class ItemSorter implements Sortable
+ {
+ GfcFunctionItem item;
+
+ double itemTotal;
+ double itemCaller;
+ double itemRecursive;
+
+ public ItemSorter(GfcFunctionItem item)
+ {
+ this.item = item;
+ // calculate and store the percentages for the items for the selected period
+ itemTotal = item.getAbsoluteTotalPercentage(firstSample, lastSample);
+ itemCaller = item.getAbsoluteCallerPercentage(firstSample, lastSample);
+ itemRecursive = item.getRecursiveCallerPercentage(firstSample, lastSample);
+
+ if (itemTotal == -1 || itemCaller == -1 || itemRecursive == -1)
+ {
+ itemTotal = getAbsoluteTraditionalPercentageFor(item);
+ itemCaller = getAbsoluteCallerPercentageFor(item);
+ itemRecursive = getRecursiveCallerPrecentageFor(item);
+
+ item.storePercents(itemTotal, itemCaller, itemRecursive,
+ firstSample, lastSample);
+ }
+ }
+
+ public long valueOf()
+ {
+ if (sortMethod == GfcTrace.SORT_BY_TOTAL_LOAD)
+ {
+ return (long)(1000 * itemTotal);
+ }
+ else if (sortMethod == GfcTrace.SORT_BY_CALLED_LOAD)
+ {
+ return (long)(1000 * itemCaller);
+ }
+ else if (sortMethod == GfcTrace.SORT_BY_RECURSIVE_LOAD)
+ {
+ return (long)(1000 * itemRecursive);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ Enumeration elements = this.parsedGfcTrace.elements();
+
+ Vector s = new Vector();
+ while (elements.hasMoreElements())
+ {
+ ItemSorter is = new ItemSorter((GfcFunctionItem)elements.nextElement());
+ s.add(is);
+ }
+
+ QuickSortImpl.sortReversed(s);
+
+ elements = s.elements();
+ this.parsedGfcTrace.clear();
+
+ GfcFunctionItem[] f = new GfcFunctionItem[s.size()];
+ int i = 0;
+ while (elements.hasMoreElements())
+ {
+ ItemSorter is = (ItemSorter)elements.nextElement();
+ f[i++] = is.item;
+ this.parsedGfcTrace.put(new Long(is.item.address), is.item);
+ }
+
+ return f;
+ }
+
+ public double getAbsoluteTraditionalPercentageFor(GfcFunctionItem gfi)
+ {
+ double samples = gfi.countSamplesThatCallThisFunction();
+ double total = this.lastSample - this.firstSample;
+ return 100 * (samples / total);
+ }
+
+ public double getAbsoluteCallerPercentageFor(GfcFunctionItem gfi)
+ {
+ double samples = gfi.countSamplesWhereThisFunctionIsTheCaller();
+ double total = this.lastSample - this.firstSample;
+ return 100 * (samples / total);
+ }
+
+ public double getRecursiveCallerPrecentageFor(GfcFunctionItem gfi)
+ {
+ double samples = gfi.getAccumulatedLoad();
+ double total = this.lastSample - this.firstSample;
+ return 100 * (samples / total);
+ }
+
+ /*
+ * Check if the function call sample set and trace are complete
+ * (first sample is at time 1, sample N is at time N)
+ */
+ public void setComplete()
+ {
+ Object intervalObject = NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.samplingInterval"); //$NON-NLS-1$
+ int samplingInterval = intervalObject != null ? (Integer) intervalObject : -1;
+ int size;
+
+ this.completeTrace = true;
+
+ size = this.completeGfcTrace.size();
+ for (int i = 0; i < size; i++) {
+ Long[] element = (Long[])this.completeGfcTrace.elementAt(i);
+ if (element[0].longValue() != (i + 1)*samplingInterval) {
+ this.completeTrace = false;
+ break;
+ }
+ }
+
+ this.completeSamples = true;
+
+ size = this.samples.size();
+ for (int i = 0; i < size; i++) {
+ GfcSample element = (GfcSample)this.samples.elementAt(i);
+ if (element.sampleSynchTime != (i + 1)*samplingInterval) {
+ this.completeSamples = false;
+ break;
+ }
+ }
+ }
+
+ // this method is not used
+ public GenericTraceGraph getTraceGraph()
+ {
+ return null;
+ }
+
+ // this method is not used
+ public GenericTraceGraph getTraceGraph(int graphNumber)
+ {
+ return null;
+ }
+
+ public CallVisualiser getCallVisualiser()
+ {
+ return this.callVisualiser;
+ }
+
+ public void setCallVisualiser(CallVisualiser callVisualiser)
+ {
+ this.callVisualiser = callVisualiser;
+ }
+
+ public void setSamplingInterval(int samplingInterval) {
+ this.samplingInterval = samplingInterval;
+ }
+
+ public void refineTrace(FunctionResolver resolver)
+ {
+ GfcSample.setAddressTraceSamples();
+ super.refineTrace(resolver);
+ }
+
+ public Vector getCompleteGfcTrace()
+ {
+ return this.completeGfcTrace;
+ }
+
+ @Override
+ public void finalizeTrace()
+ {
+ for (int i = 0; i < samples.size(); i++) {
+ GfcSample sample = (GfcSample) samples.get(i);
+
+ if (sample.currentFunctionItt == null && sample.currentFunctionSym == null) {
+ sample.currentFunctionItt = new Function(Messages.getString("GfcTrace.functionNotFound1") + Long.toHexString(sample.programCounter) + Messages.getString("GfcTrace.functionNotFound2"), //$NON-NLS-1$ //$NON-NLS-2$
+ new Long(sample.programCounter),
+ Messages.getString("GfcTrace.binaryNotFound1") + Long.toHexString(sample.programCounter) + Messages.getString("GfcTrace.binaryNotFound2")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (sample.callerFunctionItt == null && sample.callerFunctionSym == null) {
+ sample.callerFunctionItt = new Function(Messages.getString("GfcTrace.functionNotFound1") + Long.toHexString(sample.linkRegister) + Messages.getString("GfcTrace.functionNotFound2"), //$NON-NLS-1$ //$NON-NLS-2$
+ new Long(sample.linkRegister),
+ Messages.getString("GfcTrace.binaryNotFound1") + Long.toHexString(sample.linkRegister) + Messages.getString("GfcTrace.binaryNotFound2")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+}