crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/files/SummaryFile.java
changeset 0 5ad7ad99af01
child 4 615035072f7e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/files/SummaryFile.java	Thu Feb 11 15:06:45 2010 +0200
@@ -0,0 +1,685 @@
+/*
+* 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.files;
+
+import java.util.*;
+import java.io.*;
+import javax.xml.parsers.*;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.*;
+import org.w3c.dom.*;
+import com.nokia.s60tools.crashanalyser.containers.RegisterDetails;
+import com.nokia.s60tools.crashanalyser.containers.CodeSegment;
+import com.nokia.s60tools.crashanalyser.containers.Message;
+import com.nokia.s60tools.crashanalyser.containers.EventLog;
+import com.nokia.s60tools.crashanalyser.containers.Process;
+import com.nokia.s60tools.crashanalyser.containers.RegisterSet;
+import com.nokia.s60tools.crashanalyser.containers.Register;
+import com.nokia.s60tools.crashanalyser.containers.Summary;
+import com.nokia.s60tools.crashanalyser.containers.Symbol;
+import com.nokia.s60tools.crashanalyser.containers.Thread;
+import com.nokia.s60tools.crashanalyser.containers.Stack;
+import com.nokia.s60tools.crashanalyser.data.*;
+import com.nokia.s60tools.crashanalyser.model.*;
+import com.nokia.s60tools.crashanalyser.plugin.CrashAnalyserPlugin;
+
+/**
+ * A summary file is a crash file which has not been fully decoded.
+ * A summary file has been decoded without symbol information and it therefore
+ * does not contain stack information. A summary file is a base class for CrashFile. 
+ *
+ */
+public class SummaryFile extends CrashAnalyserFile implements IEditorInput {
+
+	// XML tags
+	public static final String TAG_SYMBOL_SET = "symbol_set";
+	public static final String TAG_SYMBOL = "symbol";
+	public static final String TAG_SOURCE = "source";
+	public static final String TAG_PROCESS = "process";
+	public static final String TAG_THREAD = "thread";
+	public static final String TAG_STACK = "stack";
+	public static final String TAG_SEG_STACKS = "seg_stacks";
+	public static final String TAG_ID = "id";
+	public static final String TAG_STACK_ENTRY = "stack_entry";
+	public static final String TAG_CODESEG = "codeseg";
+	public static final String TAG_REGISTER_SET = "register_set";
+	public static final String TAG_SEG_EVENT_LOG = "seg_event_log";
+	public static final String TAG_MESSAGE = "message";
+	public static final String TAG_VI_ENTRY = "vi_entry";
+	public static final String TAG_SOURCE_INFO = "source_info";
+	public static final String TAG_TYPE = "type";
+	public static final String TAG_SEGMENT = "segment";
+	public static final String TAG_MAJOR = "major";
+	public static final String TAG_MINOR = "minor";
+
+	// data from xml file
+	protected Map<Integer, Process> processes = null;
+	protected Map<Integer, Message> messages = null;
+	protected Map<Integer, Stack> stacks = null;
+	protected Map<Integer, RegisterSet> registerSets = null;
+	protected List<RegisterDetails> registerDetails = null;
+	protected EventLog eventLog = null;
+	protected Summary crashSummary = null;
+	protected String sourceFileType = "";
+	protected String sourceFileName = "";
+	protected String sourceFilePath = "";
+
+	/**
+	 * Constructor
+	 * @param filePath file path to this crash file
+	 * @param library error library
+	 */
+	protected SummaryFile(String filePath, ErrorLibrary library) {
+		super(filePath, library);
+	}
+	
+	/**
+	 * Returns the file type of this crash file.
+	 * @return "Decoded File"
+	 */
+	public String getFileType() {
+		return "Partially Decoded File";
+	}
+	
+	public String getSourceFileType() {
+		return sourceFileType;
+	}
+	
+	public String getSourceFileName() {
+		return sourceFileName;
+	}
+	
+	public String getSourceFilePath() {
+		return sourceFilePath;
+	}
+	
+	public List<Stack> getStandAloneStacks() {
+		if (stacks != null && !stacks.isEmpty()) {
+			return new ArrayList<Stack>(stacks.values());
+		}
+		
+		return null;
+	}
+	
+	public List<RegisterSet> getStandAloneRegisterSets() {
+		if (registerSets != null && !registerSets.isEmpty()) {
+			return new ArrayList<RegisterSet>(registerSets.values());
+		}
+		
+		return null;
+	}
+	
+	/**
+	 * Checks whether this file contains any error or warning messages.
+	 * @return true if file contains errors or warning, otherwise false is returned
+	 */
+	public boolean containsErrorsOrWarnings() {
+		List<Message> msgs = getMessages();
+		if (msgs != null && !msgs.isEmpty()) {
+			for (int i = 0; i < msgs.size(); i++) {
+				if (Message.MessageTypes.ERROR.equals(msgs.get(i).getMessageType()) ||
+					Message.MessageTypes.WARNING.equals(msgs.get(i).getMessageType()))
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * Returns all messages
+	 * @return all messages
+	 */
+	public List<Message> getMessages() {
+		List<Message> msgs = new ArrayList<Message>();
+		msgs.addAll(messages.values());
+		return msgs;
+	}
+
+	/**
+	 * Reads crash file
+	 * @param file crash file
+	 * @param library error library
+	 * @return read crash file or null
+	 */
+	public static SummaryFile read(File file, ErrorLibrary library) {
+		if (file == null || !file.exists() || !file.isFile())
+			return null;
+		
+		SummaryFile summaryFile = new SummaryFile(file.getAbsolutePath(), library);
+		summaryFile.doRead();
+		return summaryFile;
+	}
+	
+	/**
+	 * Reads crash file
+	 * @param folder where xml file is
+	 * @param library error library
+	 * @return read crash file or null
+	 */
+	public static SummaryFile read(String folder, ErrorLibrary library) {
+		String summaryFile = findFile(folder,"xml");
+		
+		// summary file doesn't exist
+		if (summaryFile == null)
+			return null;
+		
+		SummaryFile file = new SummaryFile(summaryFile, library);
+		file.doRead();
+		return file;
+	}
+
+	/**
+	 * Writes crash file into text or html file
+	 * @param filePathWithoutFileName file path where file is to be written to
+	 * @param html if false a .txt file is created. if true a .html file is created
+	 */
+	public void writeTo(String filePathWithoutFileName, boolean html) {
+		File file = new File(FileOperations.addSlashToEnd(filePathWithoutFileName) + 
+								FileOperations.getFileNameWithoutExtension(fileName) + ".txt");
+		if (html)
+			file = new File(FileOperations.addSlashToEnd(filePathWithoutFileName) + 
+								FileOperations.getFileNameWithoutExtension(fileName) + ".html");
+		writeTo(file);
+	}
+	
+	/**
+	 * Writes crash file into a file
+	 * @param file file to be written to (.xml, .crashxml, .html, . txt, etc)
+	 */
+	public void writeTo(File file) {
+		try {
+			// file is saved as .xml or .crashxml
+			if (file.getName().endsWith(CrashAnalyserFile.OUTPUT_FILE_EXTENSION) ||
+					file.getName().endsWith("."+CrashAnalyserFile.SUMMARY_FILE_EXTENSION)) {
+				FileOperations.copyFile(new File(filePath), file, true);
+			// file is saved as .txt or .html
+			} else {
+				BufferedWriter out = null;
+				try {
+					// Create file 
+					FileWriter fstream = new FileWriter(file, false);
+					out = new BufferedWriter(fstream);
+					
+					// check whether we are writing an html file
+					boolean html = false;
+					if (file.getName().toLowerCase().endsWith(".htm") ||
+							file.getName().toLowerCase().endsWith(".html"))
+						html = true;
+					
+					// if html file, write html start tags
+					if (html)
+						writeHtmlStart(out);
+
+					// write crash analyser file version data
+					writeVersion(out);
+
+					String panicDescription = "";
+
+					// write process & thread summary
+					Process process = getCrashedProcess();
+					if (process != null) {
+						writeLine(out, "Process", process.getName());
+
+						Thread thread = process.getFirstThread();
+						if (thread != null) {
+							writeLine(out, "Thread", thread.getFullName());
+							writeLine(out, "Stack Pointer", thread.getStackPointer());
+							writeLine(out, "Link Register", thread.getLinkRegister());
+							writeLine(out, "Program Counter", thread.getProgramCounter());
+							panicDescription = thread.getPanicDescription();
+						}
+					}
+					
+					// write crash summary to file
+					if (crashSummary != null) {
+						crashSummary.writeTo(out);
+						out.newLine();
+					}
+
+					if (html)
+						writeLine(out, "</pre><A HREF=\"#STACKPOINTER\">Current Stack Pointer</A><pre>");
+					
+					// write panic description if panic description exists. Panic description
+					// is written only to html file (not to .txt)
+					if (!"".equals(panicDescription) && html) {
+						writeLine(out, "</pre><h2>Panic Description</h2>");
+						writeLine(out, panicDescription);
+						writeLine(out, "<pre>");
+					}
+					
+					List<Message> msgs = getMessages();
+					// write errors and warnings to file
+					if (msgs != null && !msgs.isEmpty()) {
+						for (int i = 0; i < msgs.size(); i++) {
+							msgs.get(i).writeTo(out);
+						}
+						out.newLine();
+					}
+					
+					// write event log to file
+					if (eventLog != null)
+						eventLog.writeTo(out);					
+						
+					// write process data to file
+					if (process != null) {
+						process.writeTo(out, Process.StackItems.ALL, html);
+						out.newLine();
+					}
+
+					// if html file, write html end tags
+					if (html)
+						writeHtmlEnd(out);
+					
+				} catch (Exception e) {
+					e.printStackTrace();
+				} finally {
+					if (out != null)
+						out.close();
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * Writes CrashAnalyser version information.
+	 * @param out where to write
+	 * @throws IOException
+	 */
+	void writeVersion(BufferedWriter out) throws IOException {
+		String version = (String) CrashAnalyserPlugin.getDefault().getBundle().getHeaders().get("Bundle-Version"); //$NON-NLS-1$
+		out.write("Crash Report Created with Crash Analyser Carbide Extension " + version);
+		out.newLine();
+		out.write("----------------------------------------------------------------");
+		out.newLine();
+		out.newLine();
+	}
+	
+	/**
+	 * Writes html start tags
+	 * @param out where to write
+	 * @throws IOException
+	 */
+	void writeHtmlStart(BufferedWriter out) throws IOException {
+		out.write("<html><head><title>Crash File</title></head><body><pre>");
+		out.newLine();
+	}
+	
+	/**
+	 * Writes html end tags
+	 * @param out where to write
+	 * @throws IOException
+	 */
+	void writeHtmlEnd(BufferedWriter out) throws IOException {
+		out.newLine();
+		out.write("</pre></body></html>");
+		out.newLine();
+	}
+	
+	void writeLine(BufferedWriter out, String header, String value) throws IOException {
+		if (!"".equals(value)) {
+			out.write(String.format(Summary.FORMAT, header, value));
+			out.newLine();
+		}
+	}
+
+	void writeLine(BufferedWriter out, String line) throws IOException {
+		out.write(line);
+		out.newLine();
+	}
+
+	/**
+	 * Reads the file location of the given xml file
+	 * @param xmlFilePath path to xml file
+	 * @return file location or "".
+	 */
+	public static String getSourceFilePath(String xmlFilePath) {
+
+		try {
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+			// Using factory get an instance of document builder
+			DocumentBuilder db = dbf.newDocumentBuilder();
+
+			// parse using builder to get DOM representation of the XML file
+			Document dom = db.parse(xmlFilePath);
+			 
+			// get the root element
+			Element docEle = dom.getDocumentElement();		
+			
+			// read source file name
+			NodeList nl = docEle.getElementsByTagName(TAG_SOURCE);
+			if (nl != null && nl.getLength() > 0) {
+				String sourcePath = XmlUtils.getNodeValue(nl.item(0));
+				if (sourcePath != null && !"".equals(sourcePath)) {
+					File f = new File(sourcePath);
+					if (f.exists() && f.isFile()) {
+						return sourcePath;
+					}
+				}
+			}
+		}catch(Exception e) {
+			e.printStackTrace();
+		}
+		
+		return "";
+	}
+	
+	@Override
+	protected void doRead() {
+		super.doRead();
+		
+		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+		try {
+
+			// Using factory get an instance of document builder
+			DocumentBuilder db = dbf.newDocumentBuilder();
+
+			// parse using builder to get DOM representation of the XML file
+			Document dom = db.parse(filePath);
+			 
+			// get the root element
+			Element docEle = dom.getDocumentElement();		
+			
+			// read source file type
+			NodeList nl = docEle.getElementsByTagName(TAG_SOURCE_INFO);
+			if (nl != null && nl.getLength() > 0) {
+				sourceFileType = XmlUtils.getTextValue((Element)nl.item(0), TAG_TYPE);
+			}
+			
+			// read source file name
+			nl = docEle.getElementsByTagName(TAG_SOURCE);
+			if (nl != null && nl.getLength() > 0) {
+				String sourcePath = XmlUtils.getNodeValue(nl.item(0));
+				if (sourcePath != null && !"".equals(sourcePath)) {
+					File f = new File(sourcePath);
+					if (f.exists() && f.isFile()) {
+						sourceFileName = f.getName();
+						sourceFilePath = sourcePath;
+					}
+				}
+			}
+			
+			messages = new HashMap<Integer, Message>();
+			
+			// check that is the xml file newer than what we support
+			nl = docEle.getElementsByTagName(TAG_SEGMENT);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					// if major number is larger than what we support, add an error to messages
+					String major = XmlUtils.getTextValue((Element)nl.item(i), TAG_MAJOR);
+					if (!"1".equals(major)) {
+						messages.put(500, Message.newMessage(500, "Version Error", "XML file is newer than what is supported by this version of Crash Analyser.", Message.MessageTypes.ERROR));
+						break;
+					}
+					// if minor number is larger than what we support, add a warning to messages
+					String minor = XmlUtils.getTextValue((Element)nl.item(i), TAG_MINOR);
+					if (!"00".equals(minor)) {
+						messages.put(500, Message.newMessage(500, "Version Problem", "XML file has some new data which is not supported by this version of Crash Analyser.", Message.MessageTypes.WARNING));
+						break;
+					}
+				}
+			}
+			
+			// read summary data
+			crashSummary = Summary.read(docEle);
+
+			// read ROM id for this file
+			if (crashSummary != null) {
+				romId = crashSummary.getRomId();
+			}
+			
+			// read event log
+			nl = docEle.getElementsByTagName(TAG_SEG_EVENT_LOG);
+			if (nl != null && nl.getLength() > 0) {
+				eventLog = EventLog.read((Element)nl.item(0));
+			}
+			
+			// read messages from xml
+			nl = docEle.getElementsByTagName(TAG_MESSAGE);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					Message message = Message.read(el);
+					if (message != null)
+						messages.put(message.getId(), message);
+				}
+			}			
+			
+			Map<Integer, CodeSegment> codeSegments = new HashMap<Integer, CodeSegment>();
+			
+			// read code segments
+			nl = docEle.getElementsByTagName(TAG_CODESEG);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					CodeSegment codeSeg = CodeSegment.read(el, messages);
+					if (codeSeg != null)
+						codeSegments.put(codeSeg.getId(), codeSeg);
+				}
+			}
+
+			Map<Integer, Symbol> symbols = new HashMap<Integer, Symbol>();
+
+			// read symbols (Summary file won't have symbols, but this same method
+			// is used by CrashFile)
+			nl = docEle.getElementsByTagName(TAG_SYMBOL_SET);
+			if(nl != null && nl.getLength() > 0) {
+				// go throug all symbol_set tags
+				for(int i = 0 ; i < nl.getLength(); i++) {
+					// get the symbol_set element
+					Element el = (Element)nl.item(i);
+					NodeList childs = el.getChildNodes();
+					// if symbol set has child nodes
+					if (childs != null && childs.getLength() > 0) {
+						String source = "";
+						// go through all symbol_set child nodes
+						for(int k = 0; k < childs.getLength(); k++) {
+							Node child = childs.item(k);
+							// if node is source node
+							if (child.getNodeName() == TAG_SOURCE) {
+								source = child.getFirstChild().getNodeValue();
+							// if node is symbol node
+							} else if (child.getNodeName() == TAG_SYMBOL) {
+								Symbol symbol = Symbol.read((Element)child, source, codeSegments);
+								if (symbol != null)
+									symbols.put(symbol.getId(), symbol);
+							}
+						}
+					}
+				}
+			}
+					
+			registerSets = new HashMap<Integer, RegisterSet>();
+			Map<Integer, Register> allRegisters = new HashMap<Integer, Register>();
+			
+			// read register sets
+			nl = docEle.getElementsByTagName(TAG_REGISTER_SET);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					RegisterSet registerSet = RegisterSet.read(el, symbols, messages);
+					if (registerSet != null) {
+						registerSets.put(registerSet.getId(),registerSet);
+						
+						// read all individual registers from sets
+						List<Register> registers = registerSet.getRegisters();
+						for (int j = 0; j < registers.size(); j++) {
+							Register register = registers.get(j);
+							if (!allRegisters.containsKey(register.getId())) {
+								allRegisters.put(register.getId(), register);
+							}
+						}						
+					}
+				}
+			}
+			
+			stacks = new HashMap<Integer, Stack>();
+			
+			// read stacks
+			nl = docEle.getElementsByTagName(TAG_STACK);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					Stack stack = Stack.read(el, registerSets, allRegisters, symbols);
+					if (stack != null)
+						stacks.put(stack.getId(), stack);
+				}
+			}
+			
+			Map<Integer, Thread> threads = new HashMap<Integer, Thread>();
+			
+			// read threads
+			nl = docEle.getElementsByTagName(TAG_THREAD);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					Thread thread = Thread.read(el, registerSets, symbols, stacks, errorLibrary);
+					if (thread != null) {
+						threads.put(thread.getId(), thread);
+						stacks = thread.removeOwnStacks(stacks);
+						registerSets = thread.removeOwnRegisterSets(registerSets);
+					}
+				}
+			}
+			
+			processes = new HashMap<Integer, Process>();
+			
+			// read processes
+			nl = docEle.getElementsByTagName(TAG_PROCESS);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					Process process = Process.read(el, threads, codeSegments);
+					if (process != null)
+						processes.put(process.getId(), process);
+				}
+			}
+			
+			registerDetails = new ArrayList<RegisterDetails>();
+			
+			// read register details
+			nl = docEle.getElementsByTagName(TAG_VI_ENTRY);
+			if (nl != null && nl.getLength() > 0) {
+				for (int i = 0; i < nl.getLength(); i++) {
+					Element el = (Element)nl.item(i);
+					RegisterDetails details = RegisterDetails.read(el);
+					if (details != null)
+						registerDetails.add(details);
+				}
+			}
+
+			// if xml contained crash date and time, parse them into this.time
+			if (!"".equals(crashSummary.getCrashDate()) && !"".equals(crashSummary.getCrashTime()))
+				time = crashSummary.getCrashDate() + " " + crashSummary.getCrashTime();
+			
+			// set panic data
+			Process process = getCrashedProcess();
+			if (process != null) {
+				Thread firstThread = process.getFirstThread();
+				if (firstThread != null) {
+					panicCategory = firstThread.getExitCategory();
+					panicCode = firstThread.getExitReason();
+					threadName = firstThread.getFullName();
+				}
+			}
+
+			formatDescription();
+		}catch(Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Formats a description for this crash. 
+	 */
+	void formatDescription() {
+		Thread thread = null;
+		
+		// get the first thread of the first process
+		if (processes != null && !processes.isEmpty()) {
+			Process[] processesArray = processes.values().toArray(new Process[processes.values().size()]);
+			Process process = processesArray[0];
+			thread = process.getFirstThread();
+		}
+		
+		description = HtmlFormatter.formatCrashFileDescription(crashSummary, getMessages(), thread);
+		shortDescription = HtmlFormatter.formatCrashFileDescription(crashSummary, null, thread);
+	}
+	
+	public Summary getSummary() {
+		return crashSummary;
+	}
+	
+	/**
+	 * Get crashed process
+	 * 
+	 * @return first process or null
+	 */
+	public Process getCrashedProcess() {
+		Process crashedProcess = null;
+		if (processes != null && !processes.isEmpty()) {
+			Process[] processesArray = processes.values().toArray(new Process[processes.values().size()]);
+			for (Process process : processesArray) {
+				for (Thread thread : process.getThreads()) {
+					String exitType = thread.getExitType();
+					if (exitType.equalsIgnoreCase("Panic") || exitType.equalsIgnoreCase("Exception")) {
+						crashedProcess = process;
+						break;
+					}					
+				}
+			}
+		}
+		return crashedProcess;
+	}
+	
+	public EventLog getEventLog() {
+		return eventLog;
+	}
+	
+	public List<RegisterDetails> getRegisterDetails() {
+		return registerDetails;
+	}
+	
+	public boolean exists() {
+		return true;
+	}
+
+	public ImageDescriptor getImageDescriptor() {
+		return null;
+	}
+
+	public String getName() {
+		return fileName;
+	}
+
+	public IPersistableElement getPersistable() {
+		return null;
+	}
+
+	public String getToolTipText() {
+		return "Partial Crash Analyser File";
+	}
+
+	@SuppressWarnings("unchecked")
+	public Object getAdapter(Class adapter) {
+		return null;
+	}	
+}