sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.perfcounters/src/com/nokia/carbide/cpp/pi/internal/perfcounters/PecTraceParser.java
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 24 Aug 2010 14:01:48 +0300
changeset 16 72f198be1c1d
parent 12 ae255c9aa552
permissions -rw-r--r--
Crash Analyser Carbide Extension 1.4.0

/*
 * Copyright (c) 2010 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.internal.perfcounters;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;

import com.nokia.carbide.cpp.internal.pi.model.ParsedTraceData;
import com.nokia.carbide.cpp.internal.pi.model.Parser;
import com.nokia.carbide.cpp.pi.internal.perfcounters.ui.ProcessorSpeedInputDialog;


/**
 * Parser for performance counter event traces in binary format.
 * Converts parsed content into PecSamples.
 */
public class PecTraceParser extends Parser {
	private static final int INSTRUCTIONS_EXECUTED = 0x7;
	private static final int CPU_CLOCK_TICK_DIV64 = 0xFFFF;
	private static final int DUMMY_MIPS_ID = -1;
	private static final int DUMMY_CPU_CLOCK_ID = -2;
	private static final String DATA_FORMAT_VERSION_OLD = "Bappea_V1.24_PEC";
	
	
	private boolean debug = false;
	
	private long time = 0;
	
	/** true if dialogs etc is allowed here, false for quiet mode */
	private boolean allowUserInteraction;
	
	/** produce MIPS graph data */
	protected boolean mipsEnabled;
	/** the processor speed to use for MIPS graph calculations. Updated from data samples if data is available */
	protected int processorSpeed;
	
	/**
	 * Constructor
	 * @param allowUserInteraction true, if user interactions such as dialogs are allowed
	 */
	public PecTraceParser(boolean allowUserInteraction) {
		super();
		this.allowUserInteraction = allowUserInteraction;
	}

	private static final Map<Integer, String> EVENT_TYPE_TABLE = new HashMap<Integer, String>();
	static {
		EVENT_TYPE_TABLE.put(0x0,Messages.PecTraceParser_0);
		EVENT_TYPE_TABLE.put(0x1,Messages.PecTraceParser_1);
		EVENT_TYPE_TABLE.put(0x2,Messages.PecTraceParser_2);
		EVENT_TYPE_TABLE.put(0x3,Messages.PecTraceParser_3);
		EVENT_TYPE_TABLE.put(0x4, Messages.PecTraceParser_4);
		EVENT_TYPE_TABLE.put(0x5, Messages.PecTraceParser_5);
		EVENT_TYPE_TABLE.put(0x6, Messages.PecTraceParser_6);
		EVENT_TYPE_TABLE.put(INSTRUCTIONS_EXECUTED, Messages.PecTraceParser_7);
		EVENT_TYPE_TABLE.put(0x9, Messages.PecTraceParser_8);
		EVENT_TYPE_TABLE.put(0xA, Messages.PecTraceParser_9);
		EVENT_TYPE_TABLE.put(0xB, Messages.PecTraceParser_10);
		EVENT_TYPE_TABLE.put(0xC, Messages.PecTraceParser_11);
		EVENT_TYPE_TABLE.put(0xD, Messages.PecTraceParser_12);
		EVENT_TYPE_TABLE.put(0xF, Messages.PecTraceParser_13);
		EVENT_TYPE_TABLE.put(0x10, Messages.PecTraceParser_14);
		EVENT_TYPE_TABLE.put(0x11, Messages.PecTraceParser_15);
		EVENT_TYPE_TABLE.put(0x12, Messages.PecTraceParser_16);
		EVENT_TYPE_TABLE.put(0x20, Messages.PecTraceParser_17);
		EVENT_TYPE_TABLE.put(0x21, Messages.PecTraceParser_18);
		EVENT_TYPE_TABLE.put(0x22, Messages.PecTraceParser_19);
		EVENT_TYPE_TABLE.put(0xFF, Messages.PecTraceParser_20);
		EVENT_TYPE_TABLE.put(CPU_CLOCK_TICK_DIV64, Messages.PecTraceParser_21);
		EVENT_TYPE_TABLE.put(DUMMY_MIPS_ID, PecTrace.MIPS_NAME);
		EVENT_TYPE_TABLE.put(DUMMY_CPU_CLOCK_ID, Messages.PecTraceParser_23);
	}
				
	/* (non-Javadoc)
	 * @see com.nokia.carbide.cpp.internal.pi.model.Parser#parse(java.io.File)
	 */
	@Override
	public ParsedTraceData parse(File file) throws FileNotFoundException 
	{
		ParsedTraceData ptd = new ParsedTraceData();
		ptd.functionResolvers = null;
		ptd.staticData = null;
		PecTrace pecTrace = new PecTrace();
		ptd.traceData = pecTrace;
		doParsing(file, pecTrace);
		time = 0;
		
		return ptd;
	}
	
	private void doParsing(File f, PecTrace trace) throws FileNotFoundException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream(f));
		try
		{
			int len = dis.readByte();
			byte[] versionString = new byte[len];
			dis.read(versionString);
			this.traceVersion = new String(versionString);
			if(debug)System.out.println("PEC trace version:"+this.traceVersion); //$NON-NLS-1$
			
			int firstData = dis.readUnsignedByte();
			int secondData = dis.readUnsignedByte();
			int thirdData = CPU_CLOCK_TICK_DIV64;

			if (this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
				if (allowUserInteraction && (firstData == INSTRUCTIONS_EXECUTED || secondData == INSTRUCTIONS_EXECUTED)){
					Display.getDefault().syncExec( new Runnable() {

						public void run () {
							// in future, if we don't want to have a dialog in the core parser class
							// we could call into an interface which ProcessorSpeedInputDialog would have to implement
							ProcessorSpeedInputDialog dialog = new ProcessorSpeedInputDialog(
									PlatformUI.getWorkbench()
											.getActiveWorkbenchWindow().getShell());
							if (dialog.open() == Window.OK){
								processorSpeed = dialog.getIntValue();
								mipsEnabled = true;
							}
						}
					});
				}
			}
			
			// >=Bappea_V1.25_PEC
			else {
				if (firstData == INSTRUCTIONS_EXECUTED || secondData == INSTRUCTIONS_EXECUTED) {
					mipsEnabled = true;
				}

				long cpuClockRate = readCpuClockRate(dis);
				if(cpuClockRate > 0){
					// Hz => MHz
					trace.setCpuClockRate((int) cpuClockRate / 1000000);
				}
			
			}
			
			int graphCount = 0;
			
			if (this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
				graphCount = 4;
			}
			// >=Bappea_V1.25_PEC
			else {
				graphCount = 5;
			}
			
			Integer[] valueTypeVector = new Integer[mipsEnabled ? graphCount : 3];
			
			valueTypeVector[0] = Integer.valueOf(firstData);
			valueTypeVector[1] = Integer.valueOf(secondData);
			// this type is always the cpu clock tick div 64
			valueTypeVector[2] = Integer.valueOf(thirdData);
			
			if (mipsEnabled){
				valueTypeVector[3] = DUMMY_MIPS_ID;
				// >=Bappea_V1.25_PEC
				if (!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD))
				{
					valueTypeVector[4] = DUMMY_CPU_CLOCK_ID;
				}
			}
			
			trace.setValueTypes(this.parseValueTypes(valueTypeVector));
			
			PecSample s = null;			
			while(true)
			{
				s = readSample(dis,s);
				trace.addSample(s);
			}
		}
		catch (IOException ioe)
		{
			//TODO: should we log this or handle it?
			//my guess is this signifies the end of file
		}
	}

	private PecSample readSample(DataInputStream dis,PecSample prevSample) throws IOException
	{
		int headerByte = 0;
		int negBitOffset = 0;
		int sampleBitOffset = 0;
		
		if (this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
			headerByte = (dis.readByte() << 24) >>> 24;
		}
		// >=Bappea_V1.25_PEC
		else {
			int headerByte1 = (dis.readByte() << 24) >>> 24;
			int headerByte2 = (dis.readByte() << 24) >>> 24;
			headerByte = ((headerByte1) | (headerByte2) << 8);
			negBitOffset = 1;
			sampleBitOffset = 2;
		}
		
		int neg0 = 0;
		int neg1 = 0;
		int neg2 = 0;
		int neg3 = 0;
		
		int prev0 = 0;
		int prev1 = 0;
		int prev2 = 0;
		int prev3 = 0;
		
		if(prevSample != null)
		{
			prev0 = prevSample.values[0];
			prev1 = prevSample.values[1];
			prev2 = prevSample.values[2]/64;
			prev3 = processorSpeed;
		}
		if(debug) if(this.time > 7820 && this.time < 7830) System.out.println("header: "+Long.toHexString(headerByte)+" = "+Integer.toBinaryString(headerByte));  //$NON-NLS-1$//$NON-NLS-2$
		
		if( ((headerByte >>> 7 + negBitOffset + sampleBitOffset)&1) != 0)
		{
			neg0 = 1;
		}
		
		if( ((headerByte >>> 6 + negBitOffset + sampleBitOffset)&1) != 0)
		{
			neg1 = 1;
		}
		
		if( ((headerByte >>> 5 + negBitOffset + sampleBitOffset)&1) != 0)
		{
			neg2 = 1;
		}
		
		// >=Bappea_V1.25_PEC
		if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD) && ((headerByte >>> 7)&1) != 0) {
			neg3 = 1;
		}
	
		int len0 = (((headerByte >> 3 + sampleBitOffset) << 30) >>> 30)+1;
		int len1 = (((headerByte >> 1 + sampleBitOffset) << 30) >>> 30)+1;
		int len2 = ((((headerByte >> sampleBitOffset) << 31) >>> 31)+1)*2;
		int len3 = -1;
		// >=Bappea_V1.25_PEC
		if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
			len3 = ((((headerByte) << 30) >>> 30)+1);
		}
		
		if(debug) {
			if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
				if(debug) if(this.time > 7820 && this.time < 7830) System.out.println("T:"+this.time+" len0:"+len0+" len1:"+len1+" len2:"+len2+" len3:"+len3); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
				if(debug) if(this.time > 7900 && this.time < 7900) System.out.println("H: "+Integer.toBinaryString(headerByte)+" N0:"+neg0+" N1:"+neg1+" N2:"+neg2+" N3:"+neg3);   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$

			} else {
				if(debug) if(this.time > 7820 && this.time < 7830) System.out.println("T:"+this.time+" len0:"+len0+" len1:"+len1+" len2:"+len2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
				if(debug) if(this.time > 7900 && this.time < 7900) System.out.println("H: "+Integer.toBinaryString(headerByte)+" N0:"+neg0+" N1:"+neg1+" N2:"+neg2);   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
			}
		}
		
		long val0 = readVal(neg0,len0,dis);
		long val1 = readVal(neg1,len1,dis);
		long val2 = readVal(neg2,len2,dis);
		long val3 = -1;
		
		// >=Bappea_V1.25_PEC
		if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
			if (mipsEnabled) {
				val3 = readVal(neg3,len3,dis);
			}
		}
		
		if(debug) {
			if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
				if(this.time > 7820 && this.time < 7900) System.out.println("READ T:"+this.time+"   V0:"+Long.toHexString(val0)+" V1:"+Long.toHexString(val1)+" V2:"+Long.toHexString(val2)+" V3:"+Long.toHexString(val3));   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
			} else {
				if(this.time > 7820 && this.time < 7900) System.out.println("READ T:"+this.time+"   V0:"+Long.toHexString(val0)+" V1:"+Long.toHexString(val1)+" V2:"+Long.toHexString(val2));   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
			}
		}	
		
		val0 = prev0-val0;
		val1 = prev1-val1;
		val2 = prev2-val2;
		
		// >=Bappea_V1.25_PEC
		if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
			val3 = prev3-val3;
			processorSpeed = (int)val3;
		}
		
		int[] values;
		if (!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD) && mipsEnabled) {
			values = new int[]{(int)val0,(int)val1,((int)val2)*64, (int)((val3 / 1000000) * val1 / (val2*64)), ((int)(val3 / 1000000))};
		} else if (mipsEnabled) {
			values = new int[]{(int)val0,(int)val1,((int)val2)*64, (int)(processorSpeed * val1 / (val2*64))};
		} else {
			values = new int[]{(int)val0,(int)val1,((int)val2)*64};
		}
		
		PecSample ps = new PecSample(values, this.time);
		
		if (debug) {
			if(!this.traceVersion.equals(DATA_FORMAT_VERSION_OLD)) {
				if(this.time > 7820 && this.time < 7900) System.out.println("T:"+this.time+"   V0:"+Long.toHexString(val0)+" V1:"+Long.toHexString(val1)+" V2:"+Long.toHexString(val2)+" V3:"+Long.toHexString(val3));  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
				if(this.time > 7820 && this.time < 7900) System.out.println("T:"+this.time+" "+val0+" "+val1+" "+val2+" "+val3);   //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4//$NON-NLS-5$$
			} else {
				if(this.time > 7820 && this.time < 7900) System.out.println("T:"+this.time+"   V0:"+Long.toHexString(val0)+" V1:"+Long.toHexString(val1)+" V2:"+Long.toHexString(val2));  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
				if(this.time > 7820 && this.time < 7900) System.out.println("T:"+this.time+" "+val0+" "+val1+" "+val2);   //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
			}
		}
		this.time++;
		
		return ps;
	}

	private long readVal(int neg,int len,DataInputStream dis) throws IOException
	{
		byte[] array = new byte[len];
		dis.read(array);
		
		if(debug)
			for(int k=0;k<array.length;k++)
			{
				if(this.time > 7820 && this.time < 7900) System.out.println(" "+Integer.toHexString(array[k])); //$NON-NLS-1$
			}
		
		long total = 0;
		for(int i=0;i<len;i++)
		{
			int value = ((array[i] << 24) >>> 24) << (i*8);			
			total |= value;
		}
		
		if(debug) if(this.time > 7820 && this.time < 7900) System.out.println("\n"+Long.toHexString(total)); //$NON-NLS-1$

		if(debug) if(this.time > 7820 && this.time < 7900) System.out.println("T:"+this.time+" "+Long.toHexString(total));  //$NON-NLS-1$//$NON-NLS-2$
		
		if(neg != 0) total = ~total;
		
		return total;
	}
		
	/**
	 * Converts the given event type identifiers into the appropriate event type strings.
	 * @param valueTypeIntegers list of event type identifiers
	 * @return String[] of event type strings
	 */
	private String[] parseValueTypes(Integer[] valueTypeIntegers){
		String[] s = new String[valueTypeIntegers.length];
		for (int i = 0; i < valueTypeIntegers.length; i++) {
			s[i] = convertValueType(valueTypeIntegers[i]);
		}
		
		return s;
	}
	
	private String convertValueType(int value) {
		String s = EVENT_TYPE_TABLE.get(Integer.valueOf(value));
		if (s == null){
			s = String.format(
					Messages.PecTraceParser_22,
					value);			
		}
		return s;

	}
	private long readCpuClockRate(DataInputStream dis) throws IOException
	{	
		long result = dis.readUnsignedByte();
		result += dis.readUnsignedByte() << 8;
		result += dis.readUnsignedByte() << 16;
		result += dis.readUnsignedByte() << 24;
		return result;
	}
}