sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.instr/src/com/nokia/carbide/cpp/pi/instr/MapFile.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.instr/src/com/nokia/carbide/cpp/pi/instr/MapFile.java Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "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.carbide.cpp.pi.instr;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.nokia.carbide.cpp.internal.pi.model.Function;
+import com.nokia.carbide.cpp.pi.util.GeneralMessages;
+
+
+public class MapFile
+{
+ private boolean parsedMapFile = false;
+
+ private String name;
+ private File mapFile;
+ private String referencePath;
+ private long referenceLineNumber;
+ private LinkedList<Function> functionData;
+ private ArrayList<Function> sortedFunctionData;
+ private Long currentGccLibEndingOffset = new Long(0);
+ private Function lastGccFunction = null;
+
+ // RVCT/RVDS map file line
+ private static final Pattern rvctLinePattern = Pattern.compile("\\p{Blank}*((?!\\d)\\S.+)\\p{Blank}+(0[x|X]\\p{XDigit}+|\\d+)\\p{Blank}+(?:ARM Code|Thumb Code)\\p{Blank}+(0x\\p{XDigit}+|\\d+)\\p{Blank}*.*"); //$NON-NLS-1$
+
+ // a GCC map file line looks like this:
+ // <%x|%d> <symbol name> for function symbols
+ // symbol name cannot be number (e.g. first non space is not a digit)
+ private static final Pattern gccFuncLinePattern = Pattern.compile("\\p{Blank}*(0[x|X]\\p{XDigit}+|\\d+)\\p{Blank}+((?!\\d)\\S.+)"); //$NON-NLS-1$
+
+ // <section> <%x|%d> <%x|%d> <symbol name> for whole library
+ // *fill* <%x|%d> <%x|%d> 00000000 for filler
+ private static final Pattern gccLibOrFillerLinePattern = Pattern.compile("\\p{Blank}*(?:\\S*)\\p{Blank}*(0[x|X]\\p{XDigit}+|\\d+)\\p{Blank}+(0[x|X]\\p{XDigit}+|\\d+)\\p{Blank}+(\\S.+)"); //$NON-NLS-1$
+
+
+ public MapFile(File file, String referencePath, long referenceLineNumber)
+ {
+ if (!file.exists())
+ {
+ // .map not found
+ if(file.getName().endsWith(".exe.map") || //$NON-NLS-1$
+ file.getName().endsWith(".dll.map") || //$NON-NLS-1$
+ file.getName().endsWith(".ldd.map") || //$NON-NLS-1$
+ file.getName().endsWith(".pdd.map") || //$NON-NLS-1$
+ file.getName().endsWith(".app.map")) //$NON-NLS-1$
+ {
+ flagFileNotFound(file, referencePath, referenceLineNumber);
+ }
+ }
+ //System.out.println("Creating MAP file "+file.getAbsolutePath());
+
+ this.name = file.getName();
+ this.functionData = new LinkedList<Function>();
+ this.sortedFunctionData = new ArrayList<Function>();
+
+ this.mapFile = file;
+ this.referencePath = referencePath;
+ this.referenceLineNumber = referenceLineNumber;
+
+ /*
+ if (file.getName().indexOf("wserv.exe") != -1)
+ {
+ this.printSortedFunctions();
+ System.out.println("Map file "+file.getName()+" processed");
+ }*/
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public String getFunctionNameForOffset(long offset)
+ {
+ if (!this.parsedMapFile) {
+ this.parseMapFile();
+ this.copyToSortedFunctionData();
+ }
+
+ for (Function f : sortedFunctionData)
+ {
+ // is the asked offset within the area of this function
+ // test first is the offset equal or past the first
+ // instruction of the function
+ if (offset >= f.offsetFromBinaryStart)
+ {
+ // if so, make sure that the offset is less
+ // or equal to the last instruction within
+ // this function
+ if (offset < (f.offsetFromBinaryStart+f.length))
+ {
+ return f.functionName;
+ }
+ }
+ }
+
+ return Messages.getString("MapFile.functionWithOffsetNotFound1")+offset+Messages.getString("MapFile.functionWithOffsetNotFound2")+this.name+ //$NON-NLS-1$ //$NON-NLS-2$
+ Messages.getString("MapFile.functionWithOffsetNotFound3")+ //$NON-NLS-1$
+ (this.sortedFunctionData.get(this.sortedFunctionData.size() - 1)).offsetFromBinaryStart; //$NON-NLS-1$
+ }
+
+ public Function getFunctionForOffset(long offset)
+ {
+ if (!this.parsedMapFile) {
+ this.parseMapFile();
+ this.copyToSortedFunctionData();
+ }
+
+ for (Function f : sortedFunctionData)
+ {
+ // is the asked offset within the area of this function
+ // test first is the offset equal or past the first
+ // instruction of the function
+ if (offset >= f.offsetFromBinaryStart)
+ {
+ // if so, make sure that the offset is less
+ // or equal to the last instruction within
+ // this function
+ if (offset < (f.offsetFromBinaryStart+f.length))
+ {
+ return f;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public long getFunctionLengthForOffset(long offset)
+ {
+ if (!this.parsedMapFile) {
+ this.parseMapFile();
+ this.copyToSortedFunctionData();
+ }
+
+ for (Function f : sortedFunctionData)
+ {
+ // is the asked offset within the area of this function
+ // test first is the offset equal or past the first
+ // instruction of the function
+ if (offset >= f.offsetFromBinaryStart)
+ {
+ // if so, make sure that the offset is less
+ // or equal to the last instruction within
+ // this function
+ if (offset < (f.offsetFromBinaryStart+f.length))
+ {
+ return f.length;
+ }
+ }
+ }
+ return 0;
+ }
+
+ public long getOffsetFromBinaryStartForFunction(String functionName)
+ {
+ if (!this.parsedMapFile) {
+ this.parseMapFile();
+ this.copyToSortedFunctionData();
+ }
+
+ for (Function f : sortedFunctionData)
+ {
+ if (f.functionName.equals(functionName))
+ {
+ return f.offsetFromBinaryStart;
+ }
+ }
+ return -1;
+ }
+
+ private Function FunctionFromTokens(String funcNameToken, String funcOffsetToken, String funcLengthToken)
+ {
+ Function f = new Function(funcNameToken,new Long(0),null);
+ // look for length, set it tentatively
+ // we may adjust it later
+ f.length = Long.decode(funcLengthToken);
+ f.offsetFromBinaryStart = Long.decode(funcOffsetToken);
+
+ return f;
+ }
+
+ private void parseMapFile()
+ {
+ boolean isRVCT = false;
+
+ System.out.println(Messages.getString("MapFile.parsingMapFile") + this.name); //$NON-NLS-1$
+ this.parsedMapFile = true;
+
+ // read into the map and see if it's RVCT built
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(mapFile);
+ BufferedReader bufReader = new BufferedReader(new InputStreamReader(fis));
+
+ String line = bufReader.readLine();
+
+ if (line != null && line.length() > 0) {
+ if (line.toLowerCase().indexOf("arm linker,") != -1) //$NON-NLS-1$
+ isRVCT = true;
+ else
+ isRVCT = false;
+ } else {
+ // empty file, do nothing
+ return;
+ }
+
+ bufReader.close();
+ fis.close();
+
+ fis = new FileInputStream(mapFile);
+ bufReader = new BufferedReader(new InputStreamReader(fis));
+
+ if (isRVCT || mapFile.getAbsolutePath().toLowerCase().indexOf("armv5") != -1) //$NON-NLS-1$
+ {
+ parseRVCT(bufReader);
+ }
+ else
+ {
+ parseGCC(bufReader);
+ }
+
+ bufReader.close();
+ fis.close();
+ } catch (FileNotFoundException e) {
+ flagFileNotFound(mapFile, referencePath, referenceLineNumber);
+ } catch (IOException e) {
+ flagIOException(mapFile, referencePath, referenceLineNumber);
+ }
+ }
+
+ private void parseGCC(BufferedReader bufReader) throws IOException
+ {
+ String line = bufReader.readLine();
+
+ while(line != null)
+ {
+ this.processLineGCC(line);
+ line = bufReader.readLine();
+ }
+ }
+
+ private void parseRVCT(BufferedReader bufReader) throws IOException
+ {
+ String line = bufReader.readLine();
+
+ // find the global symbols section
+ //while(line.indexOf("Global Symbols") == -1)
+ // line = bufReader.readLine();
+
+ line = bufReader.readLine();
+
+ while(line != null)
+ {
+ this.processLineRVCT(line);
+ line = bufReader.readLine();
+ }
+ bufReader.close();
+ }
+
+ private void processLineRVCT(String line)
+ {
+ // a RVCT symbol line looks like this:
+ // <symbol name> <%x|%d> <ARM Code|Thumb Code> <%x|%d> <object>
+ // symbol name cannot be number (e.g. first non space is not a digit)
+ Matcher rvctLineMatcher = rvctLinePattern.matcher(line);
+
+ if (rvctLineMatcher.matches())
+ {
+ String funcNameToken = rvctLineMatcher.group(1).trim();
+ // internal symbol, not a function
+ if (funcNameToken.indexOf(Messages.getString("MapFile.dollarSign")) != -1) //$NON-NLS-1$
+ return;
+
+ String funcOffsetToken = rvctLineMatcher.group(2).trim();
+
+ if (funcOffsetToken.equalsIgnoreCase("0x00000001") && line.contains("Thumb Code"))
+ return;
+
+ String funcLengthToken = rvctLineMatcher.group(3).trim();
+
+ Function f = FunctionFromTokens(funcNameToken, funcOffsetToken, funcLengthToken);
+
+ this.insertToFunctionData(f);
+ }
+ }
+
+ // our current parser picked up too many trash, let's try our best here
+ private boolean qualifyGCCSymbol(long address, String symbol) {
+ if (symbol == null || symbol.length() <= 0)
+ return false;
+
+ // zero address on ARM, you must be kidding. This isn't a real program address
+ if (address <= 0) {
+ return false;
+ }
+ if (symbol.contains("(size before relaxing")) { //$NON-NLS-1$
+ return false;
+ }
+ if (symbol.contains("PROVIDE (")) { //$NON-NLS-1$
+ return false;
+ }
+ // you better be kidding if this is a C symbol, it's linker symbol
+ if (symbol.charAt(0) == '.') {
+ return false;
+ }
+ if (symbol.equals("_DYNAMIC")) { //$NON-NLS-1$
+ return false;
+ }
+ if (symbol.contains("vtable ") || symbol.contains("typeinfo ")) { //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ }
+ if (symbol.contains("= .")) { //$NON-NLS-1$
+ return false;
+ }
+ return true;
+ }
+
+ private void processLineGCC(String line)
+ {
+ // a GCC symbol line looks like this:
+ // <%x|%d> <symbol name> for function symbols
+ // symbol name cannot be number (e.g. first non space is not a digit)
+ Matcher gccFuncLineMatcher = gccFuncLinePattern.matcher(line); //$NON-NLS-1$
+ // <section> <%x|%d> <%x|%d> <symbol name> for whole library
+ // *fill* <%x|%d> <%x|%d> 00000000 for filler
+ Matcher gccLibOrFillerLineMatcher = gccLibOrFillerLinePattern.matcher(line); //$NON-NLS-1$
+
+ Function f = null;
+ Long currentLineOffset = currentGccLibEndingOffset;
+
+
+ if (gccFuncLineMatcher.matches())
+ {
+ String funcNameToken = gccFuncLineMatcher.group(2).trim();
+ String funcOffsetToken = gccFuncLineMatcher.group(1).trim();
+ String funcLengthToken = Messages.getString("MapFile.zero"); //$NON-NLS-1$
+
+ f = FunctionFromTokens(funcNameToken, funcOffsetToken, funcLengthToken);
+
+ // Some GCC symbol may be bogus
+ if (qualifyGCCSymbol(f.offsetFromBinaryStart, funcNameToken)) {
+ this.insertToFunctionData(f);
+ }
+
+ if (lastGccFunction != null){
+ // calculate size of last function with offset from current line
+ if (f.offsetFromBinaryStart > lastGccFunction.offsetFromBinaryStart &&
+ f.offsetFromBinaryStart < currentGccLibEndingOffset) {
+ currentLineOffset = f.offsetFromBinaryStart;
+ }
+ }
+
+ } else if (gccLibOrFillerLineMatcher.matches()) {
+ String libOffsetToken = gccLibOrFillerLineMatcher.group(1).trim();
+ String libLengthToken = gccLibOrFillerLineMatcher.group(2).trim();
+ // next time around we will use the new library offset
+ currentGccLibEndingOffset = Long.decode(libLengthToken) + Long.decode(libOffsetToken);
+ } else {
+ // next time around we will use the new library offset
+ currentGccLibEndingOffset = new Long(0);
+ }
+
+ // update last function's size if needed
+ if (lastGccFunction != null)
+ {
+ if (currentLineOffset > lastGccFunction.offsetFromBinaryStart) {
+ lastGccFunction.length = currentLineOffset - lastGccFunction.offsetFromBinaryStart;
+ }
+ }
+
+ // track function on this line as last function, or null if this line is not a function
+ lastGccFunction = f;
+
+ }
+
+ private void insertToFunctionData(Function function)
+ {
+ if (functionData.size() == 0)
+ {
+ functionData.addFirst(function);
+ }
+ else if ((functionData.getFirst()).offsetFromBinaryStart
+ < function.offsetFromBinaryStart)
+ {
+ functionData.addFirst(function);
+ }
+ else if ((functionData.getLast()).offsetFromBinaryStart
+ > function.offsetFromBinaryStart)
+ {
+ functionData.addLast(function);
+ }
+ else
+ {
+ for (int i=0;i<functionData.size();i++)
+ {
+ if ((functionData.get(i)).offsetFromBinaryStart
+ < function.offsetFromBinaryStart)
+ {
+ functionData.add(i,function);
+ break;
+ }
+ }
+ }
+ }
+
+ private void copyToSortedFunctionData()
+ {
+ if (this.functionData.size() <= 0) {
+ return;
+ }
+ this.sortedFunctionData.clear();
+ long start = (this.functionData.getLast()).offsetFromBinaryStart;
+ Function previous = null;
+ boolean reallyAdded = false;
+
+ for (int i=this.functionData.size()-1;i>=0;i--)
+ {
+ //System.out.println(i);
+ Function f = this.functionData.get(i);
+ f.offsetFromBinaryStart = f.offsetFromBinaryStart - start;
+
+ if (this.sortedFunctionData.size() == 0)
+ {
+ // add the function if the vector is empty
+ this.sortedFunctionData.add(f);
+ reallyAdded = true;
+ }
+ else if ( (this.sortedFunctionData.get(this.sortedFunctionData.size()-1)).offsetFromBinaryStart != f.offsetFromBinaryStart)
+ {
+ // add the function if the offset is not the same as with the previous line
+ this.sortedFunctionData.add(f);
+ reallyAdded = true;
+ }
+ else if ( (this.sortedFunctionData.get(this.sortedFunctionData.size()-1)).functionName.startsWith("_")) //$NON-NLS-1$
+ {
+ // if there is a key with this offset, discard the previous with prefix "_"
+ this.sortedFunctionData.remove(this.sortedFunctionData.get(this.sortedFunctionData.size()-1));
+ // add the new function with the same key
+ this.sortedFunctionData.add(f);
+ reallyAdded = true;
+ }
+
+ // do this only if we really added the function to the sorted list
+ if (reallyAdded == true)
+ {
+ // store the length of the previous function
+ if (previous != null)
+ previous.length = f.offsetFromBinaryStart - previous.offsetFromBinaryStart;
+ previous = f;
+ reallyAdded = false;
+ }
+
+ }
+ this.functionData.clear();
+ this.functionData = null;
+ }
+
+ /* internal test function: comment out so code coverage looks
+ * good quantitatively
+ private void printSortedFunctions()
+ {
+ long totalLength = 0;
+ for (Function f : sortedFunctionData)
+ {
+ System.out.println( f.offsetFromBinaryStart+Messages.getString("MapFile.openParenthesis")+totalLength+Messages.getString("MapFile.closeParenthesis")+Long.toHexString(f.length)+Messages.getString("MapFile.dashDash")+f.functionName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ totalLength += f.length;
+ }
+ }
+ */
+
+ private void flagFileNotFound(File file, String referencePath, long referenceLineNumber) {
+ String myMessage = Messages.getString("MapFile.map.file") + file.getAbsoluteFile().getName() + Messages.getString("MapFile.not.found"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (referencePath != null && referencePath.length() > 0) {
+ myMessage += Messages.getString("MapFile.referenced.by") + referencePath; //$NON-NLS-1$
+ }
+ if (referenceLineNumber > 0) {
+ myMessage += Messages.getString("MapFile.line.number") + referenceLineNumber; //$NON-NLS-1$
+ }
+
+ GeneralMessages.PiLog(myMessage, GeneralMessages.ERROR);
+ }
+
+ private void flagIOException(File file, String referencePath, long referenceLineNumber) {
+ String myMessage = Messages.getString("MapFile.map.file") + file.getAbsoluteFile().getName() + Messages.getString("MapFile.ioexception"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (referencePath != null && referencePath.length() > 0) {
+ myMessage += Messages.getString("MapFile.referenced.by") + referencePath; //$NON-NLS-1$
+ }
+ if (referenceLineNumber > 0) {
+ myMessage += Messages.getString("MapFile.line.number") + referenceLineNumber; //$NON-NLS-1$
+ }
+
+ GeneralMessages.showErrorMessage(myMessage);
+ GeneralMessages.PiLog(myMessage, GeneralMessages.ERROR);
+ }
+}