sysperfana/analyzetoolext/com.nokia.s60tools.analyzetool/src/com/nokia/s60tools/analyzetool/engine/CallstackDataParser.java
changeset 6 f65f740e69f9
child 15 0367d2db2c06
equal deleted inserted replaced
5:844b047e260d 6:f65f740e69f9
       
     1 /*
       
     2  * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3  * All rights reserved.
       
     4  * This component and the accompanying materials are made available
       
     5  * under the terms of "Eclipse Public License v1.0"
       
     6  * which accompanies this distribution, and is available
       
     7  * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8  *
       
     9  * Initial Contributors:
       
    10  * Nokia Corporation - initial contribution.
       
    11  *
       
    12  * Contributors:
       
    13  *
       
    14  * Description:  Definitions for the class ParseAnalyzeData
       
    15  *
       
    16  */
       
    17 package com.nokia.s60tools.analyzetool.engine;
       
    18 
       
    19 import java.util.ArrayList;
       
    20 import java.util.HashMap;
       
    21 import java.util.List;
       
    22 
       
    23 import com.nokia.s60tools.analyzetool.engine.statistic.AllocCallstack;
       
    24 import com.nokia.s60tools.analyzetool.engine.statistic.AllocInfo;
       
    25 import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo;
       
    26 import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad;
       
    27 import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo;
       
    28 import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo;
       
    29 import com.nokia.s60tools.analyzetool.global.Constants;
       
    30 import com.nokia.s60tools.analyzetool.global.Constants.Operation;
       
    31 
       
    32 /**
       
    33  * This class parses line of .dat file format and extracts
       
    34  * callstack data. For now this is a separate class but could
       
    35  * eventually be moved into ParseAnalyzeData after some refactoring. 
       
    36  *
       
    37  */
       
    38 public class CallstackDataParser {
       
    39 	/** true if callstack reading for this BaseInfo is now complete*/
       
    40 	boolean complete = false;
       
    41 	/** resulting callstack */
       
    42 	private List<AllocCallstack> callstack;
       
    43 	/** BaseInfo for the current alloc or free */
       
    44 	private BaseInfo baseInfo;
       
    45 	/** ProcessInfo for the current alloc or free */
       
    46 	private ProcessInfo process;
       
    47 	/** number of remaining callstack elements expected to be processed with ALLOCF or FREEF */
       
    48 	private int remainingSize;
       
    49 	
       
    50 	/** Cache for callstack items. Used when allocation fragment is parsed in the wrong order. */
       
    51 	private HashMap<Integer, List<AllocCallstack>> callstackCache = null;
       
    52 	
       
    53 	/**
       
    54 	 * Constructor
       
    55 	 * @param baseInfo BaseInfo for the current alloc or free
       
    56 	 * @param p ProcessInfo for the current alloc or free
       
    57 	 */
       
    58 	public CallstackDataParser(BaseInfo baseInfo, ProcessInfo p) {
       
    59 		if (p == null || baseInfo == null || p.getProcessID() != baseInfo.getProcessID()){
       
    60 			throw new IllegalArgumentException("BaseInfo and ProcessInfo are mandatory and must not be null; and the process id of both must match."); //$NON-NLS-1$
       
    61 		}
       
    62 		this.baseInfo = baseInfo;
       
    63 		this.process = p;
       
    64 		callstack = new ArrayList<AllocCallstack>();
       
    65 	}
       
    66 
       
    67 	/**
       
    68 	 * Parses one line of PCSS statement
       
    69 	 * @param aLine
       
    70 	 * @return true, if this callstack is now complete (all parts are available)
       
    71 	 */
       
    72 	public final boolean parseLine(final String aLine) {
       
    73 			
       
    74 		if (aLine.indexOf(Constants.PREFIX) == -1){
       
    75 			//not a PCSS statement
       
    76 			return false;
       
    77 		}
       
    78 		
       
    79 		String[] lineFragments = getLineFragments(aLine);
       
    80 		if (lineFragments.length < 5){
       
    81 			//not a valid PCSS statment for callstack processing
       
    82 			return false;
       
    83 		}
       
    84 		int processID = Integer.parseInt(lineFragments[1], 16);
       
    85 		if (baseInfo.getProcessID() != processID || process.getProcessID() != processID){
       
    86 			//statement is not for current process id
       
    87 			return false;
       
    88 		}
       
    89 		
       
    90 		//the operation must match
       
    91 		Constants.Operation op = Constants.Operation.toOperation(lineFragments[2]);
       
    92 		if (!verifyOperation(op, baseInfo)){
       
    93 			return false;
       
    94 		}
       
    95 		
       
    96 		//the memory address must match
       
    97 		long memoryAddress = Long.parseLong(lineFragments[3],16);
       
    98 		if (baseInfo.getMemoryAddress() != memoryAddress){
       
    99 			return false;
       
   100 		}
       
   101 		
       
   102 		
       
   103 		boolean ret = false;
       
   104 		
       
   105 		switch (op) {
       
   106 		case ALLOC:
       
   107 			ret = parseAlloc(lineFragments);
       
   108 			break;
       
   109 		case ALLOCH:
       
   110 			ret = parseHeader(lineFragments, 6);
       
   111 			break;
       
   112 		case FREEH:
       
   113 			int traceFileVersion = process.getTraceDataVersion();
       
   114 			ret = parseHeader(lineFragments, traceFileVersion > 1 ? 5 : 4);
       
   115 			break;
       
   116 		case ALLOCF:
       
   117 			//fall through
       
   118 		case FREEF:
       
   119 			ret = parseFragment(lineFragments);				
       
   120 			break;
       
   121 		default:
       
   122 			// ignore this line
       
   123 			break;
       
   124 		}
       
   125 		
       
   126 		return ret;
       
   127 	}
       
   128 	
       
   129 	private static boolean verifyOperation(Operation op, BaseInfo aBaseInfo) {
       
   130 		return ((aBaseInfo instanceof AllocInfo && (op == Constants.Operation.ALLOC
       
   131 				|| op == Constants.Operation.ALLOCH || op == Constants.Operation.ALLOCF)) 
       
   132 				|| (aBaseInfo instanceof FreeInfo && (op == Constants.Operation.FREEH 
       
   133 				|| op == Constants.Operation.FREEF)));
       
   134 	}
       
   135 
       
   136 	/**
       
   137 	 * Returns the line fragments of the PCSS statement
       
   138 	 * @param aLine the PCSS statement
       
   139 	 * @return the line fragments separated by space
       
   140 	 */
       
   141 	private String[] getLineFragments(final String aLine){
       
   142 		int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded
       
   143 		
       
   144 		if (index == -1){
       
   145 			return new String[0];
       
   146 		}
       
   147 		
       
   148 		String usedString = (index == 0) ? aLine : aLine.substring(index, aLine.length());
       
   149 		return usedString.split(" "); //$NON-NLS-1$
       
   150 	}
       
   151 	
       
   152 	/**
       
   153 	 * Parses an ALLOC statement. This statement contains a complete callstack.
       
   154 	 * @param fragments the line fragments of the statement to process
       
   155 	 * @return true if statement is complete (this method will always return true)
       
   156 	 */
       
   157 	private boolean parseAlloc(String[] fragments) {
       
   158 		
       
   159 		if (fragments.length > 5) {
       
   160 			createCallstack(fragments, process, callstack, 6);
       
   161 		}
       
   162 		
       
   163 		complete = true;
       
   164 		return true; //we are done; there are no fragments for this alloc
       
   165 	}
       
   166 	
       
   167 	/**
       
   168 	 * Parses an ALLOCH or FREEH statement
       
   169 	 * @param fragments the line fragments of the statement to process
       
   170 	 * @param startIndex index at which the callstack size is to be found
       
   171 	 * @return true if callstack is complete, false if callstack fragment is expected
       
   172 	 */
       
   173 	private boolean parseHeader(String[] fragments, int startIndex) {
       
   174 		
       
   175 		if (callstack.size() > 0 || remainingSize > 0) {
       
   176 			throw new IllegalStateException(
       
   177 					"Callstack list should still be empty when starting to process ALLOCH"); //$NON-NLS-1$
       
   178 		}
       
   179 
       
   180 		if (fragments.length > startIndex) {
       
   181 
       
   182 			int callstackSize = Integer.parseInt(fragments[startIndex], 16);
       
   183 			startIndex ++;
       
   184 
       
   185 			createCallstack(fragments, process, callstack, startIndex);
       
   186 
       
   187 			callstackSize -= callstack.size();
       
   188 			if (callstackSize > 0) {
       
   189 				// expect fragments
       
   190 				remainingSize = callstackSize;
       
   191 			} else {
       
   192 				complete = true;
       
   193 			}
       
   194 		} else {
       
   195 			complete = true; // this header doesn't have callstacks - a bit strange
       
   196 		}
       
   197 		return complete;
       
   198 	}
       
   199 	
       
   200 	/**
       
   201 	 * Parses an ALLOCF or FREEF callstack fragment
       
   202 	 * @param fragments the line fragments of the statement to process
       
   203 	 * @return true if callstack is now complete; false otherwise
       
   204 	 */
       
   205 	private boolean parseFragment(String[] fragments) {
       
   206 		long time = Long.parseLong(fragments[4],16);
       
   207 		int packetNumber = Integer.parseInt(fragments[5], 16);
       
   208 	
       
   209 		if (baseInfo.getTime() == time) {
       
   210 			List<AllocCallstack> tmpCallstack = new ArrayList<AllocCallstack>();
       
   211 			createCallstack(fragments, process, tmpCallstack, 6);
       
   212 			updateFragment(tmpCallstack, packetNumber);
       
   213 			
       
   214 			remainingSize -= tmpCallstack.size();
       
   215 			if (remainingSize <= 0){
       
   216 				complete = true;
       
   217 				finaliseCallstack();
       
   218 			}
       
   219 		}
       
   220 		return complete;
       
   221 	}
       
   222 	
       
   223 	private DllLoad getDllForAddress(ProcessInfo p, Long memoryAddress,
       
   224 			long time) {
       
   225 		for (DllLoad oneLoad : p.getDllLoads()) {
       
   226 			if (memoryAddress >= oneLoad.getStartAddress()
       
   227 					&& memoryAddress <= oneLoad.getEndAddress()
       
   228 					&& time >= oneLoad.getLoadTime()
       
   229 					&& time <= oneLoad.getUnloadTime()) {
       
   230 				// dll load found
       
   231 				return oneLoad;
       
   232 			}
       
   233 		}
       
   234 		return null;
       
   235 	}
       
   236 	private void createCallstack(String[] fragments, ProcessInfo p, List<AllocCallstack> callstack, int startIndex) {
       
   237 		for (int i = startIndex; i < fragments.length; i++) {
       
   238 			AllocCallstack callstackElem = new AllocCallstack(fragments[i]);
       
   239 
       
   240 			// find matching dll
       
   241 			DllLoad dllLoad = getDllForAddress(process, callstackElem.getMemoryAddress(), baseInfo.getTime());
       
   242 			if (dllLoad != null) {
       
   243 				callstackElem.setDllLoad(dllLoad);
       
   244 			}
       
   245 			callstack.add(callstackElem);
       
   246 		}
       
   247 	}
       
   248 	/**
       
   249 	 * Updates allocation fragment. Means that given callstack is addition to
       
   250 	 * previous added alloc
       
   251 	 *
       
   252 	 * @param callstack
       
   253 	 *            Addition tmpcallstack items
       
   254 	 * @param packetNumber
       
   255 	 *            ordinal of callstack fragment
       
   256 	 */
       
   257 	private void updateFragment(List<AllocCallstack> tmpcallstack,
       
   258 			int packetNumber) {
       
   259 		if (packetNumber == 1){
       
   260 			//special case; this can be added to the end of the list straight away
       
   261 			callstack.addAll(tmpcallstack);
       
   262 		} else {
       
   263 			//packages may come out of order; this is managed in the callstackCache
       
   264 			if (callstackCache == null){
       
   265 				callstackCache = new HashMap<Integer, List<AllocCallstack>>();			
       
   266 			}
       
   267 			callstackCache.put(packetNumber, tmpcallstack);			
       
   268 		}
       
   269 	}
       
   270 	/**
       
   271 	 * Optimises internal callstack data structures.
       
   272 	 * Should only be called after all data for this memory operation
       
   273 	 * has been loaded (i.e. all fragments)
       
   274 	 */
       
   275 	public void finaliseCallstack(){
       
   276 		if (callstackCache == null){
       
   277 			//nothing to do
       
   278 			return;
       
   279 		}
       
   280 		
       
   281 		if (!complete){
       
   282 			throw new IllegalStateException("callstack processing is not yet complete."); //$NON-NLS-1$
       
   283 		}
       
   284 			
       
   285 		if (callstack == null && callstackCache != null){
       
   286 			throw new IllegalStateException(); //first set of callstacks should always be in callstacks
       
   287 		}
       
   288 		
       
   289 		if (callstackCache != null){
       
   290 			int size = callstackCache.size();
       
   291 			int i = 2;
       
   292 			while(size != 0){
       
   293 				List<AllocCallstack> nextCallStacks = callstackCache.get(i);
       
   294 				if (nextCallStacks != null){
       
   295 					size --;
       
   296 					callstack.addAll(nextCallStacks);
       
   297 				} //TODO else: missing callstack: shall we report it or log it?
       
   298 				i++;
       
   299 			}
       
   300 			callstackCache = null;
       
   301 		}
       
   302 	}
       
   303 	
       
   304 	/**
       
   305 	 * @return the completed callstack.
       
   306 	 */
       
   307 	public List<AllocCallstack> getCallstack(){
       
   308 		if (!complete){
       
   309 			throw new IllegalStateException("Callstack has not been completely processed."); //$NON-NLS-1$
       
   310 		}
       
   311 		
       
   312 		finaliseCallstack();
       
   313 		return callstack;
       
   314 	}
       
   315 	
       
   316 	/**
       
   317 	 * Forces the callstack state to be set to complete. This should only be used when the end of file is encountered.
       
   318 	 */
       
   319 	public void forceComplete(){
       
   320 		complete = true;
       
   321 		finaliseCallstack();
       
   322 	}
       
   323 	
       
   324 }