sysperfana/analyzetoolext/com.nokia.s60tools.analyzetool/src/com/nokia/s60tools/analyzetool/engine/ParseAnalyzeData.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:22:14 +0200
changeset 1 1050670c6980
child 6 f65f740e69f9
permissions -rw-r--r--
Initial version of AnalyzeTool under EPL

/*
 * Copyright (c) 2008-2009 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.Hashtable;
import java.util.Iterator;

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.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 tracing utility.
 * If one message contains PCSS prefix the message will 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 free info is separated to multiple lines.
	 */
	private final Hashtable<Long, AllocInfo> allocCache;


	/**
	 * Deallocation cache.
	 * Used when one free info is separated to multiple lines.
	 */
	private final Hashtable<Long, FreeInfo> freeCache;


	/**
	 * 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 final Hashtable<Long, DllLoad> dllLoadCache;


	/**
	 * 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;
	/**
	 * Constructor.
	 * @param saveData Need to save data to file.
	 */
	public ParseAnalyzeData(boolean saveData, boolean createModel) {

		processStart = new Hashtable<String, Integer>();
		processEnd = new ArrayList<Integer>();
		processes = new Hashtable<Integer, ProcessInfo>();
		processList = new ArrayList<ProcessInfo>();
		allocCache = new Hashtable<Long, AllocInfo>();
		saveDataToFile = saveData;
		dllLoadCache = new Hashtable<Long, DllLoad>();
		freeCache = new Hashtable<Long, FreeInfo>();
		createGraphModel = createModel;
	}

	/**
	 * 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;
			}
		} 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();
		allocCache.clear();
		freeCache.clear();
		dllLoadCache.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
	 */
	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);
			}
			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]);
				dllLoad.setStartAddress(splittedText[5]);
				dllLoad.setEndAddress(splittedText[6]);
			}
			else {
				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();
			freeInfo.setProcessID(processID);
			freeInfo.setMemoryAddress(splitted[3]);
			removeMemAddress(freeInfo);
		}
	}


	/**
	 * Parse dealloction header from the line
	 * @param line Allocation header line
	 */
	private void parseFreeHeader(String[] splitted) {
		//get free line info
		String processID = splitted[1];
		FreeInfo freeInfo = new FreeInfo();
		freeInfo.setProcessID(processID);

		freeInfo.setMemoryAddress(splitted[3]);
		
		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 callstakc 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]);
				callstackCount = Integer.parseInt(splitted[5], 16);
				startIndex = 6;
			}
			else {
				callstackCount = Integer.parseInt(splitted[5], 16);
			}

			AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
			createCallstack(splitted, freeInfo.getProcessID(), callstack, startIndex);
			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);
			}
		}
		
		removeMemAddress(freeInfo);

		
	}


	/**
	 * Parse dealloction fragment from the line
	 * @param line Allocation fragment line
	 */
	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);
				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);
						info.updateFragment(callstack, packetNumber);
					}
				}
			}
		}
	}
	
	/**
	 * Remove dll load from the list.
	 * This provides functionality for dynamically loaded dll loads
	 * @param dllLoad DllLoad item to unload
	 */
	private void unloadDll( DllLoad dllLoad )
	{
		if (processes.containsKey(dllLoad.getProcessID())) {
			ProcessInfo tempProcessInfo = processes.get(dllLoad.getProcessID());
			tempProcessInfo.unloadOneDll(dllLoad);

			//remove found dll load item from cache
			for( java.util.Enumeration<Long> e = dllLoadCache.keys(); e.hasMoreElements();)
			{
				Long key = e.nextElement();
				DllLoad tempDllLoad = dllLoadCache.get(key);

				// if values equals remove it from the list
				if (tempDllLoad.getProcessID() == dllLoad.getProcessID() && tempDllLoad.getStartAddress() == dllLoad.getStartAddress()
						&& tempDllLoad.getEndAddress() == dllLoad.getEndAddress() && tempDllLoad.getName() == tempDllLoad.getName()) {
					dllLoadCache.remove(key);
				}
			}

		}
	}

	/**
	 * Parse allocation header info from the line
	 * @param line Allocation line
	 */
	private void parseAllocHeader(String[] splitted)
	{
		try {
			String procID = splitted[1];
			int processID = Integer.parseInt(procID, 16);
			if (processes.containsKey(processID)) {
				AllocInfo oneAlloc = new AllocInfo();
				oneAlloc.setProcessID(procID);
				oneAlloc.setMemoryAddress(splitted[3]);
				if( createGraphModel ) {
					oneAlloc.setTime(splitted[4]);
					
					oneAlloc.setSizeInt(Integer.parseInt(splitted[5], 16));
					// if one trace message contains callstack
					if (splitted.length > 6) {

						AbstractList<AllocCallstack> callstack = new ArrayList<AllocCallstack>();
						createCallstack(splitted, processID, callstack, 7);
						oneAlloc.addCallstack(callstack);
						allocCache.put(oneAlloc.getMemoryAddress(), oneAlloc);
					}
				}
				addMemAddress(oneAlloc);
			}
		} catch (NumberFormatException nfe) {
			nfe.printStackTrace();
		}

	}

	/**
	 * Parse allocation fragment from the line
	 * 
	 * @param line
	 *            Allocation fragment line
	 */
	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);
				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);
						info.updateFragment(callstack, packetNumber);
					}
				}
			}
		}
	}

	/**
	 * 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) {
		// append whole callstack as a one memory address
		for (int i = startIndex; i < splitted.length; i++) {
			try{
				AllocCallstack allocCAll = new AllocCallstack();
				allocCAll.setMemoryAddress(splitted[i]);

				// define dll load for current alloc
				DllLoad dllLoad = getDllLoadName(processId, Long.parseLong(splitted[i],16));
				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 line
	 *            One trace message
	 */
	private void parseMemAddressesFromLine(String[] splitted) {

		//TODO
		String processID = splitted[1];
		if (processes.containsKey(processID)) {
			AllocInfo oneAlloc = new AllocInfo();
			oneAlloc.setProcessID(processID);
			oneAlloc.setMemoryAddress(splitted[3]);
			oneAlloc.setTime(splitted[4]);
			oneAlloc.setSizeInt(Integer.parseInt(splitted[5], 16));

			// if one trace message contains callstack
			if (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();
					allocCAll.setMemoryAddress(splitted[i]);

					// define dll load for current alloc
					DllLoad dllLoad = getDllLoadName(oneAlloc.getProcessID(), allocCAll.getMemoryAddress());
					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) {
		if (processes.containsKey(processId)) {
			ProcessInfo tempProcessInfo = processes.get(processId);
			Hashtable<String, DllLoad> loads = tempProcessInfo.getDllLoads();

			// 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
			for (java.util.Enumeration<String> e = loads.keys(); e.hasMoreElements();) {
				try {
					String key = e.nextElement();
					DllLoad oneLoad = loads.get(key);
					Long start = oneLoad.getStartAddress();
					Long end =  oneLoad.getEndAddress();
					Long actual = memoryAddress;
					if (actual >= start && actual <= end) {
						// dll load found => save it to cache and return it
						dllLoadCache.put(memoryAddress, oneLoad);
						return oneLoad;
					}
				} catch (java.lang.NumberFormatException nfe) {
					// trace message corrupt?
				}
			}
		}
		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:
				parseAllocFragment(lineFragments);
				break;
			case FREEF:
				parseFreeFragment(lineFragments);
				break;
			case DLL_LOAD:
				if(createGraphModel) {
					dllLoad = new DllLoad();
					dllLoad.setProcessID(lineFragments[1]);
					dllLoad.setName(lineFragments[3]);
					fillDllLoadInfo(dllLoad, lineFragments);
					addDllLoad(dllLoad);
				}
				break;
			case DLL_UNLOAD:
				if(createGraphModel) {
					dllLoad = new DllLoad();
					dllLoad.setProcessID(lineFragments[1]);
					dllLoad.setName(lineFragments[3]);
					fillDllLoadInfo(dllLoad, lineFragments);
					unloadDll(dllLoad);
				}
				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
				if( lineFragments[4].equals(KEYWORD_ABNORMAL) && lineFragments.length > 5 ) {
					processes.get(processId).setEndTime(lineFragments[5]);	
				}
				else {
					processes.get(processId).setEndTime(lineFragments[4]);
				}	
			}
			

			// 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
			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]);
		}

		// 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"));
			}

		} catch (IOException ioe) {
			return;
		}
	}
}