comparison graal/com.oracle.max.criutils/src/com/oracle/max/criutils/HexCodeFile.java @ 3733:e233f5660da4

Added Java files from Maxine project.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 17 Dec 2011 19:59:18 +0100
parents
children bc8527f3071c
comparison
equal deleted inserted replaced
3732:3e2e8b8abdaf 3733:e233f5660da4
1 /*
2 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.max.criutils;
24
25 import java.io.*;
26 import java.lang.reflect.*;
27 import java.util.*;
28 import java.util.regex.*;
29
30 import com.sun.cri.ci.*;
31 import com.sun.cri.ci.CiTargetMethod.CodeAnnotation;
32 import com.sun.cri.ci.CiTargetMethod.CodeComment;
33 import com.sun.cri.ci.CiTargetMethod.JumpTable;
34 import com.sun.cri.ci.CiTargetMethod.LookupTable;
35
36
37 /**
38 * A HexCodeFile is a textual format for representing a chunk of machine code along
39 * with extra information that can be used to enhance a disassembly of the code.
40 *
41 * A pseudo grammar for a HexCodeFile is given below.
42 * <pre>
43 * HexCodeFile ::= Platform Delim HexCode Delim (OptionalSection Delim)*
44 *
45 * OptionalSection ::= Comment | OperandComment | JumpTable | LookupTable
46 *
47 * Platform ::= "Platform" ISA WordWidth
48 *
49 * HexCode ::= "HexCode" StartAddress HexDigits
50 *
51 * Comment ::= "Comment" Position String
52 *
53 * OperandComment ::= "OperandComment" Position String
54 *
55 * JumpTable ::= "JumpTable" Position EntrySize Low High
56 *
57 * LookupTable ::= "LookupTable" Position NPairs KeySize OffsetSize
58 *
59 * Position, EntrySize, Low, High, NPairs KeySize OffsetSize ::= int
60 *
61 * Delim := "<||@"
62 * </pre>
63 *
64 * There must be exactly one HexCode and Platform part in a HexCodeFile. The length of HexDigits must be even
65 * as each pair of digits represents a single byte.
66 * <p>
67 * Below is an example of a valid Code input:
68 * <pre>
69 *
70 * Platform AMD64 64 <||@
71 * HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3 <||@
72 * Comment 24 frame-ref-map: +0 {0}
73 * at java.lang.String.toLowerCase(String.java:2496) [bci: 1]
74 * |0
75 * locals: |stack:0:a
76 * stack: |stack:0:a
77 * <||@
78 * OperandComment 24 {java.util.Locale.getDefault()} <||@
79 * Comment 36 frame-ref-map: +0 {0}
80 * at java.lang.String.toLowerCase(String.java:2496) [bci: 4]
81 * |0
82 * locals: |stack:0:a
83 * <||@
84 * OperandComment 36 {java.lang.String.toLowerCase(Locale)} <||@
85 *
86 * </pre>
87 */
88 public class HexCodeFile {
89
90 public static final String NEW_LINE = CiUtil.NEW_LINE;
91 public static final String SECTION_DELIM = " <||@";
92 public static final Pattern SECTION = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL);
93 public static final Pattern COMMENT = Pattern.compile("(\\d+)\\s+(.*)", Pattern.DOTALL);
94 public static final Pattern OPERAND_COMMENT = COMMENT;
95 public static final Pattern JUMP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(-{0,1}\\d+)\\s+(-{0,1}\\d+)\\s*");
96 public static final Pattern LOOKUP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*");
97 public static final Pattern HEX_CODE = Pattern.compile("(\\p{XDigit}+)(?:\\s+(\\p{XDigit}*))?");
98 public static final Pattern PLATFORM = Pattern.compile("(\\S+)\\s+(\\S+)", Pattern.DOTALL);
99
100 /**
101 * Delimiter placed before a HexCodeFile when embedded in a string/stream.
102 */
103 public static final String EMBEDDED_HCF_OPEN = "<<<HexCodeFile";
104
105 /**
106 * Delimiter placed after a HexCodeFile when embedded in a string/stream.
107 */
108 public static final String EMBEDDED_HCF_CLOSE = "HexCodeFile>>>";
109
110 /**
111 * Map from a machine code position to a list of comments for the position.
112 */
113 public final Map<Integer, List<String>> comments = new TreeMap<Integer, List<String>>();
114
115 /**
116 * Map from a machine code position to a comment for the operands of the instruction at the position.
117 */
118 public final Map<Integer, String> operandComments = new TreeMap<Integer, String>();
119
120 public final byte[] code;
121
122 public final ArrayList<JumpTable> jumpTables = new ArrayList<JumpTable>();
123
124 public final ArrayList<LookupTable> lookupTables = new ArrayList<LookupTable>();
125
126 public final String isa;
127
128 public final int wordWidth;
129
130 public final long startAddress;
131
132 public HexCodeFile(byte[] code, long startAddress, String isa, int wordWidth) {
133 this.code = code;
134 this.startAddress = startAddress;
135 this.isa = isa;
136 this.wordWidth = wordWidth;
137 }
138
139 /**
140 * Parses a string in the format produced by {@link #toString()} to produce a {@link HexCodeFile} object.
141 */
142 public static HexCodeFile parse(String source, String sourceName) {
143 return new Parser(source, sourceName).hcf;
144 }
145
146 /**
147 * Formats this HexCodeFile as a string that can be parsed with {@link #parse(String, String)}.
148 */
149 @Override
150 public String toString() {
151 ByteArrayOutputStream baos = new ByteArrayOutputStream();
152 writeTo(baos);
153 return baos.toString();
154 }
155
156 public String toEmbeddedString() {
157 return EMBEDDED_HCF_OPEN + NEW_LINE + toString() + EMBEDDED_HCF_CLOSE;
158 }
159
160 public void writeTo(OutputStream out) {
161 PrintStream ps = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
162 ps.printf("Platform %s %d %s%n", isa, wordWidth, SECTION_DELIM);
163 ps.printf("HexCode %x %s %s%n", startAddress, HexCodeFile.hexCodeString(code), SECTION_DELIM);
164
165 for (JumpTable table : jumpTables) {
166 ps.printf("JumpTable %d %d %d %d %s%n", table.position, table.entrySize, table.low, table.high, SECTION_DELIM);
167 }
168
169 for (LookupTable table : lookupTables) {
170 ps.printf("LookupTable %d %d %d %d %s%n", table.position, table.npairs, table.keySize, table.keySize, SECTION_DELIM);
171 }
172
173 for (Map.Entry<Integer, List<String>> e : comments.entrySet()) {
174 int pos = e.getKey();
175 for (String comment : e.getValue()) {
176 ps.printf("Comment %d %s %s%n", pos, comment, SECTION_DELIM);
177 }
178 }
179
180 for (Map.Entry<Integer, String> e : operandComments.entrySet()) {
181 ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM);
182 }
183 ps.flush();
184 }
185
186
187 /**
188 * Formats a byte array as a string of hex digits.
189 */
190 public static String hexCodeString(byte[] code) {
191 StringBuilder sb = new StringBuilder(code.length * 2);
192 for (int b : code) {
193 String hex = Integer.toHexString(b & 0xff);
194 if (hex.length() == 1) {
195 sb.append('0');
196 }
197 sb.append(hex);
198 }
199 return sb.toString();
200 }
201
202 /**
203 * Adds a comment to the list of comments for a given position.
204 */
205 public void addComment(int pos, String comment) {
206 List<String> list = comments.get(pos);
207 if (list == null) {
208 list = new ArrayList<String>();
209 comments.put(pos, list);
210 }
211 list.add(encodeString(comment));
212 }
213
214 /**
215 * Sets an operand comment for a given position.
216 *
217 * @return the previous operand comment for {@code pos}
218 */
219 public String addOperandComment(int pos, String comment) {
220 return operandComments.put(pos, encodeString(comment));
221 }
222
223 /**
224 * Adds any jump tables, lookup tables or code comments from a list of code annotations.
225 */
226 public static void addAnnotations(HexCodeFile hcf, List<CodeAnnotation> annotations) {
227 if (annotations == null || annotations.isEmpty()) {
228 return;
229 }
230 for (CodeAnnotation a : annotations) {
231 if (a instanceof JumpTable) {
232 JumpTable table = (JumpTable) a;
233 hcf.jumpTables.add(table);
234 } else if (a instanceof LookupTable) {
235 LookupTable table = (LookupTable) a;
236 hcf.lookupTables.add(table);
237 } else if (a instanceof CodeComment) {
238 CodeComment comment = (CodeComment) a;
239 hcf.addComment(comment.position, comment.value);
240 }
241 }
242 }
243
244 /**
245 * Modifies a string to mangle any substrings matching {@link #SECTION_DELIM}.
246 */
247 public static String encodeString(String s) {
248 int index;
249 while ((index = s.indexOf(SECTION_DELIM)) != -1) {
250 s = s.substring(0, index) + " < |@" + s.substring(index + SECTION_DELIM.length());
251 }
252 return s;
253 }
254
255 /**
256 * Helper class to parse a string in the format produced by {@link HexCodeFile#toString()}
257 * and produce a {@link HexCodeFile} object.
258 */
259 static class Parser {
260 private static final Field offsetField = stringField("offset");
261 private static final Field valueField = stringField("value");
262
263 static Field stringField(String name) {
264 try {
265 Field field = String.class.getDeclaredField(name);
266 field.setAccessible(true);
267 return field;
268 } catch (Exception e) {
269 throw new Error("Could not get reflective access to field " + String.class.getName() + "." + name);
270 }
271 }
272
273 final String input;
274 final String inputSource;
275 String isa;
276 int wordWidth;
277 byte[] code;
278 long startAddress;
279 HexCodeFile hcf;
280
281 Parser(String source, String sourceName) {
282 this.input = storage(source);
283 this.inputSource = sourceName;
284 parseSections(source);
285 }
286
287 void makeHCF() {
288 if (hcf == null) {
289 if (isa != null && wordWidth != 0 && code != null) {
290 hcf = new HexCodeFile(code, startAddress, isa, wordWidth);
291 }
292 }
293 }
294
295 void checkHCF(String section, String where) {
296 check(hcf != null, where, section + " section must be after Platform and HexCode section");
297 }
298
299 void check(boolean condition, String where, String message) {
300 if (!condition) {
301 error(where, message);
302 }
303 }
304
305 int offset(String s) {
306 try {
307 return offsetField.getInt(s);
308 } catch (Exception e) {
309 throw new Error("Could not read value of field " + offsetField, e);
310 }
311 }
312
313 /**
314 * Gets a string corresponding to the storage char array for a given string.
315 */
316 String storage(String s) {
317 try {
318 char[] value = (char[]) valueField.get(s);
319 if (offset(s) == 0 && value.length == s.length()) {
320 return s;
321 }
322 return new String(value);
323 } catch (Exception e) {
324 throw new Error("Could not read value of field " + valueField, e);
325 }
326 }
327
328 Error error(String where, String message) {
329 return error(offset(where), message);
330 }
331
332 Error error(int offset, String message) {
333 throw new Error(errorMessage(offset, message));
334 }
335
336 void warning(String where, String message) {
337 System.err.println("Warning: " + errorMessage(offset(where), message));
338 }
339
340 String errorMessage(int offset, String message) {
341 assert offset < input.length();
342 InputPos inputPos = filePos(offset);
343 int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset);
344 int lineStart = offset - inputPos.col;
345 String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd);
346 return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^");
347 }
348
349 static class InputPos {
350 final int line;
351 final int col;
352 public InputPos(int line, int col) {
353 this.line = line;
354 this.col = col;
355 }
356 }
357
358 InputPos filePos(int index) {
359 assert input != null;
360 int line = 1;
361 int lineEnd = 0;
362 int lineStart = 0;
363 while ((lineEnd = input.indexOf(HexCodeFile.NEW_LINE, lineStart)) != -1) {
364 if (lineEnd < index) {
365 line++;
366 lineStart = lineEnd + HexCodeFile.NEW_LINE.length();
367 } else {
368 break;
369 }
370 }
371 return new InputPos(line, index - lineStart);
372 }
373
374 void parseSections(String source) {
375 int index = 0;
376 int endIndex = source.indexOf(SECTION_DELIM);
377 while (endIndex != -1) {
378 String section = source.substring(index, endIndex).trim();
379 parseSection(section);
380 index = endIndex + SECTION_DELIM.length();
381 endIndex = source.indexOf(SECTION_DELIM, index);
382 }
383 }
384
385 int parseInt(String value) {
386 try {
387 return Integer.parseInt(value);
388 } catch (NumberFormatException e) {
389 throw error(value, "Not a valid integer: " + value);
390 }
391 }
392
393 void parseSection(String section) {
394 if (section.isEmpty()) {
395 return;
396 }
397 Matcher m = HexCodeFile.SECTION.matcher(section);
398 check(m.matches(), section, "Section does not match pattern " + HexCodeFile.SECTION);
399
400 String header = m.group(1);
401 String body = m.group(2);
402
403 if (header.equals("Platform")) {
404 check(isa == null, body, "Duplicate Platform section found");
405 m = HexCodeFile.PLATFORM.matcher(body);
406 check(m.matches(), body, "Platform does not match pattern " + HexCodeFile.PLATFORM);
407 isa = m.group(1);
408 wordWidth = parseInt(m.group(2));
409 makeHCF();
410 } else if (header.equals("HexCode")) {
411 check(code == null, body, "Duplicate Code section found");
412 m = HexCodeFile.HEX_CODE.matcher(body);
413 check(m.matches(), body, "Code does not match pattern " + HexCodeFile.HEX_CODE);
414 String hexAddress = m.group(1);
415 startAddress = Long.valueOf(hexAddress, 16);
416 String hexCode = m.group(2);
417 if (hexCode == null) {
418 code = new byte[0];
419 } else {
420 check((hexCode.length() % 2) == 0, body, "Hex code length must be even");
421 code = new byte[hexCode.length() / 2];
422 for (int i = 0; i < code.length; i++) {
423 String hexByte = hexCode.substring(i * 2, (i + 1) * 2);
424 code[i] = (byte) Integer.parseInt(hexByte, 16);
425 }
426 }
427 makeHCF();
428 } else if (header.equals("Comment")) {
429 checkHCF("Comment", header);
430 m = HexCodeFile.COMMENT.matcher(body);
431 check(m.matches(), body, "Comment does not match pattern " + HexCodeFile.COMMENT);
432 int pos = parseInt(m.group(1));
433 String comment = m.group(2);
434 hcf.addComment(pos, comment);
435 } else if (header.equals("OperandComment")) {
436 checkHCF("OperandComment", header);
437 m = HexCodeFile.OPERAND_COMMENT.matcher(body);
438 check(m.matches(), body, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT);
439 int pos = parseInt(m.group(1));
440 String comment = m.group(2);
441 hcf.addOperandComment(pos, comment);
442 } else if (header.equals("JumpTable")) {
443 checkHCF("JumpTable", header);
444 m = HexCodeFile.JUMP_TABLE.matcher(body);
445 check(m.matches(), body, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE);
446 int pos = parseInt(m.group(1));
447 int entrySize = parseInt(m.group(2));
448 int low = parseInt(m.group(3));
449 int high = parseInt(m.group(4));
450 hcf.jumpTables.add(new JumpTable(pos, low, high, entrySize));
451 } else if (header.equals("LookupTable")) {
452 checkHCF("LookupTable", header);
453 m = HexCodeFile.LOOKUP_TABLE.matcher(body);
454 check(m.matches(), body, "LookupTable does not match pattern " + HexCodeFile.LOOKUP_TABLE);
455 int pos = parseInt(m.group(1));
456 int npairs = parseInt(m.group(2));
457 int keySize = parseInt(m.group(3));
458 int offsetSize = parseInt(m.group(4));
459 hcf.lookupTables.add(new LookupTable(pos, npairs, keySize, offsetSize));
460 } else {
461 error(section, "Unknown section header: " + header);
462 }
463 }
464 }
465 }