sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.address/src/com/nokia/carbide/cpp/pi/address/GppTrace.java
changeset 2 b9ab3b238396
child 5 844b047e260d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.address/src/com/nokia/carbide/cpp/pi/address/GppTrace.java	Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,1118 @@
+/*
+ * 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.address;
+
+import java.util.Arrays;
+import java.util.Comparator;
+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.GenericSampledTraceWithFunctions;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledBinary;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledFunction;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledGeneric;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledThread;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledThreshold;
+import com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph;
+import com.nokia.carbide.cpp.pi.editors.PIPageEditor;
+import com.nokia.carbide.cpp.pi.util.BinaryColorPalette;
+import com.nokia.carbide.cpp.pi.util.FunctionColorPalette;
+import com.nokia.carbide.cpp.pi.util.ThreadColorPalette;
+
+
+public class GppTrace extends GenericSampledTraceWithFunctions
+{
+	private static final long serialVersionUID = -658505849351283165L;
+	
+	// sample times start at a set interval, monotonically increase by that interval, and end at a
+	// time equal to (number of elements - 1) times the interval
+	private boolean complete;
+	
+	private transient GppSample[] sortedSamples = null;
+
+	// unchanging set of objects in the trace, ordered by total load
+	private transient Vector<ProfiledGeneric> sortedProfiledThreads;
+	private transient Vector<ProfiledGeneric> sortedProfiledBinaries;
+	private transient Vector<ProfiledGeneric> sortedProfiledFunctions;
+	
+	// unchanging set of objects in the trace, sorted by index
+	private transient Vector<ProfiledGeneric> profiledThreads;
+	private transient Vector<ProfiledGeneric> profiledBinaries;
+	private transient Vector<ProfiledGeneric> profiledFunctions;
+	
+	// selection time based objects in the trace
+	private transient int startSampleIndex;	// first sample in the selection
+	private transient int endSampleIndex;		// last sample in the selection
+
+	// sample counts for the currently selected area of the graph 
+	private transient int[] threadSamples;
+	private transient int[] binarySamples;
+	private transient int[] functionSamples;
+
+	private transient GppTraceGraph[] graphs;
+	
+	// tie palette to a trace instead of graph since trace is the data it represents
+	private transient ThreadColorPalette threadColorPalette = null;
+	private transient BinaryColorPalette binaryColorPalette = null;
+	private transient FunctionColorPalette functionColorPalette = null;
+	
+	//protected int uid;
+	
+	public GppTrace() 
+	{
+	}
+	
+	// for fast access, created a sorted array of samples
+	// this will remove duplicate times, find if times are missing, and
+	// find if times are increasing  
+	public void sortGppSamples()
+	{
+		// check if already sorted
+		if (this.sortedSamples != null)
+			return;
+		
+		int samplingInterval = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.samplingInterval"); //$NON-NLS-1$
+
+		complete = this.samples.size()*samplingInterval == this.getLastSampleNumber();
+		
+		this.sortedSamples = new GppSample[this.samples.size()];
+	
+		// fill the sorted array, assuming each sample time matches its (index+1)*samplingInterval
+		boolean sorted = true;
+		int arrayIndex = 0;
+		long lastTime = -1;
+		
+		for (int i = 0, sampleTime = 0; i < this.samples.size(); i++) {
+			Object o = this.samples.get(i);
+			if (o instanceof GppSample) {
+				  GppSample sample = (GppSample)o;
+
+				  // don't copy duplicates
+				  if (sample.sampleSynchTime == lastTime)
+					  continue;
+
+				  this.sortedSamples[arrayIndex++] = sample;
+				  sampleTime += samplingInterval;
+				  
+				  // make sure times are in increasing order
+				  if (sample.sampleSynchTime < lastTime)
+					  sorted = false;
+				  
+				  lastTime = sample.sampleSynchTime;
+				  
+				  if (sample.sampleSynchTime != sampleTime) {
+					  this.complete = false;
+					  
+					  // find the time it does match, in case one is missing
+					  while (sample.sampleSynchTime > sampleTime)
+						  sampleTime += samplingInterval;
+				  }
+			} else {
+				// error case
+				this.sortedSamples = new GppSample[1];
+				this.sortedSamples[0].sampleSynchTime = -1;
+				this.complete = false;
+				return;
+			}
+		}
+		
+		// if any duplicates, create a shorter array
+		if (arrayIndex != this.samples.size()) {
+			GppSample[] sampleObjects1 = new GppSample[this.samples.size()];
+			for (int i = 0; i < this.sortedSamples.length; i++)
+				sampleObjects1[i] = this.sortedSamples[i];
+			this.sortedSamples = sampleObjects1;
+		}
+		
+		if (!sorted) {
+			// now we have to actually sort
+			Arrays.sort(this.sortedSamples, new Comparator<Object>() {
+	
+				public int compare(Object arg0, Object arg1)
+				{
+					return (int) (((GppSample)arg0).sampleSynchTime - ((GppSample)arg1).sampleSynchTime);
+				}
+			});
+
+			// get rid of any duplicates and check for completeness
+			for (int i = 0, sampleTime = 0, length = this.sortedSamples.length; i < length; i++) {
+				GppSample sample = this.sortedSamples[i];
+
+				// don't copy duplicates
+				if (sample.sampleSynchTime == lastTime) {
+					for (int j = i; j < length - 1; j++) {
+						this.sortedSamples[j] = this.sortedSamples[j + 1];
+					}
+					length--;
+					this.complete = false;
+					continue;
+				}
+				
+				sampleTime += samplingInterval;
+				
+				if (sample.sampleSynchTime != sampleTime) {
+					  this.complete = false;
+						  
+					  // find the time it does match, in case one is missing
+					  while (sample.sampleSynchTime > sampleTime)
+						  sampleTime += samplingInterval;
+			    }
+			}
+		}
+	}
+	
+	public boolean isGppSampleComplete()
+	{
+		return this.complete;
+	}
+	
+	public GppSample[] getSortedGppSamples()
+	{
+		return this.sortedSamples;
+	}
+	
+	public GppSample getGppSample(int number)
+	{
+		return (GppSample)this.samples.elementAt(number);
+	}
+	
+	public GenericTraceGraph getTraceGraph(int graphIndex, int uid)
+	{	
+		return getGppGraph(graphIndex,uid);
+	}
+	
+	public GppTraceGraph getGppGraph(int graphIndex, int uid)
+	{
+		if (graphs == null) {
+			graphs = new GppTraceGraph[3];
+		}
+			
+		// note that graphIndex needs not match the index sent to GppTraceGraph
+		if (   (graphIndex == PIPageEditor.THREADS_PAGE)
+			|| (graphIndex == PIPageEditor.BINARIES_PAGE)
+			|| (graphIndex == PIPageEditor.FUNCTIONS_PAGE)) {
+			if (graphs[graphIndex] == null)
+				graphs[graphIndex] = new GppTraceGraph(graphIndex, this, uid);
+			return graphs[graphIndex];
+		}
+	
+		return null;
+	}
+	
+	public GenericTraceGraph getTraceGraph(int graphIndex)
+	{
+		int uid = NpiInstanceRepository.getInstance().activeUid();
+		return getTraceGraph(graphIndex, uid);
+	}
+	
+	public void refineTrace(FunctionResolver resolver)
+	{
+		super.refineTrace(resolver);
+	}
+
+	public int getSortedThreadsCount()
+	{
+		return this.sortedProfiledThreads.size();
+	}
+	
+	public Enumeration<ProfiledGeneric> getSortedThreadsElements()
+	{
+		return this.sortedProfiledThreads.elements();
+	}
+
+	public Vector<ProfiledGeneric> getSortedThreads()
+	{
+		if (this.sortedProfiledThreads == null)
+			this.sortedProfiledThreads = new Vector<ProfiledGeneric>();
+		return this.sortedProfiledThreads;
+	}
+
+	public Vector<ProfiledGeneric> getIndexedThreads()
+	{
+		if (this.profiledThreads == null)
+			this.profiledThreads = new Vector<ProfiledGeneric>();
+		return this.profiledThreads;
+	}
+
+	public int getSortedBinariesCount()
+	{
+	    return this.sortedProfiledBinaries.size();
+	}
+	
+	public Enumeration<ProfiledGeneric> getSortedBinariesElements()
+	{
+	    return this.sortedProfiledBinaries.elements();
+	}
+	
+	public Vector<ProfiledGeneric> getSortedBinaries()
+	{
+		if (this.sortedProfiledBinaries == null)
+			this.sortedProfiledBinaries = new Vector<ProfiledGeneric>();
+	    return this.sortedProfiledBinaries;
+	}
+
+	public Vector<ProfiledGeneric> getIndexedBinaries()
+	{
+		if (this.profiledBinaries == null)
+			this.profiledBinaries = new Vector<ProfiledGeneric>();
+	    return this.profiledBinaries;
+	}
+
+	public int getSortedFunctionsCount()
+	{
+	    return this.sortedProfiledFunctions.size();
+	}
+	
+	public Enumeration<ProfiledGeneric> getSortedFunctionsElements()
+	{
+        return this.sortedProfiledFunctions.elements();
+	}
+	
+	public Vector<ProfiledGeneric> getSortedFunctions()
+	{
+		if (this.sortedProfiledFunctions == null)
+			this.sortedProfiledFunctions = new Vector<ProfiledGeneric>();
+		return this.sortedProfiledFunctions;
+	}
+	
+	public Vector<ProfiledGeneric> getIndexedFunctions()
+	{
+		if (this.profiledFunctions == null)
+			this.profiledFunctions = new Vector<ProfiledGeneric>();
+		return this.profiledFunctions;
+	}
+
+	/*
+	 *	Determine the threads, binaries, and functions associated with a time period
+	 *  from the start time up to and including the end time. If the times are equal,
+	 *  do not include any samples.
+	 */
+	public void setSelectedArea() {
+		// create empty arrays to hold sample counts
+		this.threadSamples   = new int[this.profiledThreads.size()];
+		this.binarySamples   = new int[this.profiledBinaries.size()];
+		this.functionSamples = new int[this.profiledFunctions.size()];
+
+		double doubleStartTime = PIPageEditor.currentPageEditor().getStartTime();
+		double doubleEndTime   = PIPageEditor.currentPageEditor().getEndTime();
+
+		int samplingInterval = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.samplingInterval"); //$NON-NLS-1$
+
+		GppSample[] sortedGppSamples = this.getSortedGppSamples();
+
+		// get the sample start time in integer multiples of milliseconds
+		startSampleIndex = ((int) ((doubleStartTime + 0.0005f)* 1000.0f))/samplingInterval;
+		if (startSampleIndex*samplingInterval < this.getFirstSampleNumber()) {
+			startSampleIndex = 0;
+		} else if (startSampleIndex > sortedGppSamples.length) {
+			startSampleIndex = sortedGppSamples.length - 1;
+		}
+			
+		// get the sample end time in integer multiples of milliseconds
+		// NOTE: endSampleIndex is one past the last allowed index, so that when no time
+		// is selected startSampleIndex will equal end sample index
+		endSampleIndex = ((int) ((doubleEndTime + 0.0005f) * 1000.0f))/samplingInterval;
+		if (endSampleIndex*samplingInterval < this.getFirstSampleNumber()) {
+			endSampleIndex = 0;
+		} else if (endSampleIndex > sortedGppSamples.length) {
+			endSampleIndex = sortedGppSamples.length - 1;
+		}
+
+		if (this.isGppSampleComplete()) {
+			// find the sample counts in each category
+			// just use the start and end times as indices
+			for (int i = startSampleIndex; i < endSampleIndex; i++) {
+				threadSamples[sortedGppSamples[i].threadIndex]++;
+				binarySamples[sortedGppSamples[i].binaryIndex]++;
+				functionSamples[sortedGppSamples[i].functionIndex]++;
+			}
+		} else {
+			// use a binary search to find the first sample
+			int startIndex = 0;
+			GppSample sample = null;
+			int lowerBound = 0;
+			int upperBound = sortedGppSamples.length;
+		    while (lowerBound <= upperBound) {
+		    	startIndex = (lowerBound + upperBound)/2;
+				sample = sortedGppSamples[startIndex];
+				if (startSampleIndex*samplingInterval == sample.sampleSynchTime) {
+		            break;
+				} else if (sample.sampleSynchTime > startSampleIndex*samplingInterval)
+		            upperBound = startIndex - 1;
+		        else
+		        	lowerBound = startIndex + 1;
+		    }
+		    
+		    // if there is no match, it's okay if the sample's time is larger than the
+		    // startTime, but not if the startTime is less
+		    if (sample.sampleSynchTime < startSampleIndex*samplingInterval)
+		    	startIndex++;
+	    	endSampleIndex = startIndex;
+		    
+			// find the sample counts in each category
+	    	// use comparisons to find the end sample
+		    if (startIndex < this.samples.size()) {
+				while (sample.sampleSynchTime < endSampleIndex*samplingInterval) {
+					threadSamples[sample.threadIndex]++;
+					binarySamples[sample.binaryIndex]++;
+					functionSamples[sample.functionIndex]++;
+	
+					endSampleIndex++;
+					if (endSampleIndex == sortedGppSamples.length)
+						break;
+	
+					sample = sortedGppSamples[endSampleIndex];
+				}
+				
+				if (   (endSampleIndex == this.samples.size())
+					|| (sample.sampleSynchTime > endSampleIndex*samplingInterval))
+					endSampleIndex--;
+		    }
+		    endSampleIndex++;
+		}
+		
+		// set the sample counts and loads for all the trace-related graphs
+		// set the % load strings only for a tab's base table
+		// To optimise this, we could just set the main sample count and load per graph (e.g., thread stuff for page 0)
+		// To ptimise this, we could check drawMode and ignore sample counts when tables don't show them
+		// requires sample counts, a
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		// NOTE: For slightly better performance, might have other functions rely on this one zeroing out sample counts
+		for (int i = 0; i < this.profiledThreads.size(); i++) {
+			ProfiledGeneric pThread = this.profiledThreads.elementAt(i);
+			pThread.setSampleCount(PIPageEditor.THREADS_PAGE, this.threadSamples[i]);
+			pThread.setLoadAndString(PIPageEditor.THREADS_PAGE, (float)(this.threadSamples[i] * percentPerSample));
+		}
+
+		for (int i = 0; i < this.profiledBinaries.size(); i++) {
+			ProfiledGeneric pBinary = this.profiledBinaries.elementAt(i);
+			pBinary.setSampleCount(PIPageEditor.BINARIES_PAGE, this.binarySamples[i]);
+			pBinary.setLoadAndString(PIPageEditor.BINARIES_PAGE, (float)(this.binarySamples[i] * percentPerSample));
+		}
+
+		for (int i = 0; i < this.profiledFunctions.size(); i++) {
+			ProfiledGeneric pFunction = this.profiledFunctions.elementAt(i);
+			pFunction.setSampleCount(PIPageEditor.FUNCTIONS_PAGE, this.functionSamples[i]);
+			pFunction.setLoadAndString(PIPageEditor.FUNCTIONS_PAGE, (float)(this.functionSamples[i] * percentPerSample));
+		}		
+	}
+
+	/*
+	 * Based on a graph's set of enabled threads, produce a set of binaries, and
+	 * disable all other binaries for that graph
+	 */
+	public Vector<ProfiledGeneric> setThreadBinary(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphBinaries = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundBinaries = new Hashtable<String,String>();
+		
+		// disable all binaries for the given graph
+		for (int i = 0; i < this.profiledBinaries.size(); i++) {
+			ProfiledGeneric pBinary = this.profiledBinaries.elementAt(i);
+			pBinary.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find binaries below the threshold
+		boolean lowBinary;
+		ProfiledThreshold thresholdBinary = this.graphs[graphIndex].getThresholdBinary();
+		thresholdBinary.init(graphIndex);
+
+		// for each binary in the selected sample range, if its thread is enabled, add the binary
+		int binaryThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountBinary"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledThread pThread = (ProfiledThread)
+							this.profiledThreads.elementAt(sample.threadIndex);
+
+			if (pThread.isEnabled(graphIndex)) {
+				ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+				String binaryName = pBinary.getNameString();
+
+				lowBinary = pBinary.getTotalSampleCount() < binaryThreshold;
+
+				if (!foundBinaries.containsKey(binaryName)) {
+					pBinary.setEnabled(graphIndex, true);
+					foundBinaries.put(binaryName, binaryName);
+					if (lowBinary) {
+						thresholdBinary.addItem(graphIndex, pBinary, 1);
+					} else {
+						pBinary.setSampleCount(graphIndex, 1);
+						graphBinaries.add(pBinary);
+					}
+				} else {
+					if (lowBinary)
+						thresholdBinary.incSampleCount(graphIndex);
+					else
+						pBinary.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphBinaries.size(); i++) {
+			ProfiledBinary pBinary = (ProfiledBinary) graphBinaries.elementAt(i);
+			pBinary.setLoadAndString(graphIndex, (float)(pBinary.getSampleCount(graphIndex) * percentPerSample));
+		}
+
+		return graphBinaries;
+	}
+
+	/*
+	 * Based on a graph's set of enabled threads and binaries, produce a set of functions, and
+	 * disable all other functions for that graph
+	 */
+	public Vector<ProfiledGeneric> setThreadBinaryFunction(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphFunctions = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundFunctions = new Hashtable<String,String>();
+		
+		// disable all functions for the given graph
+		for (int i = 0; i < this.profiledFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(i);
+			pFunction.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find functions below the threshold
+		boolean lowFunction;
+		ProfiledThreshold thresholdFunction = this.graphs[graphIndex].getThresholdFunction();
+		thresholdFunction.init(graphIndex);
+
+		// for each function in the selected sample range, if its thread and binary are enabled,
+		// add the function
+		int functionThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountFunction"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+
+			if (pThread.isEnabled(graphIndex) && pBinary.isEnabled(graphIndex)) {
+				ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+				String functionName = pFunction.getNameString();
+
+				lowFunction = pFunction.getTotalSampleCount() < functionThreshold;
+
+				if (!foundFunctions.containsKey(functionName)) {
+					pFunction.setEnabled(graphIndex, true);
+					foundFunctions.put(functionName, functionName);
+					if (lowFunction) {
+						thresholdFunction.addItem(graphIndex, pFunction, 1);
+					} else {
+						pFunction.setSampleCount(graphIndex, 1);
+						graphFunctions.add(pFunction);
+					}
+				} else {
+					if (lowFunction)
+						thresholdFunction.incSampleCount(graphIndex);
+					else
+						pFunction.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) graphFunctions.elementAt(i);
+			pFunction.setLoadAndString(graphIndex, (float)(pFunction.getSampleCount(graphIndex)* percentPerSample));
+		}		
+
+		return graphFunctions;
+	}
+
+	public Vector<ProfiledGeneric> setBinaryThreadFunction(int graphIndex)
+	{
+		return setThreadBinaryFunction(graphIndex);
+	}
+	
+	/*
+	 * Based on a graph's set of enabled threads, produce a set of functions, and
+	 * disable all other functions for that graph
+	 */
+	public Vector<ProfiledGeneric> setThreadFunction(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphFunctions = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundFunctions = new Hashtable<String,String>();
+		
+		// disable all functions for the given graph
+		for (int i = 0; i < this.profiledFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(i);
+			pFunction.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find functions below the threshold
+		boolean lowFunction;
+		ProfiledThreshold thresholdFunction = this.graphs[graphIndex].getThresholdFunction();
+		thresholdFunction.init(graphIndex);
+
+		// for each function in the selected sample range, if its thread is enabled, add the function
+		int functionThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountFunction"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+
+			if (pThread.isEnabled(graphIndex)) {
+				ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+				String functionName = pFunction.getNameString();
+
+				lowFunction = pFunction.getTotalSampleCount() < functionThreshold;
+
+				if (!foundFunctions.containsKey(functionName)) {
+					pFunction.setEnabled(graphIndex, true);
+					foundFunctions.put(functionName, functionName);
+					if (lowFunction) {
+						thresholdFunction.addItem(graphIndex, pFunction, 1);
+					} else {
+						pFunction.setSampleCount(graphIndex, 1);
+						graphFunctions.add(pFunction);
+					}
+				} else {
+					if (lowFunction)
+						thresholdFunction.incSampleCount(graphIndex);
+					else
+						pFunction.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) graphFunctions.elementAt(i);
+			pFunction.setLoadAndString(graphIndex, (float)(pFunction.getSampleCount(graphIndex) * percentPerSample));
+		}		
+		
+		return graphFunctions;
+	}
+
+	/*
+	 * Based on a graph's set of enabled threads and functions, produce a set of binaries, and
+	 * disable all other binaries for that graph
+	 */
+	public Vector<ProfiledGeneric> setThreadFunctionBinary(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphBinaries = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundBinaries = new Hashtable<String,String>();
+		
+		// disable all binaries for the given graph
+		for (int i = 0; i < this.profiledBinaries.size(); i++) {
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(i);
+			pBinary.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find binaries below the threshold
+		boolean lowBinary;
+		ProfiledThreshold thresholdBinary = this.graphs[graphIndex].getThresholdBinary();
+		thresholdBinary.init(graphIndex);
+
+		// for each binary in the selected sample range, if its thread and function are enabled,
+		// add the binary
+		int binaryThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountBinary"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+
+			if (pThread.isEnabled(graphIndex) && pFunction.isEnabled(graphIndex)) {
+				ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+				String binaryName = pBinary.getNameString();
+
+				lowBinary = pBinary.getTotalSampleCount() < binaryThreshold;
+
+				if (!foundBinaries.containsKey(binaryName)) {
+					pBinary.setEnabled(graphIndex, true);
+					foundBinaries.put(binaryName, binaryName);
+					if (lowBinary) {
+						thresholdBinary.addItem(graphIndex, pBinary, 1);
+					} else {
+						pBinary.setSampleCount(graphIndex, 1);
+						graphBinaries.add(pBinary);
+					}
+				} else {
+					if (lowBinary)
+						thresholdBinary.incSampleCount(graphIndex);
+					else
+						pBinary.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphBinaries.size(); i++) {
+			ProfiledBinary pBinary = (ProfiledBinary) graphBinaries.elementAt(i);
+			pBinary.setLoadAndString(graphIndex, (float)(pBinary.getSampleCount(graphIndex) * percentPerSample));
+		}
+		
+		return graphBinaries;
+	}
+
+	public Vector<ProfiledGeneric> setFunctionThreadBinary(int graphIndex)
+	{
+		return setThreadFunctionBinary(graphIndex);
+	}
+
+	/*
+	 * Based on a graph's set of enabled binaries, produce a set of threads, and
+	 * disable all other threads for that graph
+	 */
+	public Vector<ProfiledGeneric> setBinaryThread(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphThreads = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundThreads = new Hashtable<String,String>();
+		
+		// disable all threads for the given graph
+		for (int i = 0; i < this.profiledThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(i);
+			pThread.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find threads below the threshold
+		boolean lowThread;
+		ProfiledThreshold thresholdThread = this.graphs[graphIndex].getThresholdThread();
+		thresholdThread.init(graphIndex);
+
+		// for each thread in the selected sample range, if its binary is enabled, add the thread
+		int threadThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountThread"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+
+			if (pBinary.isEnabled(graphIndex)) {
+				ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+				String threadName = pThread.getNameString();
+
+				lowThread = pThread.getTotalSampleCount() < threadThreshold;
+
+				if (!foundThreads.containsKey(threadName)) {
+					pThread.setEnabled(graphIndex, true);
+					foundThreads.put(threadName, threadName);
+					if (lowThread) {
+						thresholdThread.addItem(graphIndex, pThread, 1);
+					} else {
+						pThread.setSampleCount(graphIndex, 1);
+						graphThreads.add(pThread);
+					}
+				} else {
+					if (lowThread)
+						thresholdThread.incSampleCount(graphIndex);
+					else
+						pThread.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) graphThreads.elementAt(i);
+			pThread.setLoadAndString(graphIndex, (float)(pThread.getSampleCount(graphIndex) * percentPerSample));
+		}
+		
+		return graphThreads;
+	}
+
+	/*
+	 * Based on a graph's set of enabled functions, produce a set of threads, and
+	 * disable all other threads for that graph
+	 */
+	public Vector<ProfiledGeneric> setFunctionThread(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphThreads = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundThreads = new Hashtable<String,String>();
+		
+		// disable all threads for the given graph
+		for (int i = 0; i < this.profiledThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(i);
+			pThread.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find threads below the threshold
+		boolean lowThread;
+		ProfiledThreshold thresholdThread = this.graphs[graphIndex].getThresholdThread();
+		thresholdThread.init(graphIndex);
+
+		// for each thread in the selected sample range, if its function is enabled, add the thread
+		int threadThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountThread"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+
+			if (pFunction.isEnabled(graphIndex)) {
+				ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+				String threadName = pThread.getNameString();
+
+				lowThread = pThread.getTotalSampleCount() < threadThreshold;
+
+				if (!foundThreads.containsKey(threadName)) {
+					pThread.setEnabled(graphIndex, true);
+					foundThreads.put(threadName, threadName);
+					if (lowThread) {
+						thresholdThread.addItem(graphIndex, pThread, 1);
+					} else {
+						pThread.setSampleCount(graphIndex, 1);
+						graphThreads.add(pThread);
+					}
+				} else {
+					if (lowThread)
+						thresholdThread.incSampleCount(graphIndex);
+					else
+						pThread.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) graphThreads.elementAt(i);
+			pThread.setLoadAndString(graphIndex, (float)(pThread.getSampleCount(graphIndex) * percentPerSample));
+		}
+		
+		return graphThreads;
+	}
+
+	/*
+	 * Based on a graph's set of enabled binaries and functions, produce a set of threads, and
+	 * disable all other threads for that graph
+	 */
+	public Vector<ProfiledGeneric> setBinaryFunctionThread(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphThreads = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundThreads = new Hashtable<String,String>();
+		
+		// disable all threads for the given graph
+		for (int i = 0; i < this.profiledThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(i);
+			pThread.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find threads below the threshold
+		boolean lowThread;
+		ProfiledThreshold thresholdThread = this.graphs[graphIndex].getThresholdThread();
+		thresholdThread.init(graphIndex);
+
+		// for each thread in the selected sample range, if its binary and function are enabled,
+		// add the thread
+		int threadThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountThread"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+
+			if (pBinary.isEnabled(graphIndex)
+	 		    && (pFunction.isEnabled(graphIndex))) {
+				ProfiledThread pThread = (ProfiledThread) this.profiledThreads.elementAt(sample.threadIndex);
+				String threadName = pThread.getNameString();
+
+				lowThread = pThread.getTotalSampleCount() < threadThreshold;
+
+				if (!foundThreads.containsKey(threadName)) {
+					pThread.setEnabled(graphIndex, true);
+					foundThreads.put(threadName, threadName);
+					if (lowThread) {
+						thresholdThread.addItem(graphIndex, pThread, 1);
+					} else {
+						pThread.setSampleCount(graphIndex, 1);
+						graphThreads.add(pThread);
+					}
+				} else {
+					if (lowThread)
+						thresholdThread.incSampleCount(graphIndex);
+					else
+						pThread.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphThreads.size(); i++) {
+			ProfiledThread pThread = (ProfiledThread) graphThreads.elementAt(i);
+			pThread.setLoadAndString(graphIndex, (float)(pThread.getSampleCount(graphIndex) * percentPerSample));
+		}
+		
+		return graphThreads;
+	}
+
+	public Vector<ProfiledGeneric> setFunctionBinaryThread(int graphIndex)
+	{
+		return setBinaryFunctionThread(graphIndex);
+	}
+
+	/*
+	 * Based on a graph's set of enabled functions, produce a set of binaries, and
+	 * disable all other binaries for that graph
+	 */
+	public Vector<ProfiledGeneric> setFunctionBinary(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphBinaries = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundBinaries = new Hashtable<String,String>();
+		
+		// disable all binaries for the given graph
+		for (int i = 0; i < this.profiledBinaries.size(); i++) {
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(i);
+			pBinary.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find binaries below the threshold
+		boolean lowBinary;
+		ProfiledThreshold thresholdBinary = this.graphs[graphIndex].getThresholdBinary();
+		thresholdBinary.init(graphIndex);
+
+		// for each binary in the selected sample range, if its function is enabled, add the binary
+		int binaryThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountBinary"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+
+			if (pFunction.isEnabled(graphIndex)) {
+				ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+				String binaryName = pBinary.getNameString();
+
+				lowBinary = pBinary.getTotalSampleCount() < binaryThreshold;
+
+				if (!foundBinaries.containsKey(binaryName)) {
+					pBinary.setEnabled(graphIndex, true);
+					foundBinaries.put(binaryName, binaryName);
+					if (lowBinary) {
+						thresholdBinary.addItem(graphIndex, pBinary, 1);
+					} else {
+						pBinary.setSampleCount(graphIndex, 1);
+						graphBinaries.add(pBinary);
+					}
+				} else {
+					if (lowBinary)
+						thresholdBinary.incSampleCount(graphIndex);
+					else
+						pBinary.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphBinaries.size(); i++) {
+			ProfiledBinary pBinary = (ProfiledBinary) graphBinaries.elementAt(i);
+			pBinary.setLoadAndString(graphIndex, (float)(pBinary.getSampleCount(graphIndex) * percentPerSample));
+		}
+		
+		return graphBinaries;
+	}
+
+	/*
+	 * Based on a graph's set of enabled binaries, produce a set of functions, and
+	 * disable all other functions for that graph
+	 */
+	public Vector<ProfiledGeneric> setBinaryFunction(int graphIndex)
+	{
+		Vector<ProfiledGeneric>  graphFunctions = new Vector<ProfiledGeneric>();
+		Hashtable<String,String> foundFunctions = new Hashtable<String,String>();
+		
+		// disable all functions for the given graph
+		for (int i = 0; i < this.profiledFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(i);
+			pFunction.setEnabled(graphIndex, false);
+		}
+
+		// set up in case we find functions below the threshold
+		boolean lowFunction;
+		ProfiledThreshold thresholdFunction = this.graphs[graphIndex].getThresholdFunction();
+		thresholdFunction.init(graphIndex);
+
+		// for each function in the selected sample range, if its binary is enabled, add the function
+		int functionThreshold = (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountFunction"); //$NON-NLS-1$
+		for (int i = this.startSampleIndex; i < this.endSampleIndex; i++) {
+			GppSample sample = this.sortedSamples[i];
+			ProfiledBinary pBinary = (ProfiledBinary) this.profiledBinaries.elementAt(sample.binaryIndex);
+
+			if (pBinary.isEnabled(graphIndex)) {
+				ProfiledFunction pFunction = (ProfiledFunction) this.profiledFunctions.elementAt(sample.functionIndex);
+				String functionName = pFunction.getNameString();
+
+				lowFunction = pFunction.getTotalSampleCount() < functionThreshold;
+
+				if (!foundFunctions.containsKey(functionName)) {
+					pFunction.setEnabled(graphIndex, true);
+					foundFunctions.put(functionName, functionName);
+					if (lowFunction) {
+						thresholdFunction.addItem(graphIndex, pFunction, 1);
+					} else {
+						pFunction.setSampleCount(graphIndex, 1);
+						graphFunctions.add(pFunction);
+					}
+				} else {
+					if (lowFunction)
+						thresholdFunction.incSampleCount(graphIndex);
+					else
+						pFunction.incSampleCount(graphIndex);
+				}
+			}
+		}
+		
+		// since we are not converting float % load to string % load inside the table viewers, do it here
+		double percentPerSample;
+		if (startSampleIndex == endSampleIndex)
+			percentPerSample = 0.0;
+		else
+			percentPerSample = 100.0 / ((double)(this.endSampleIndex - this.startSampleIndex));
+
+		for (int i = 0; i < graphFunctions.size(); i++) {
+			ProfiledFunction pFunction = (ProfiledFunction) graphFunctions.elementAt(i);
+			pFunction.setLoadAndString(graphIndex, (float)(pFunction.getSampleCount(graphIndex) * percentPerSample));
+		}		
+		
+		return graphFunctions;
+	}
+	
+	public int getStartSampleIndex()
+	{
+		return this.startSampleIndex;
+	}
+	
+	public int getEndSampleIndex()
+	{
+		return this.endSampleIndex;
+	}
+	
+	public int[] getThreadSampleCounts()
+	{
+		return this.threadSamples;
+	}
+	
+	public int[] getBinarySampleCounts()
+	{
+		return this.binarySamples;
+	}
+	
+	public int[] getFunctionSampleCounts()
+	{
+		return this.functionSamples;
+	}
+
+	public void setThreadSampleCounts(int[] sampleCounts)
+	{
+		this.threadSamples = sampleCounts;
+	}
+	
+	public void setBinarySampleCounts(int[] sampleCounts)
+	{
+		this.binarySamples = sampleCounts;
+	}
+	
+	public void setFunctionSampleCounts(int[] sampleCounts)
+	{
+		this.functionSamples = sampleCounts;
+	}
+	
+	public void setThreadColorPalette(ThreadColorPalette tableColorPalette) {
+		this.threadColorPalette = tableColorPalette;
+	}
+
+	public ThreadColorPalette getThreadColorPalette() {
+		if (this.threadColorPalette == null) {
+			this.threadColorPalette = new ThreadColorPalette();
+		}
+		return this.threadColorPalette;
+	}
+
+	public void setBinaryColorPalette(BinaryColorPalette tableColorPalette) {
+		this.binaryColorPalette = tableColorPalette;
+	}
+
+	public BinaryColorPalette getBinaryColorPalette() {
+		if (this.binaryColorPalette == null) {
+			this.binaryColorPalette = new BinaryColorPalette();
+		}
+		return this.binaryColorPalette;
+	}
+
+	public void setFunctionColorPalette(FunctionColorPalette tableColorPalette) {
+		this.functionColorPalette = tableColorPalette;
+	}
+
+	public FunctionColorPalette getFunctionColorPalette() {
+		if (this.functionColorPalette == null) {
+			this.functionColorPalette = new FunctionColorPalette();
+		}
+		return this.functionColorPalette;
+	}
+
+	@Override
+	public void finalizeTrace() {
+		samples.trimToSize();
+		for (int i = 0; i < samples.size(); i++) {
+			GppSample sample = (GppSample) samples.get(i);
+			
+			if (sample.currentFunctionItt == null && sample.currentFunctionSym == null) {
+				sample.currentFunctionItt = new Function(Messages.getString("GppTrace.functionNotFound1") + Long.toHexString(sample.programCounter) + Messages.getString("GppTrace.functionNotFound2"), //$NON-NLS-1$ //$NON-NLS-2$
+						new Long(sample.programCounter),
+						Messages.getString("GppTrace.binaryNotFound1") +  Long.toHexString(sample.programCounter) + Messages.getString("GppTrace.binarynotFound2")); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+}