Commit | Line | Data |
---|---|---|
5da83da5 | 1 | /******************************************************************************* |
318c06c9 | 2 | * Copyright (c) 2013, 2015 Ericsson |
5da83da5 AM |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are | |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * Alexandre Montplaisir - Initial API and implementation | |
41d9ce5b | 11 | * Marc-Andre Laperle - Map from binary file |
5da83da5 AM |
12 | *******************************************************************************/ |
13 | ||
318c06c9 | 14 | package org.eclipse.tracecompass.internal.tmf.core.callstack; |
5da83da5 AM |
15 | |
16 | import java.io.BufferedReader; | |
17 | import java.io.File; | |
41d9ce5b | 18 | import java.io.FileInputStream; |
5da83da5 AM |
19 | import java.io.FileNotFoundException; |
20 | import java.io.FileReader; | |
21 | import java.io.IOException; | |
41d9ce5b MAL |
22 | import java.io.InputStream; |
23 | import java.util.ArrayList; | |
5da83da5 | 24 | import java.util.HashMap; |
41d9ce5b | 25 | import java.util.List; |
5da83da5 | 26 | import java.util.Map; |
25af0e4c | 27 | import java.util.regex.Pattern; |
5da83da5 | 28 | |
41d9ce5b MAL |
29 | import org.eclipse.cdt.core.CCorePlugin; |
30 | import org.eclipse.cdt.core.IBinaryParser; | |
31 | import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; | |
32 | import org.eclipse.cdt.core.IBinaryParser.ISymbol; | |
33 | import org.eclipse.core.runtime.IConfigurationElement; | |
34 | import org.eclipse.core.runtime.IPath; | |
35 | import org.eclipse.core.runtime.ISafeRunnable; | |
36 | import org.eclipse.core.runtime.Path; | |
37 | import org.eclipse.core.runtime.Platform; | |
38 | import org.eclipse.core.runtime.SafeRunner; | |
5da83da5 | 39 | import org.eclipse.jdt.annotation.Nullable; |
318c06c9 | 40 | import org.eclipse.tracecompass.internal.tmf.core.Activator; |
5da83da5 | 41 | |
25af0e4c AM |
42 | import com.google.common.collect.ImmutableMap; |
43 | ||
5da83da5 AM |
44 | /** |
45 | * Class containing the different methods to import an address->name mapping. | |
46 | * | |
47 | * @author Alexandre Montplaisir | |
48 | */ | |
25af0e4c AM |
49 | public final class FunctionNameMapper { |
50 | ||
51 | private FunctionNameMapper() {} | |
52 | ||
53 | private static final Pattern REMOVE_ZEROS_PATTERN = Pattern.compile("^0+(?!$)"); //$NON-NLS-1$ | |
5da83da5 | 54 | |
318c06c9 AM |
55 | /** |
56 | * Get the function name mapping from a text file obtained by doing | |
57 | * | |
58 | * <pre> | |
59 | * nm[--demangle][binary] > file.txt | |
60 | * </pre> | |
61 | * | |
62 | * @param mappingFile | |
63 | * The file to import | |
64 | * @return A map<address, function name> of the results | |
65 | */ | |
5da83da5 | 66 | public static @Nullable Map<String, String> mapFromNmTextFile(File mappingFile) { |
507b1336 | 67 | Map<String, String> map = new HashMap<>(); |
5da83da5 | 68 | |
507b1336 AM |
69 | try (FileReader fr = new FileReader(mappingFile); |
70 | BufferedReader reader = new BufferedReader(fr);) { | |
5da83da5 | 71 | for (String line = reader.readLine(); line != null; line = reader.readLine()) { |
e5ecc9ac | 72 | line = line.trim(); |
5da83da5 | 73 | /* Only lines with 3 elements contain addresses */ |
a9b1f367 | 74 | String[] elems = line.split(" ", 3); //$NON-NLS-1$ |
5da83da5 | 75 | if (elems.length == 3) { |
318c06c9 | 76 | String address = stripLeadingZeros(elems[0]); |
5da83da5 AM |
77 | String name = elems[elems.length - 1]; |
78 | map.put(address, name); | |
79 | } | |
80 | } | |
507b1336 AM |
81 | } catch (FileNotFoundException e) { |
82 | return null; | |
5da83da5 AM |
83 | } catch (IOException e) { |
84 | /* Stop reading the file at this point */ | |
5da83da5 AM |
85 | } |
86 | ||
87 | if (map.isEmpty()) { | |
88 | return null; | |
89 | } | |
25af0e4c | 90 | return ImmutableMap.copyOf(map); |
41d9ce5b MAL |
91 | } |
92 | ||
318c06c9 AM |
93 | /** |
94 | * Get the function name mapping from an executable binary. | |
95 | * | |
96 | * @param file | |
97 | * The file to import | |
98 | * @return A map<address, function name> of the results | |
99 | */ | |
41d9ce5b MAL |
100 | public static @Nullable Map<String, String> mapFromBinaryFile(File file) { |
101 | Map<String, String> map = new HashMap<>(); | |
102 | IBinaryParser.IBinaryObject binaryObject = getBinaryObject(file); | |
103 | if (binaryObject != null) { | |
104 | ISymbol[] symbols = binaryObject.getSymbols(); | |
105 | for (ISymbol symbol : symbols) { | |
106 | String address = symbol.getAddress().toHexAddressString(); | |
107 | /* Remove "0x" */ | |
108 | address = address.substring(2); | |
109 | /* Strip the leading zeroes from the address */ | |
110 | address = stripLeadingZeros(address); | |
111 | map.put(address, symbol.getName()); | |
112 | } | |
113 | } | |
114 | ||
25af0e4c AM |
115 | return ImmutableMap.copyOf(map); |
116 | } | |
117 | ||
118 | ||
119 | /** | |
120 | * Strip the leading zeroes from the address | |
121 | * */ | |
122 | private static String stripLeadingZeros(String address) { | |
123 | return REMOVE_ZEROS_PATTERN.matcher(address).replaceFirst(""); //$NON-NLS-1$ | |
41d9ce5b MAL |
124 | } |
125 | ||
126 | private static @Nullable IBinaryParser.IBinaryObject getBinaryObject(File file) { | |
127 | IPath filePath = new Path(file.toString()); | |
128 | ||
129 | /* Get all the available binary parsers */ | |
130 | final List<IBinaryParser> binaryParsers = new ArrayList<>(); | |
131 | IConfigurationElement[] elements = Platform.getExtensionRegistry() | |
132 | .getConfigurationElementsFor(CCorePlugin.BINARY_PARSER_UNIQ_ID); | |
133 | for (IConfigurationElement element : elements) { | |
134 | IConfigurationElement[] children = element.getChildren("run"); //$NON-NLS-1$ | |
135 | for (final IConfigurationElement run : children) { | |
136 | SafeRunner.run(new ISafeRunnable() { | |
137 | @Override | |
138 | public void run() throws Exception { | |
139 | IBinaryParser binaryParser = (IBinaryParser) run.createExecutableExtension("class"); //$NON-NLS-1$ | |
140 | binaryParsers.add(binaryParser); | |
141 | } | |
142 | ||
143 | @Override | |
144 | public void handleException(Throwable exception) { | |
318c06c9 | 145 | Activator.logError("Error creating binary parser", exception); //$NON-NLS-1$ |
41d9ce5b MAL |
146 | } |
147 | }); | |
148 | } | |
149 | } | |
150 | ||
151 | /* Find the maximum "hint" buffer size we'll need from all the parsers */ | |
152 | int hintBufferSize = 0; | |
153 | for (IBinaryParser parser : binaryParsers) { | |
154 | if (parser.getHintBufferSize() > hintBufferSize) { | |
155 | hintBufferSize = Math.max(hintBufferSize, parser.getHintBufferSize()); | |
156 | } | |
157 | } | |
158 | ||
159 | /* Read the initial "hint" bytes */ | |
160 | byte[] hintBuffer = new byte[hintBufferSize]; | |
161 | if (hintBufferSize > 0) { | |
162 | try (InputStream is = new FileInputStream(file) ){ | |
163 | ||
164 | int count = 0; | |
165 | // Make sure we read up to 'hints' bytes if we possibly can | |
166 | while (count < hintBufferSize) { | |
167 | int bytesRead = is.read(hintBuffer, count, hintBufferSize - count); | |
168 | if (bytesRead < 0) { | |
169 | break; | |
170 | } | |
171 | count += bytesRead; | |
172 | } | |
173 | if (count > 0 && count < hintBuffer.length) { | |
174 | byte[] array = new byte[count]; | |
175 | System.arraycopy(hintBuffer, 0, array, 0, count); | |
176 | hintBuffer = array; | |
177 | } | |
178 | } catch (IOException e) { | |
318c06c9 | 179 | Activator.logError("Error reading initial bytes of binary file", e); //$NON-NLS-1$ |
41d9ce5b MAL |
180 | return null; |
181 | } | |
182 | } | |
183 | ||
184 | /* For all binary parsers, try to get a binary object */ | |
185 | for (IBinaryParser parser : binaryParsers) { | |
186 | if (parser.isBinary(hintBuffer, filePath)) { | |
187 | IBinaryFile binFile; | |
188 | try { | |
189 | binFile = parser.getBinary(hintBuffer, filePath); | |
bba6b570 | 190 | if (binFile instanceof IBinaryParser.IBinaryObject) { |
41d9ce5b MAL |
191 | return (IBinaryParser.IBinaryObject)binFile; |
192 | } | |
193 | } catch (IOException e) { | |
318c06c9 | 194 | Activator.logError("Error parsing binary file", e); //$NON-NLS-1$ |
41d9ce5b MAL |
195 | } |
196 | } | |
197 | } | |
198 | ||
199 | return null; | |
200 | } | |
201 | ||
5da83da5 | 202 | } |