sysperfana/analyzetoolext/com.nokia.s60tools.analyzetool/src/com/nokia/s60tools/analyzetool/engine/ParseAnalyzeData.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Wed, 21 Apr 2010 19:42:48 +0300
changeset 6 f65f740e69f9
parent 1 1050670c6980
child 15 0367d2db2c06
permissions -rw-r--r--
AnalyzeTool Carbide Extension v1.9.0

/*
 * Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "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:  Definitions for the class ParseAnalyzeData
 *
 */

package com.nokia.s60tools.analyzetool.engine;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.core.runtime.IStatus;

import com.nokia.s60tools.analyzetool.Activator;
import com.nokia.s60tools.analyzetool.engine.statistic.AllocCallstack;
import com.nokia.s60tools.analyzetool.engine.statistic.AllocInfo;
import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo;
import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad;
import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo;
import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo;
import com.nokia.s60tools.analyzetool.global.Constants;

/**
 * Parses trace messages which comes thru TraceViewer.
 * If one message contains PCSS prefix the message will be saved to the data file. Data file will be saved
 * to the project [bld.inf location]\atool_temp folder.
 *
 * @author kihe
 *
 */
public class ParseAnalyzeData {

	private static final String KEYWORD_ABNORMAL = "ABNORMAL";//$NON-NLS-1$
	/** FileOutputStream . */
	private FileOutputStream fis;

	/** File. */
	private File file;

	/** Contains information which processes are started. */
	private final Hashtable<String, Integer> processStart;

	/** Contains information which processes are ended. */
	private final AbstractList<Integer> processEnd;


	/** Contains information of where to save trace data. */
	private String usedFilePath;

	/** Contains active process information. */
	public Hashtable<Integer, ProcessInfo> processes;

	/**
	 * Contains list of executed process. One process will begin in the
	 * PROCESS_START and ends with the PROCESS_END tag
	 */
	private final AbstractList<ProcessInfo> processList;

	/**
	 * Allocation cache
	 * Used when one alloc info is separated to multiple lines.
	 */
	private Hashtable<Long, AllocInfo> allocCache = null;
	
	/**
	 * For each memory operation, keeps count of how many 
	 * callstacks are outstanding. This is used to clean up 
	 * after all fragments have been processed.
	 * 
	 * Zero callstack counts should not be kept.
	 */
	private Map<BaseInfo, Integer> remainingCallstacksMap = null;


	/**
	 * Deallocation cache.
	 * Used when one free info is separated to multiple lines.
	 */
	private Hashtable<Long, FreeInfo> freeCache = null;
	
	/**
	 * Cache for dll loads.
	 * We must find dll load item for every allocation callstack item.
	 * This is heavy process and usually we must find more than thousand times.
	 * So when using cache for found items => it makes finding process more rapid than without it.
	 */
	private Hashtable<Long, DllLoad> dllLoadCache = null;


	/**
	 * Flag to determinate need to save parsed data to file.
	 * If this flag is set to "true" all the lines which contains PCSS are saved to the file.
	 * Otherwise just parse the file/trace content.
	 */
	boolean saveDataToFile;

	boolean createGraphModel;
	
	/** When set to true, ignore any callstack information. Can be used decrease memory consumption of the model */
	boolean ignoreCallstacks;
	
	/** Callstacks will be read later on demand directly from file */
	private boolean deferCallstacks;
	/** file position showing current write position, this is saved in BaseInfo for deferred callstack reading */
	private long filePos;
	
	long lastTime = 0;
	private int lineBreakSize;
	
	
	/**
	 * Constructor.
	 * 
	 * @param saveData
	 *            if true, save PCSS statements to file. Typical use case is for
	 *            TraceWrapper
	 * @param createModel
	 *            boolean indicating whether to create a graph model. Used to
	 *            improve performance.
	 * @param deferCallstackReading
	 *            true, if callstack reading is to be done later. This requires
	 *            saving the file position during parse phase, so saveData must
	 *            be true or use constructor with FileChannel.
	 */
	public ParseAnalyzeData(boolean saveData, boolean createModel, boolean deferCallstackReading) {
		this(saveData, createModel, deferCallstackReading, 0);
	}	
	
	/**
	 * Constructor. Use this constructor when working with deferred callstacks and not
	 * saving an output file.
	 * 
	 * @param saveData
	 *            if true, save PCSS statements to file. Typical use case is for
	 *            TraceWrapper
	 * @param createModel
	 *            boolean indicating whether to create a graph model. Used to
	 *            improve performance.
	 * @param deferCallstackReading
	 *            true, if callstack reading is to be done later. This requires
	 *            saving the file position during parse phase, so saveData must
	 *            be true or use constructor with FileChannel.
	 * @param lineBreakSize
	 *            Size of line break, usually 1 for device-side file, and 2 for host-side file
	 */
	public ParseAnalyzeData(boolean saveData, boolean createModel, boolean deferCallstackReading, int lineBreakSize) {
		
		if (deferCallstackReading && !saveData && lineBreakSize == 0){
			throw new IllegalArgumentException("linebreak size must be specified when trying to use deferred callstack reading with a .dat input file ");
		}

		processStart = new Hashtable<String, Integer>();
		processEnd = new ArrayList<Integer>();
		processes = new Hashtable<Integer, ProcessInfo>();
		processList = new ArrayList<ProcessInfo>();
		saveDataToFile = saveData;
		deferCallstacks = deferCallstackReading; //orig data input might be streamed through TraceWrapper
		createGraphModel = createModel;
		ignoreCallstacks = !createGraphModel ; 
		if (!ignoreCallstacks && !deferCallstacks){
			allocCache = new Hashtable<Long, AllocInfo>();
			freeCache = new Hashtable<Long, FreeInfo>();
			remainingCallstacksMap = new HashMap<BaseInfo, Integer>();
			dllLoadCache = new Hashtable<Long, DllLoad>();
		}
		filePos = deferCallstacks ? 0 : -1;//set to beginning of file if applicable
		this.lineBreakSize = lineBreakSize;
	}
	
	/**
	 * Add one dllLoad object to process related list
	 *
	 * @param dllLoad
	 *            One DllLoad
	 */
	private void addDllLoad(DllLoad dllLoad) {
		// if one of the started process contains same process id what dll load
		// has add dll load to list
		if (processes.containsKey(dllLoad.getProcessID())) {
			ProcessInfo tempProcessInfo = processes.get(dllLoad.getProcessID());
			tempProcessInfo.addOneDllLoad(dllLoad);
		}
	}

	/**
	 * Add one memory allocation info to process related list
	 *
	 * @param info
	 *            One memory allocation info
	 */
	private void addMemAddress(AllocInfo info) {
		// if one of the started process contains same process id what memory
		// allocation has add memory allocation info to list
		if (processes.containsKey(info.getProcessID())) {
			ProcessInfo tempProcessInfo = processes.get(info.getProcessID());
			tempProcessInfo.addOneAlloc(info);
		}
	}


	/**
	 * Removes memory allocation from process related
	 * memory allocations list.
	 *
	 * @param info Deallocation info
	 */
	private void removeMemAddress(FreeInfo info) {
		if (processes.containsKey(info.getProcessID())) {
			ProcessInfo tempProcessInfo = processes.get(info.getProcessID());
			tempProcessInfo.free(info);
		}

	}
	/**
	 * Closes the input stream if it is open.
	 */
	private final void closeStreams() {
		try {

			// if fis exists => close fis
			if (fis != null) {
				fis.close();

				// clear file and fis
				file = null;
				fis = null;
				filePos = 0;
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}

	/**
	 * Finish the writing.
	 *
	 */
	public final void finish() {

		try {
			// if some data is not write to file yet => do it now
			if (fis != null) {
				fis.flush();
			}
		} catch (IOException ioe) {
			return;
		} finally {

			// close needed streams
			closeStreams();
		}

		// clear stored data
		processList.clear();
		processes.clear();
		processStart.clear();
		processEnd.clear();
		if (allocCache != null){
			allocCache.clear();			
		}
		if (freeCache != null){
			freeCache.clear();
		}
		if (dllLoadCache != null){
			dllLoadCache.clear();			
		}
		if (remainingCallstacksMap != null){
			remainingCallstacksMap.clear();
		}
	}

	/**
	 * Return memory allocation size for all started processes.
	 *
	 * @return Memory allocation size for all started processes
	 */
	public final int getAllocationsSize() {
		int allocCnt = 0;
		// get memory allocations count from ongoing processes
		for (ProcessInfo p : processes.values()) {
			//the current number of memory allocations is traced as potential memory leaks
			allocCnt += p.getMemLeaksNumber();
		}
		
		//get memory allocations count from already ended processes
		Iterator<ProcessInfo> iterPro = processList.iterator();
		while( iterPro.hasNext() ) {
			ProcessInfo oneInfo = iterPro.next();
			allocCnt += oneInfo.getMemLeaksNumber();
		}
		return allocCnt;
	}

	/**
	 * Gets used data file name.
	 *
	 * @return Data file name
	 */
	public final String getDataFileName() {
		return usedFilePath;
	}

	/**
	 * Checks given path if it is null set used path to point java temp
	 * directory.
	 *
	 * @param path
	 *            Given path
	 * @return Data file location
	 */
	private final String getFileName(final String path) {
		if (path == null || ("").equals(path)) {
			// null path given => use java temp dir
			usedFilePath = System.getProperty("java.io.tmpdir")
					+ Constants.FILENAME;
		} else if (!path.contains(Constants.FILENAME)) {
			usedFilePath = path + Constants.FILENAME;
		}
		return usedFilePath;
	}

	/**
	 * Gets started process info.
	 *
	 * @return Hashtable<String, String> Started processes info
	 */
	public final Hashtable<String, Integer> getStartedProcesses() {
		if (processStart.isEmpty()) {
			return null;
		}
		return processStart;
	}

	/**
	 * Returns stored statistic
	 *
	 * @return Stored statistic
	 */
	public AbstractList<ProcessInfo> getStatistic() {
		return processList;
	}

	/**
	 * Opens the needed streams.
	 *
	 * @param filePath
	 *            File name and path where to save trace data
	 *
	 * @return True if stream could be opened otherwise false
	 */
	public final boolean openStreams(final String filePath) {

		boolean returnValue = false;
		try {
			// store used data file name
			usedFilePath = getFileName(filePath);

			// if file did not exists => create it
			if (file == null) {
				file = new File(usedFilePath);
			}

			// if fis did not exists => open it
			if (fis == null) {
				fis = new FileOutputStream(file, false);
			}
			returnValue = true;
		} catch (FileNotFoundException fno) {

			// if error occurs => close open streams
			closeStreams();
			returnValue = false;
		}
		return returnValue;
	}

	/**
	 * Executes parser.
	 *
	 * @param data
	 *            File name to be used
	 * @return true on success
	 */
	public final boolean parse(final String data) {

		try{
			// if no data => leave
			if (data == null) {
				//although the data is null
				//return true because false is returned only when
				//some unexpected error occurs
				return true;
			}
			
			

			// if data must be saved to project group\atool_temp folder
			if(saveDataToFile) {
				// open needed streams
				openStreams(usedFilePath);
			}

			// if data contains desired prefix => write data to file
			boolean contains = data.contains(Constants.PREFIX);

			if (contains) {
				writeDataToFile(data);
			}

			if (deferCallstacks && !saveDataToFile){
				filePos += (data.length()+lineBreakSize);
			}
			return true;
		}catch(OutOfMemoryError oome) {
			return false;
		}catch(Exception e) {
			Activator.getDefault().log(IStatus.ERROR, "AnalyzeTool - parsing trace data", e);
			return false;
		}
	}

	/**
	 * Fills dll load item information. Check what data file version is used,
	 * because data file format is changed.
	 * 
	 * @param dllLoad
	 *            Dll load item
	 * @param splittedText
	 *            One line of trace data file.
	 */
	private void fillDllLoadInfo(DllLoad dllLoad, String[] splittedText) {
		int processID = dllLoad.getProcessID();
		if(processes.containsKey(processID)) {
			ProcessInfo processInfo = processes.get(processID);

			//if data file contains also time stamp for dll load
			//this information is added later than other information
			//thats why we must check which version is used.
			if( processInfo.getTraceDataVersion() > 1 && splittedText.length > 6 ) {
				dllLoad.setLoadTime(splittedText[4]);
				lastTime = dllLoad.getLoadTime();
				dllLoad.setStartAddress(splittedText[5]);
				dllLoad.setEndAddress(splittedText[6]);
			}
			else {
				//load time note present - assume last available time (typically from an alloc or process start)
				dllLoad.setLoadTime(lastTime);
				dllLoad.setStartAddress(splittedText[4]);
				dllLoad.setEndAddress(splittedText[5]);
			}
		}
	}

	/**
	 * Parses "FREE" tag information from the trace data file.
	 *
	 * Note! This tag is not used in current version,
	 * this is support for older version of trace data files.
	 * @param splitted One line content of trace file.
	 */
	private void parseFree(String[] splitted) {
		
		// check that there is enough data
		// at least we need know to memory address 
		// which is forth item of trace data
		if( splitted.length > 3 ) {
			String processID = splitted[1];
			FreeInfo freeInfo = new FreeInfo(splitted[3]);
			freeInfo.setProcessID(processID);
			freeInfo.setFilePos(-1);//a free doesn't have a callstack
			removeMemAddress(freeInfo);
		}
	}


	/**
	 * Parse dealloction header from the line
	 * @param splitted Split trace message
	 */
	private void parseFreeHeader(String[] splitted) {
		//get free line info
		String processID = splitted[1];
		FreeInfo freeInfo = new FreeInfo(splitted[3]);
		freeInfo.setProcessID(processID);
		freeInfo.setFilePos(splitted.length > 6 ? filePos : -1);
		
		if( createGraphModel ) {
		
			int traceFileVersion = 1;
			//get trace file version
			if(processes.containsKey(freeInfo.getProcessID())) {
				ProcessInfo processInfo = processes.get(freeInfo.getProcessID());
				traceFileVersion = processInfo.getTraceDataVersion();
			}
			// how many callstack items
			int callstackCount = 0;

			// index where the callstack addresses begins
			int startIndex = 5;

			//if using the new trace file format
			if( traceFileVersion > 1 ) {
				freeInfo.setTime(splitted[4]);
				lastTime = freeInfo.getTime();
				callstackCount = Integer.parseInt(splitted[5], 16);
				startIndex = 6;
			}
			else {
				callstackCount = Integer.parseInt(splitted[4], 16);
			}

			if (!ignoreCallstacks && !deferCallstacks){
				AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
				createCallstack(splitted, freeInfo.getProcessID(), callstack, startIndex, lastTime);
				freeInfo.addCallstack(callstack);
				
				//if this free item contains fragments
				//so we must store this info to cache
				//and rest of the callstack items later
				if( callstackCount > (splitted.length-startIndex) ) {
					freeCache.put(freeInfo.getMemoryAddress(), freeInfo);
					//expect fragments
					remainingCallstacksMap.put(freeInfo, callstackCount - callstack.size());
				}				
			}
		}
		
		removeMemAddress(freeInfo);

		
	}


	/**
	 * Parse dealloction fragment from the line
	 * @param splitted Split trace message
	 */
	private void parseFreeFragment(String[] splitted)
	{
		if( createGraphModel ) {
			String procId = splitted[1];
			int processId = Integer.parseInt(procId,16);
			if (processes.containsKey(processId)) {
				String memAddr = splitted[3];
				Long memoryAddress =Long.parseLong(memAddr, 16);
				Long time = Long.parseLong(splitted[4],16);
				lastTime = time;
				String packetNumber = splitted[5];

				//if cache contains corresponding free info
				if (freeCache.containsKey(memoryAddress)) {
					FreeInfo info = freeCache.get(memoryAddress);
					if (info.getMemoryAddress() == memoryAddress && info.getTime() == time ) {
						AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
						createCallstack(splitted, processId, callstack, 6, time);
						info.updateFragment(callstack, packetNumber);

						int callstackCount = callstack.size();
						int remaining = remainingCallstacksMap.get(info);
						remaining -= callstackCount;
						if (remaining <= 0){
							remainingCallstacksMap.remove(info);
							freeCache.remove(info);
							info.finaliseCallstack();
						} else {
							remainingCallstacksMap.put(info, remaining);
						}
					}
					
				}
			}
		}
	}
	
	/**
	 * Mark dll as unloaded
	 * This provides functionality for dynamically loaded dll loads
	 * @param lineFragments elements of text to parse for unloading dll
	 */
	private void unloadDll(String[] lineFragments)
	{
		int processID = Integer.parseInt(lineFragments[1], 16);
		
		if(processes.containsKey(processID)) {
			ProcessInfo processInfo = processes.get(processID);
			String dllName = lineFragments[3];

			//timestamp only exists for more recent trace versions
			long dllUnloadTime;
			long startAddr;
			long endAddr;
			
			if( processInfo.getTraceDataVersion() > 1 && lineFragments.length > 6 ) {
				dllUnloadTime = Long.parseLong(lineFragments[4], 16);
				lastTime = dllUnloadTime;
				startAddr = Long.parseLong(lineFragments[5], 16);
				endAddr = Long.parseLong(lineFragments[6], 16);
			}
			else {
				//load time note present - assume last available time (typically from an alloc or process start)
				dllUnloadTime = lastTime;
				startAddr = Long.parseLong(lineFragments[4], 16);
				endAddr = Long.parseLong(lineFragments[5], 16);
			}
			
			ProcessInfo p = processes.get(processID);
			DllLoad dll = p.unloadOneDll(dllName, startAddr, endAddr, dllUnloadTime);
			
			//remove found dll load item from cache
			if (!ignoreCallstacks && !deferCallstacks && dll != null){
				for (Entry<Long, DllLoad> entry : dllLoadCache.entrySet()) {
					if (entry.getValue().equals(dll)){
						dllLoadCache.remove(entry.getKey());
						System.out.println("dllLoadCache.removedEntry for"+dll.getName());
					}
				}
			}
		}
	}

	/**
	 * Parse allocation header info from the line
	 * @param splitted Split trace message
	 */
	private void parseAllocHeader(String[] splitted)
	{
		try {
			String procID = splitted[1];
			int processID = Integer.parseInt(procID, 16);
			if (processes.containsKey(processID)) {
				AllocInfo oneAlloc = new AllocInfo(splitted[3]);
				oneAlloc.setProcessID(procID);
				oneAlloc.setFilePos(splitted.length > 6 ? filePos : -1);
				if( createGraphModel ) {
					oneAlloc.setTime(splitted[4]);
					lastTime = oneAlloc.getTime();
					
					oneAlloc.setSizeInt(Integer.parseInt(splitted[5], 16));
					// if one trace message contains callstack
					if (!ignoreCallstacks && !deferCallstacks && splitted.length > 6) {
						
						int callstackSize = Integer.parseInt(splitted[6], 16);

						AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
						createCallstack(splitted, processID, callstack, 7, oneAlloc.getTime());
						oneAlloc.addCallstack(callstack);
						
						callstackSize -= callstack.size();
						if (callstackSize > 0){
							//expect fragments
							remainingCallstacksMap.put(oneAlloc, callstackSize);
							allocCache.put(oneAlloc.getMemoryAddress(), oneAlloc);
						}
					}
				}
				addMemAddress(oneAlloc);
			}
		} catch (NumberFormatException nfe) {
			nfe.printStackTrace();
		}

	}

	/**
	 * Parse allocation fragment from the line
	 * 
	 * @param splitted Split trace message
	 */
	private void parseAllocFragment(String[] splitted) {
		if( createGraphModel ) {
			String procId = splitted[1];
			int processId = Integer.parseInt(procId, 16);
			if (processes.containsKey(processId)) {
				String memAddr = splitted[3];
				long memoryAddress = Long.parseLong(memAddr,16);
				long time = Long.parseLong(splitted[4],16);
				lastTime = time;
				String packetNumber = splitted[5];

				if (allocCache.containsKey(memoryAddress)) {
					AllocInfo info = allocCache.get(memoryAddress);
					if (info.getMemoryAddress() == memoryAddress && info.getTime() == time) {
						AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
						createCallstack(splitted, processId, callstack, 6, time);
						info.updateFragment(callstack, packetNumber);
						
						int callstackCount = callstack.size();
						int remaining = remainingCallstacksMap.get(info);
						remaining -= callstackCount;
						if (remaining <= 0){
							remainingCallstacksMap.remove(info);
							info.finaliseCallstack();
							allocCache.remove(info);
						} else {
							remainingCallstacksMap.put(info, remaining);
						}
					}
				}
			}
		}
	}

	/**
	 * Creates callstack values
	 * 
	 * @param splitted
	 *            Callstack values
	 * @param processId
	 *            Process id
	 * @param callstack
	 *            Callstack reference where the callstack values are added
	 * @param startIndex
	 *            Index where to start parse callstack values
	 */
	private void createCallstack(String[] splitted, int processId, AbstractList<AllocCallstack> callstack, int startIndex, long time) {
		// append whole callstack as a one memory address
		for (int i = startIndex; i < splitted.length; i++) {
			try{
				AllocCallstack allocCAll = new AllocCallstack(splitted[i]);

				// define dll load for current alloc
				DllLoad dllLoad = getDllLoadName(processId, Long.parseLong(splitted[i],16), time);
				if( dllLoad != null ) {
					allocCAll.setDllLoad(dllLoad);
					callstack.add(allocCAll);	
				}
			} catch(NumberFormatException nfe) {
				//no nothing by design
				
			} catch( Exception e ) {
				//no nothing by design
			}
			
		}
	}

	/**
	 * Parses memory address from the one trace message line. Parses one trace
	 * message whole callstack to one memory address. This memory address is
	 * used to calculate most used memory allocation location.
	 * 
	 * @param splitted Split trace message
	 */
	private void parseMemAddressesFromLine(String[] splitted) {

		String processID = splitted[1];
		if (processes.containsKey(processID)) {
			AllocInfo oneAlloc = new AllocInfo(splitted[3]);
			oneAlloc.setProcessID(processID);
			oneAlloc.setFilePos(splitted.length > 5 ? filePos : -1);
			oneAlloc.setTime(splitted[4]);
			lastTime = oneAlloc.getTime();
			oneAlloc.setSizeInt(Integer.parseInt(splitted[5], 16));

			// if one trace message contains callstack
			if (!ignoreCallstacks && !deferCallstacks && splitted.length > 5) {
				AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
				// append whole callstack as a one memory address
				for (int i = 6; i < splitted.length; i++) {
					AllocCallstack allocCAll = new AllocCallstack(splitted[i]);

					// define dll load for current alloc
					DllLoad dllLoad = getDllLoadName(oneAlloc.getProcessID(), allocCAll.getMemoryAddress(), lastTime);
					if (dllLoad != null) {
						allocCAll.setDllLoad(dllLoad);
					}

					callstack.add(allocCAll);
				}
				oneAlloc.addCallstack(callstack);
			}
			addMemAddress(oneAlloc);
		}
	}

	/**
	 * Returns dll load item for the memory address. Checks that entered memory
	 * address is dll load memory area
	 * 
	 * @param processId
	 *            Process id
	 * @param memoryAddress
	 *            Memory address
	 * @return DllLoad item if found otherwise null
	 */
	private DllLoad getDllLoadName(int processId, Long memoryAddress, long time) {
		if (processes.containsKey(processId)) {

			// check does cache contains already corresponding item
			if (dllLoadCache.containsKey(memoryAddress)) {
				return dllLoadCache.get(memoryAddress);
			}

			// no item found in the cache loop thru the loaded dlls
			ProcessInfo p = processes.get(processId);

			for (DllLoad oneLoad : p.getDllLoads()) {
				if (memoryAddress >= oneLoad.getStartAddress() && memoryAddress <= oneLoad.getEndAddress() && time >= oneLoad.getLoadTime() && time <= oneLoad.getUnloadTime()) {
					// dll load found => save it to cache and return it
					dllLoadCache.put(memoryAddress, oneLoad);
					return oneLoad;
				}
			}
		}
		return null;
	}

	/**
	 * process the line: switch on the event type and fill the model
	 * 
	 * @param aLine
	 *            One debug print
	 */
	private final void parseLine(final String aLine) {

		try {
			// parse switch
			int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded
			// with PCSS
			String usedString = null;

			if (index == -1 || index == 0) {
				usedString = aLine;
			} else {
				usedString = aLine.substring(index, aLine.length());
			}

			String[] lineFragments = usedString.split(" ");

			// determine the memory operation/event
			String event = null;
			if (lineFragments.length >= 3) {
				event = lineFragments[2];
			}

			DllLoad dllLoad = null;

			switch (Constants.Operation.toOperation(event)) {
			case PROCESS_START:
				// process PROCESS_START
				parseProcessStarted(lineFragments);
				break;
			case PROCESS_END:
				// process PROCESS_END
				parseProcessEnded(lineFragments);
				break;
			case ALLOC:
				// process ALLOC
				parseMemAddressesFromLine(lineFragments);
				break;
			case FREE:
				parseFree(lineFragments);
				break;
			case ALLOCH:
				parseAllocHeader(lineFragments);
				break;
			case FREEH:
				parseFreeHeader(lineFragments);
				break;
			case ALLOCF:
				if (!ignoreCallstacks && !deferCallstacks){
					parseAllocFragment(lineFragments);				
				}
				break;
			case FREEF:
				if (!ignoreCallstacks && !deferCallstacks){
					parseFreeFragment(lineFragments);				
				}
				break;
			case DLL_LOAD:
				if(createGraphModel && !ignoreCallstacks) {
					dllLoad = new DllLoad();
					dllLoad.setProcessID(lineFragments[1]);
					dllLoad.setName(lineFragments[3]);
					fillDllLoadInfo(dllLoad, lineFragments);
					addDllLoad(dllLoad);
				}
				break;
			case DLL_UNLOAD:
				if(createGraphModel && !ignoreCallstacks) {
					unloadDll(lineFragments);
				}
				break;
			default:
				// ignore this line
				break;
			}
			
		}catch(Exception e) {
			Activator.getDefault().log(IStatus.ERROR, "Error while parsing data", e);
		}
		
	}

	/**
	 * Processing PROCESS_END tag
	 * @param lineFragments String[] containing split PROCESS_END tag line
	 */
	private void parseProcessEnded(String[] lineFragments) {

		// process id
		String procId = null;

		// if data contains all the needed information
		if (lineFragments.length >= 2) {
			procId = lineFragments[1];
		}
		
		int processId = Integer.parseInt(procId, 16);
		if (processes.containsKey(processId)) {
			if( lineFragments.length > 4 ){
				//check is ABNORMAL process end and that there are enough data to parse
				ProcessInfo p = processes.get(processId); 
				if( lineFragments[4].equals(KEYWORD_ABNORMAL) && lineFragments.length > 5 ) {
					p.setEndTime(lineFragments[5]);	
				}
				else {
					p.setEndTime(lineFragments[4]);
				}	
				lastTime = p.getEndTime();
			}
			

			// store process id and process name
			this.processEnd.add(processId);

			if (processStart.containsValue(processId)) {
				for (java.util.Enumeration<String> e = processStart.keys(); e.hasMoreElements();) {
					Object key = e.nextElement();
					Object value = processStart.get(key);
					if (value.equals(processId)) {
						processStart.remove(key);
					}

				}
			}

			// PROCESS_END tag reached
			// check that started processes contains this process id.
			ProcessInfo info = processes.get(processId);

			// process found from the started processes list
			// add it to list
			if (info != null) {
				addProcessInfoToList(processes.get(processId));
			}

			// remove process id from processes list
			processes.remove(processId);

			// clear the founded dll load items list
			// this prevent that results between runs do not mixed up
			if (dllLoadCache != null){
				dllLoadCache.clear();				
			}
		}
	}

	/**
	 * Processing PROCESS_START tag
	 * @param lineFragments String[] containing split PROCESS_START tag line
	 */
	private void parseProcessStarted(String[] lineFragments) {

		String procId = lineFragments[1];
		int processId = Integer.parseInt(procId, 16);

		ProcessInfo processInfo = new ProcessInfo();
		processInfo.setProcessID(processId);

		if (lineFragments.length >= 4) {
			processInfo.setProcessName(lineFragments[3]);
		}

		if (lineFragments.length > 5) {
			processInfo.setStartTime(lineFragments[5]);
			lastTime = processInfo.getStartTime();
		}

		// set trace data version number
		// this information is used later when parsing e.g. one allocation
		// information
		if (lineFragments.length > 7) {
			processInfo.setTraceDataVersion(lineFragments[7]);
		}

		// store process id and process name
		if( lineFragments.length > 3 ) {
			this.processStart.put(lineFragments[3], processId);
			processes.put(processId, processInfo);
		}
	}

	/**
	 * Adds process info to the list
	 * 
	 * @param info
	 *            ProcessInfo reference
	 */
	private void addProcessInfoToList(ProcessInfo info) {
		if (processList.isEmpty()) {
			processList.add(info);
			return;
		}
		for (int i = 0; i < processList.size(); i++) {
			ProcessInfo tempInfo = processList.get(i);
			if( info.getStartTime() != null && info.getStartTime() < tempInfo.getStartTime() ){
				processList.add(i, info);
				return;
			}
		}
		processList.add(info);
	}

	/**
	 * Writes data to file if line contains wanted data.
	 * 
	 * @param data
	 *            Data to write
	 */
	private void writeDataToFile(String data) {
		try {

			// check if data contains PROCESS_START or PROCESS_END tag
			parseLine(data);

			// if saveDataToFile flag is true => save data
			if (saveDataToFile) {

				if (fis == null) {

					// streams not open => try to open
					boolean returnValue = this.openStreams(usedFilePath);
					if (!returnValue) {
						return;
					}
				}

				// append line feed and write given data to file
				String dataAndLineFeed = data + "\n";

				// write data
				fis.write(dataAndLineFeed.getBytes("UTF-8"));
				filePos = deferCallstacks ? fis.getChannel().position() : -1;
			}

		} catch (IOException ioe) {
			return;
		}
	}
	
	/**
	 * Returns true if callstack reading from file is done
	 * on demand; false if callstacks are made available during parsing
	 * phase.
	 * @return true for deferred callstack reading
	 */
	public boolean hasDeferredCallstacks(){
		return deferCallstacks;
	}
}