crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/containers/Thread.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:06:45 +0200
changeset 0 5ad7ad99af01
child 4 615035072f7e
permissions -rw-r--r--
Initial version of CrashAnalyser under EPL

/*
* Copyright (c) 2008 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:
*
*/

package com.nokia.s60tools.crashanalyser.containers;

import org.w3c.dom.*;

import com.nokia.s60tools.crashanalyser.model.XmlUtils;
import com.nokia.s60tools.crashanalyser.containers.Process.StackItems;
import com.nokia.s60tools.crashanalyser.data.ErrorLibrary;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.*;

/**
 * Contains thread data.
 *
 */
public final class Thread {

	// XML tags
	public static final String TAG_ID = "id";
	public static final String TAG_FULLNAME = "fullname";
	public static final String TAG_EXIT_INFO = "exit_info";
	public static final String TAG_EXIT_TYPE = "exit_type";
	public static final String TAG_EXIT_CATEGORY = "exit_category";
	public static final String TAG_EXIT_REASON = "exit_reason";
	public static final String TAG_LINK = "link";
	public static final String ATTRIBUTE_SEG = "seg";
	public static final String SEGMENT_STACKS = "seg_stacks";
	public static final String SEGMENT_REGISTERS = "seg_registers";
	
	static final String FORMAT = "%-13s: %s";
	
	private final int threadId;
	private final String threadFullName;
	private final String threadExitType;
	private final String threadExitCategory;
	private final String threadExitReason;
	private final String threadPanicDescription;
	// PC, SP and LR do not really belong to thread,
	// but these are taken from this thread's stack which
	// contains CPSR. So these are the most "interesting" 
	// PC, SP and LR values for this thread, even though 
	// this thread can own multiple stacks and hence multiple
	// PC, SP and LR.
	private final String threadProgramCounter;
	private final String threadStackPointer;
	private final String threadLinkRegister;
	private final List<Stack> threadStacks;
	private final List<RegisterSet> threadRegisters;

	private Thread(int id, String fullName, String exitType, String exitCategory, 
					String panicDescription, String programCounter, String stackPointer, String linkRegister, 
					String exitReason, List<Stack> stacks, List<RegisterSet> registers) {
		threadId = id;
		threadFullName = fullName;
		threadExitType = exitType;
		threadExitCategory = exitCategory;
		threadExitReason = exitReason;
		threadProgramCounter = programCounter;
		threadStackPointer = stackPointer;
		threadLinkRegister = linkRegister;
		threadStacks = stacks;
		threadPanicDescription = panicDescription;
		threadRegisters = registers;
	}
	
	public int getId() {
		return threadId;
	}
	
	public String getFullName() {
		return threadFullName;
	}
	
	public String getExitType() {
		return threadExitType;
	}
	
	public String getExitCategory() {
		return threadExitCategory;
	}
	
	public String getExitReason() {
		return threadExitReason;
	}
	
	public String getProgramCounter() {
		return threadProgramCounter;
	}
	
	public String getStackPointer() {
		return threadStackPointer;
	}
	
	public String getLinkRegister() {
		return threadLinkRegister;
	}
	
	public String getPanicDescription() {
		return threadPanicDescription;
	}
	
	public List<Stack> getStacks() {
		return threadStacks;
	}
	
	public List<RegisterSet> getRegisters() {
		return threadRegisters;
	}
	
	/**
	 * Writes thread data into given buffer (i.e. text file)
	 * @param out
	 * @param stackItems
	 * @param html
	 * @throws IOException
	 */
	public void writeTo(BufferedWriter out, StackItems stackItems, boolean html) throws IOException {
		writeLine(out,"");
		writeLine(out, "THREAD:");
		writeLine(out, "--------");
		writeLine(out, "Thread Name", threadFullName);
		writeLine(out, "Exit Type", threadExitType);
		
		if ("Exception".equals(threadExitType)) {
			writeLine(out, "Exit Reason", threadExitReason);
		} else {
			writeLine(out, "Exit Reason", threadExitCategory + " - " +threadExitReason);
		}
		
		writeLine(out, "");
		if (threadRegisters != null && !threadRegisters.isEmpty()) {
			for (int i = 0; i < threadRegisters.size(); i++) {
				RegisterSet registerSet = threadRegisters.get(i);
				registerSet.writeTo(out);
				writeLine(out, "");
			}
		}
		
		if (threadStacks != null && !threadStacks.isEmpty()) {
			for (int i = 0; i < threadStacks.size(); i++) {
				Stack stack = threadStacks.get(i);
				stack.writeTo(out, stackItems, html);
			}
		}
	}
	
	void writeLine(BufferedWriter out, String line) throws IOException {
		out.write(line);
		out.newLine();
	}
	
	void writeLine(BufferedWriter out, String header, String value) throws IOException {
		if (!"".equals(value)) {
			out.write(String.format(FORMAT, header, value));
			out.newLine();
		}
	}
	
	/**
	 * Reads and creates thread from thread xml element
	 * @param elementThread
	 * @param registers
	 * @param symbols
	 * @param stacks
	 * @param errorLibrary
	 * @return created thread or null
	 */
	public static Thread read(Element elementThread,
								Map<Integer, RegisterSet> registers,
								Map<Integer, Symbol> symbols,
								Map<Integer, Stack> stacks,
								ErrorLibrary errorLibrary) {
		try {
			// read thread id
			String threadId = XmlUtils.getTextValue(elementThread, TAG_ID);
			if (threadId == null)
				return null;
			
			// convert thread id to integer
			int id;
			try {
				id = Integer.parseInt(threadId);
			} catch (Exception e) {
				return null;
			}
			
			// read the threads full name
			String fullName = XmlUtils.getTextValue(elementThread, TAG_FULLNAME);
			if (fullName == null)
				return null;
			
			String exitType = "";
			String exitCategory = "";
			String exitReason = "";
			// get child nodes such as exit_info, stacks, registers
			NodeList childNodes = elementThread.getChildNodes();
			if (childNodes == null || childNodes.getLength() < 1)
				return null;
			
			// read Exit info
			NodeList exitInfo = elementThread.getElementsByTagName(TAG_EXIT_INFO);
			if (exitInfo != null && exitInfo.getLength() > 0) {
				NodeList exitInfos = exitInfo.item(0).getChildNodes();
				if (exitInfos != null && exitInfos.getLength() > 0) {
					for (int i = 0; i < exitInfos.getLength(); i++) {
						Node el = exitInfos.item(i);
						Node firstChild = null;
						if (TAG_EXIT_TYPE.equals(el.getNodeName())) {
							// read exit type (Exception, Panic, Kill, Terminate)
							firstChild = el.getFirstChild();
							if (firstChild != null) {
								exitType = firstChild.getNodeValue();
								if (exitType == null) {
									exitType = "";
								}
							} else {
								exitType = "";
							}
						} else if (TAG_EXIT_CATEGORY.equals(el.getNodeName())) {
							// read exit category (e.g W32)
							firstChild = el.getFirstChild();
							if (firstChild != null) {
								exitCategory = firstChild.getNodeValue();
								if (exitCategory == null)
									exitCategory = "";
							} else {
								exitCategory = "";
							}
						} else if (TAG_EXIT_REASON.equals(el.getNodeName())) {
							// read exit reason (e.g 3)
							firstChild = el.getFirstChild();
							if (firstChild != null) {							
								exitReason = firstChild.getNodeValue();
								if (exitReason == null)
									exitReason = "";
							} else {
								exitReason = "";
							}								
						}
					}
				}
			}

			if ("Exception".equals(exitType)) {
				exitReason = exitCategory;
				exitCategory = "Exceptions";
			}
			
			
			String panicDescription = "";
			if (!"".equals(exitCategory) && !"".equals(exitReason)) {
				panicDescription = errorLibrary.getPanicDescription(exitCategory, exitReason);
			}
			
			List<Stack> threadStacks = new ArrayList<Stack>();
			List<RegisterSet> threadRegisters = new ArrayList<RegisterSet>();
			String programCounter = "";
			String stackPointer = "";
			String linkRegister = "";
			
			// see if register has a symbol and/or message
			NodeList nl = elementThread.getElementsByTagName(TAG_LINK);
			if (nl != null && nl.getLength() > 0) {
				for (int i = 0; i < nl.getLength(); i++) {
					Node linkNode = nl.item(i);
					String nodeValue = XmlUtils.getNodeValue(linkNode);
					NamedNodeMap attributes = linkNode.getAttributes();
					if (attributes != null && attributes.getLength() > 0) {
						Node seg = attributes.getNamedItem(ATTRIBUTE_SEG);
						// stack id
						if (SEGMENT_STACKS.equals(XmlUtils.getNodeValue(seg))) {
							int sId = Integer.parseInt(nodeValue);
							if (stacks.containsKey(sId)) {
								Stack s = stacks.get(sId);
								threadStacks.add(s);
								// the most interesting PC, SP and LR comes from
								// that stack which contains CPSR.
								if (s.stackRegisterContainsCpsr()) {
									programCounter = s.getProgramCounter();
									stackPointer = s.getStackPointer();
									linkRegister = s.getLinkRegister();
								}
							}
						// register id
						} else if (SEGMENT_REGISTERS.equals(XmlUtils.getNodeValue(seg))) {
							int rId = Integer.parseInt(nodeValue);
							// if passed registers list contains a register for this id
							if (registers.containsKey(rId)) {
								RegisterSet registerSet = registers.get(rId);
								threadRegisters.add(registerSet);								
							}							
						}
					}
				}
			}

			return new Thread(id, fullName, exitType, exitCategory, 
								panicDescription, programCounter, stackPointer, linkRegister,
								exitReason, threadStacks, threadRegisters);

		} catch (Exception e) {
			return null;
		}
	}
	
	public Map<Integer, Stack> removeOwnStacks(Map<Integer, Stack> stacks) {
		
		if (threadStacks != null && !threadStacks.isEmpty()) {
			for (int i = 0; i < threadStacks.size(); i++) {
				if (stacks.containsKey(threadStacks.get(i).getId())) {
					stacks.remove(threadStacks.get(i).getId());
				}
			}
		}
		
		return stacks;
	}
	
	public Map<Integer, RegisterSet> removeOwnRegisterSets(Map<Integer, RegisterSet> registerSets) {
		
		if (threadRegisters != null && !threadRegisters.isEmpty()) {
			for (int i = 0; i < threadRegisters.size(); i++) {
				if (registerSets.containsKey(threadRegisters.get(i).getId())) {
					registerSets.remove(threadRegisters.get(i).getId());
				}
			}
		}
		
		return registerSets;
	}
	
}