trace/traceviewer/com.nokia.traceviewer/src/com/nokia/traceviewer/engine/BaseDataReader.java
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 24 Aug 2010 14:01:48 +0300
changeset 16 72f198be1c1d
parent 11 5b9d4d8641ce
permissions -rw-r--r--
Crash Analyser Carbide Extension 1.4.0

/*
 * Copyright (c) 2007-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:
 *
 * Base DataReader class
 *
 */
package com.nokia.traceviewer.engine;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Base DataReader class
 */
public abstract class BaseDataReader extends Thread implements DataReader,
		TraceViewerConst, BTraceConstants {

	/**
	 * Sleeping time
	 */
	protected static final int SLEEP_TIME = 200;

	/**
	 * Pause waiting time
	 */
	protected static final int PAUSE_TIME = 300;

	/**
	 * Trace configuration to be used in this Reader
	 */
	protected TraceConfiguration traceConfiguration;

	/**
	 * Trace properties, passed to MediaCallback
	 */
	protected TraceProperties trace;

	/**
	 * Data source
	 */
	protected FileChannel sourceChannel;

	/**
	 * Buffer for data received from data source
	 */
	protected ByteBuffer receiveBuffer;

	/**
	 * Media processor
	 */
	protected MediaCallback mediaCallback;

	/**
	 * File path from where to read
	 */
	protected String filePath;

	/**
	 * Random access file we use to read the binary
	 */
	protected RandomAccessFile readFile;

	/**
	 * Filemap that hold the trace block positions
	 */
	protected FileMap fileMap;

	/**
	 * Trace count in this file
	 */
	protected int traceCount;

	/**
	 * File start offset
	 */
	protected long fileStartOffset;

	/**
	 * Multipart trace array
	 */
	protected static Map<Integer, MultiPartItem> multiPartTraceArray;

	/**
	 * Constructor
	 */
	public BaseDataReader() {
		fileMap = new FileMap();
	}

	/**
	 * Creates file channel
	 */
	protected void createFileChannel() {
		try {
			if (readFile != null) {
				// Close previous file channel
				readFile.close();
			}
			readFile = new RandomAccessFile(filePath, "r"); //$NON-NLS-1$
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (readFile != null) {
			sourceChannel = readFile.getChannel();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getFilePath()
	 */
	public String getFilePath() {
		return filePath;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.DataReader#setFilePath(java.lang.String)
	 */
	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#setFileStartOffset(long)
	 */
	public void setFileStartOffset(long fileStartOffset) {
		this.fileStartOffset = fileStartOffset;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getFileStartOffset()
	 */
	public long getFileStartOffset() {
		return fileStartOffset;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getFileMap()
	 */
	public FileMap getFileMap() {
		return fileMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#clearFile()
	 */
	public void clearFile() {
		fileMap.clearMap();
		traceCount = 0;
		fileStartOffset = 0;
		if (readFile != null) {
			createFileChannel();

			// Set everything to zero
			setFilePosition(0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getTraceCount()
	 */
	public int getTraceCount() {
		return traceCount;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.nokia.traceviewer.engine.DataReader#createScrollReader(com.nokia.
	 * traceviewer.engine.MediaCallback,
	 * com.nokia.traceviewer.engine.TraceConfiguration)
	 */
	public DataScrollReader createScrollReader(MediaCallback mediaCallback,
			TraceConfiguration conf) {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#getTraceConfiguration()
	 */
	public TraceConfiguration getTraceConfiguration() {
		return traceConfiguration;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.nokia.traceviewer.engine.DataReader#setFilePosition(long)
	 */
	public void setFilePosition(long filePos) {
		try {
			if (readFile != null) {
				readFile.seek(filePos);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Parses BTrace variables
	 * 
	 * @param buf
	 *            buffer where the data is located
	 * @param bTraceHeaderOffset
	 *            BTrace header offset
	 * @param trace
	 *            trace where the variables are inserted
	 */
	protected void parseBTraceVariables(ByteBuffer buf, int bTraceHeaderOffset,
			TraceProperties trace) {
		int variablesFound = 0;

		// Get BTrace header bytes
		byte recordSize = buf.get(bTraceHeaderOffset);
		byte flags = buf.get(bTraceHeaderOffset + 1);
		byte category = buf.get(bTraceHeaderOffset + 2);
		byte subCategory = buf.get(bTraceHeaderOffset + 3);

		// Set BTrace header bytes
		trace.bTraceInformation.setRecordSize(recordSize);
		trace.bTraceInformation.setFlags(flags);
		trace.bTraceInformation.setCategory(category);
		trace.bTraceInformation.setSubCategory(subCategory);

		// Loop through flags
		for (int i = 0; i < FLAGS_LENGTH; i++) {
			if ((flags & (1 << i)) != 0) {

				switch (i) {

				// Header2 present (bit number 0)
				case HEADER2_PRESENT_BIT:
					trace.bTraceInformation.setHeader2Present(true);

					// CPU Id
					int cpuId = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH);
					cpuId = Integer.reverseBytes(cpuId);
					cpuId = (cpuId & CPU_ID_MASK) >> CPU_ID_SHIFT;
					trace.bTraceInformation.setCpuId(cpuId);

					// Multipart trace
					int multipart = 0;
					multipart |= buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH);
					multipart = Integer.reverseBytes(multipart);
					multipart = multipart & MULTIPART_MASK;
					trace.bTraceInformation.setMultiPart(multipart);
					break;

				// Timestamp present (bit number 1)
				case TIMESTAMP_PRESENT_BIT:
					int timestamp = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH
							+ (variablesFound * BTRACE_VARIABLE_LENGTH));

					timestamp = Integer.reverseBytes(timestamp);
					trace.bTraceInformation.setTimestampPresent(true);
					trace.bTraceInformation.setTimestamp(timestamp);
					break;

				// Timestamp2 present (bit number 2)
				case TIMESTAMP2_PRESENT_BIT:
					int timestamp2 = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH
							+ (variablesFound * BTRACE_VARIABLE_LENGTH));

					timestamp2 = Integer.reverseBytes(timestamp2);
					trace.bTraceInformation.setTimestamp2Present(true);
					trace.bTraceInformation.setTimestamp2(timestamp2);
					break;

				// Context ID present (bit number 3)
				case CONTEXT_ID_PRESENT_BIT:
					int contextId = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH
							+ (variablesFound * BTRACE_VARIABLE_LENGTH));

					contextId = Integer.reverseBytes(contextId);
					trace.bTraceInformation.setContextIdPresent(true);
					trace.bTraceInformation.setThreadId(contextId);
					break;

				// Program counter present (bit number 4)
				case PROGRAM_COUNTER_PRESENT_BIT:
					int programCounter = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH
							+ (variablesFound * BTRACE_VARIABLE_LENGTH));

					programCounter = Integer.reverseBytes(programCounter);
					trace.bTraceInformation.setProgramCounterPresent(true);
					trace.bTraceInformation.setProgramCounter(programCounter);
					break;

				// Extra value present (bit number 5)
				case EXTRA_VALUE_PRESENT_BIT:
					int extraValue = buf.getInt(bTraceHeaderOffset
							+ BTRACE_HEADER_LENGTH
							+ (variablesFound * BTRACE_VARIABLE_LENGTH));

					extraValue = Integer.reverseBytes(extraValue);
					trace.bTraceInformation.setExtraValuePresent(true);
					trace.bTraceInformation.setExtraValue(extraValue);
					break;

				// Record truncated (bit number 6)
				case RECORD_TRUNCATED_BIT:
					trace.bTraceInformation.setTruncated(true);
					variablesFound--;
					break;

				// Record missing (bit number 7)
				case RECORD_MISSING_BIT:
					trace.bTraceInformation.setTraceMissing(true);
					variablesFound--;
					break;
				}

				// Add variable count for each found flag
				variablesFound++;
			}
		}

		// Data starts after BTrace header + possible variables
		trace.dataStart = bTraceHeaderOffset + BTRACE_HEADER_LENGTH
				+ (variablesFound * BTRACE_VARIABLE_LENGTH);

		// Get length of the BTrace data
		int btraceDataLength = buf.get(bTraceHeaderOffset) & BYTE_MASK;

		// If length is FF, use the length from header instead
		if (btraceDataLength == BYTE_MASK) {
			trace.dataLength = trace.messageLength
					- (trace.dataStart - trace.messageStart);
		} else {
			trace.dataLength = btraceDataLength
					- ((variablesFound + 1) * BTRACE_VARIABLE_LENGTH);
		}
	}

	/**
	 * Handles multipart trace
	 * 
	 * @param trace
	 *            trace properties
	 * @param multiPartValue
	 *            multi part value
	 * @param bTraceHeaderOffset
	 *            BTrace header offset
	 * @return true if trace should be given to DataProcessors
	 */
	protected boolean handleMultiPart(TraceProperties trace,
			int multiPartValue, int bTraceHeaderOffset) {
		boolean valid = true;
		if (multiPartTraceArray == null) {
			multiPartTraceArray = Collections
					.synchronizedMap(new HashMap<Integer, MultiPartItem>());
		}

		Integer key = Integer.valueOf(trace.bTraceInformation.getExtraValue());
		MultiPartItem item = multiPartTraceArray.get(key);

		// First or middle part
		if (multiPartValue == 1 || multiPartValue == 2) {
			valid = false;

			// Add trace to array
			if (item == null) {
				MultiPartItem newItem = new MultiPartItem();
				newItem.addPart(trace, false);
				multiPartTraceArray.put(key, newItem);

				// Don't add parts if trace is already completed
			} else if (!item.isCompleted()) {
				item.addPart(trace, false);
			}

			// Last part
		} else if (multiPartValue == 3 && item != null) {
			if (!item.isCompleted()) {
				item.addPart(trace, true);
				item.setCompleted(true);
			}

			// Create byte array
			int totalMessageLength = item.getTotalMessageLength();
			byte byteArr[] = new byte[totalMessageLength];

			// First copy the header from the last trace part
			int initialHeaderSize = trace.dataStart - trace.messageStart;
			trace.byteBuffer.position(trace.messageStart);
			trace.byteBuffer.get(byteArr, 0, initialHeaderSize);

			// Then the old data
			Iterator<Integer> headerLenIterator = item
					.getTracePartHeaderSizes().iterator();
			Iterator<byte[]> byteIterator = item.getTraceParts().iterator();
			int startOffset = initialHeaderSize;

			// Copy all the stuff from parts
			while (headerLenIterator.hasNext()) {
				int headerLen = headerLenIterator.next().intValue();
				byte[] partBytes = byteIterator.next();
				System.arraycopy(partBytes, headerLen, byteArr, startOffset,
						partBytes.length - headerLen);
				startOffset += (partBytes.length - headerLen);
			}

			// Set multipart item to the trace
			trace.bTraceInformation.setMultiPartTraceParts(item);

			// Set record size
			int recordSize = totalMessageLength;
			if (recordSize > 0xFF) {
				recordSize = 0xFF;
			}
			bTraceHeaderOffset -= trace.messageStart;
			byteArr[bTraceHeaderOffset] = (byte) recordSize;
			trace.bTraceInformation.setRecordSize((byte) recordSize);

			// Remove multipart info from BTrace header
			int bTraceHeader2Offset = bTraceHeaderOffset + BTRACE_HEADER_LENGTH;
			byteArr[bTraceHeader2Offset] &= ~MULTIPART_MASK;

			// Set protocol specific stuff to multi part trace
			byteArr = setProtocolSpecificStuffToMultiPartTrace(byteArr, trace);

			trace.byteBuffer = ByteBuffer.wrap(byteArr);
		}

		return valid;
	}

	/**
	 * Sets protocol specific stuff to multi part trace
	 * 
	 * @param byteArr
	 *            byte array containing the multi part trace
	 * @param trace
	 *            Trace properties
	 * @return byte array containing the trace after procotol specific stuff is
	 *         added
	 */
	protected abstract byte[] setProtocolSpecificStuffToMultiPartTrace(
			byte[] byteArr, TraceProperties trace);
}