sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.address/src/com/nokia/carbide/cpp/pi/address/GppTrace.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:32:31 +0200
changeset 2 b9ab3b238396
child 5 844b047e260d
permissions -rw-r--r--
Initial version of Performance Investigator under EPL

/*
 * 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$
			}
		}
	}
}