Fixes bug libgcj/8170
[official-gcc.git] / libjava / gnu / gcj / runtime / NameFinder.java
blobb14bbf933273afd69933903cce57b7accfc7986d
1 /* NameFinder.java -- Translates addresses to StackTraceElements.
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of libgcj.
6 This software is copyrighted work licensed under the terms of the
7 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
8 details. */
10 package gnu.gcj.runtime;
12 import gnu.gcj.RawData;
14 import java.lang.StringBuffer;
16 import java.io.BufferedReader;
17 import java.io.BufferedWriter;
18 import java.io.InputStreamReader;
19 import java.io.OutputStreamWriter;
20 import java.io.IOException;
21 import java.io.File;
23 /**
24 * Helper class that translates addresses (represented as longs) to a
25 * StackTraceElement array.
27 * There are a couple of system properties that can be set to manipulate the
28 * result (all default to true):
29 * <li>
30 * <ul><code>gnu.gcj.runtime.NameFinder.demangle</code>
31 * Whether names should be demangled.</ul>
32 * <ul><code>gnu.gcj.runtime.NameFinder.sanitize</code></ul>
33 * Whether calls to initialize exceptions and starting the runtime system
34 * should be removed from the stack trace. Only done when names are
35 * demangled.</ul>
36 * <ul><code>gnu.gcj.runtime.NameFinder.remove_unknown</code>
37 * Whether calls to unknown functions (class and method names are unknown)
38 * should be removed from the stack trace. Only done when the stack is
39 * sanitized.</ul>
40 * <ul><code>gnu.gcj.runtime.NameFinder.remove_internal</code>
41 * Whether runtime internal calls (methods in the internal _Jv_* classes
42 * and functions starting with 'ffi_') should be removed from the stack
43 * trace. Only done when the stack is sanitized.</ul>
44 * <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
45 * Whether an external process (addr2line or addr2name.awk) should be used
46 * as fallback to convert the addresses to function names when the runtime
47 * is unable to do it through <code>dladdr</code>.</ul>
48 * </li>
50 * <code>close()</code> should be called to get rid of all resources.
52 * This class is used from <code>java.lang.VMThrowable</code>.
54 * Currently the <code>lookup(long[])</code> method is not thread safe.
55 * It can easily be made thread safe by synchronizing access to all external
56 * processes when used.
58 * @author Mark Wielaard (mark@klomp.org)
60 public class NameFinder
62 // Set these to false when not needed.
63 private static final boolean demangle
64 = Boolean.valueOf(System.getProperty
65 ("gnu.gcj.runtime.NameFinder.demangle", "true")
66 ).booleanValue();
67 private static final boolean sanitize
68 = Boolean.valueOf(System.getProperty
69 ("gnu.gcj.runtime.NameFinder.sanitize", "true")
70 ).booleanValue();
71 private static final boolean remove_unknown
72 = Boolean.valueOf(System.getProperty
73 ("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
74 ).booleanValue();
76 // The remove_interpreter name is an old 3.3/3.4 (deprecated) synonym.
77 private static final boolean remove_internal
78 = (Boolean.valueOf(System.getProperty
79 ("gnu.gcj.runtime.NameFinder.remove_internal", "true")
80 ).booleanValue()
82 Boolean.valueOf(System.getProperty
83 ("gnu.gcj.runtime.NameFinder.remove_interpreter", "true")
84 ).booleanValue()
87 private static final boolean use_addr2line
88 = Boolean.valueOf(System.getProperty
89 ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
90 ).booleanValue();
92 /**
93 * The name of the currently running executable.
95 private final String executable;
97 /**
98 * Process used for demangling names.
100 private Process cppfilt;
102 private BufferedWriter cppfiltOut;
103 private BufferedReader cppfiltIn;
106 * Process used for translating addresses to function/file names.
108 private Process addr2line;
110 private BufferedWriter addr2lineOut;
111 private BufferedReader addr2lineIn;
114 * Flag set if using addr2name.awk instead of addr2line from binutils.
116 private boolean usingAddr2name = false;
119 * Creates a new NameFinder. Call close to get rid of any resources
120 * created while using the <code>lookup</code> methods.
122 public NameFinder()
124 executable = getExecutable();
125 Runtime runtime = Runtime.getRuntime();
126 if (demangle)
130 String[] exec = new String[] {"c++filt", "-s", "java"};
131 cppfilt = runtime.exec(exec);
132 cppfiltIn = new BufferedReader
133 (new InputStreamReader(cppfilt.getInputStream()));
134 cppfiltOut = new BufferedWriter
135 (new OutputStreamWriter(cppfilt.getOutputStream()));
137 catch (IOException ioe)
139 if (cppfilt != null)
140 cppfilt.destroy();
141 cppfilt = null;
145 if (use_addr2line)
149 String[] exec = new String[] {"addr2line", "-f", "-e", executable};
150 addr2line = runtime.exec(exec);
152 catch (IOException ioe)
156 String[] exec = new String[] {"addr2name.awk", executable};
157 addr2line = runtime.exec(exec);
158 usingAddr2name = true;
160 catch (IOException ioe2) { addr2line = null; }
163 if (addr2line != null)
165 addr2lineIn = new BufferedReader
166 (new InputStreamReader(addr2line.getInputStream()));
167 addr2lineOut = new BufferedWriter
168 (new OutputStreamWriter(addr2line.getOutputStream()));
174 * Returns the name of the currently running process.
176 native private static String getExecutable();
179 * Tries to use dladdr to create the nth StackTraceElement from the given
180 * addresses. Returns null on failure.
182 native private StackTraceElement dladdrLookup(RawData addrs, int n);
185 * Returns the nth element from the stack as a hex encoded String.
187 native private String getAddrAsString(RawData addrs, int n);
190 * Returns the label that is exported for the given method name.
192 native private String getExternalLabel(String name);
195 * If nth element of stack is an interpreted frame, return the
196 * element representing the method being interpreted.
198 native private StackTraceElement lookupInterp(RawData addrs, int n);
201 * Creates the nth StackTraceElement from the given native stacktrace.
203 private StackTraceElement lookup(RawData addrs, int n)
205 StackTraceElement result;
207 result = lookupInterp(addrs, n);
208 if (result == null)
209 result = dladdrLookup(addrs, n);
210 if (result == null)
212 String name = null;
213 String file = null;
215 String hex = getAddrAsString(addrs, n);
217 if (addr2line != null)
221 addr2lineOut.write(hex);
222 addr2lineOut.newLine();
223 addr2lineOut.flush();
224 name = addr2lineIn.readLine();
225 file = addr2lineIn.readLine();
227 // addr2line uses symbolic debugging information instead
228 // of the actually exported labels as addr2name.awk does.
229 // This name might need some modification, depending on
230 // the system, to make it a label like that returned
231 // by addr2name.awk or dladdr.
232 if (! usingAddr2name)
233 if (name != null && ! "??".equals (name))
234 name = getExternalLabel (name);
236 catch (IOException ioe) { addr2line = null; }
239 if (name == null || "??".equals(name))
240 name = hex;
242 result = createStackTraceElement(name, file);
245 return result;
249 * Given an Throwable and a native stacktrace returns an array of
250 * StackTraceElement containing class, method, file and linenumbers.
252 public StackTraceElement[] lookup(Throwable t, StackTrace trace)
254 RawData addrs = trace.stackTraceAddrs();
255 int length = trace.length();
257 StackTraceElement[] elements = new StackTraceElement[length];
258 for (int i=0; i < length; i++)
259 elements[i] = lookup(addrs, i);
261 if (demangle && sanitize)
262 return sanitizeStack(elements, t);
263 else
264 return elements;
269 * Removes calls to initialize exceptions and the runtime system from
270 * the stack trace including stack frames of which nothing usefull is known.
271 * Throw away the top of the stack till we find the constructor(s)
272 * of this Throwable or at least the contructors of java.lang.Throwable
273 * or the actual fillInStackTrace call.
274 * Also throw away from the top everything before and including a runtime
275 * _Jv_Throw call.
277 private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
278 Throwable t)
280 StackTraceElement[] stack;
282 String className = t.getClass().getName();
283 String consName;
284 int lastDot = className.lastIndexOf('.');
285 if (lastDot == -1)
286 consName = className + '(';
287 else
288 consName = className.substring(lastDot + 1) + '(';
290 int unknown = 0;
291 int internal = 0;
292 int last_throw = -1;
293 int length = elements.length;
294 int end = length-1;
295 for (int i = 0; i < length; i++)
297 String CName = elements[i].getClassName();
298 String MName = elements[i].getMethodName();
299 if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
301 (CName != null
302 && (CName.equals(className)
303 || CName.equals("java.lang.Throwable")
304 || CName.equals("java.lang.VMThrowable"))
305 && MName != null
306 && (MName.startsWith(consName)
307 || MName.startsWith("Throwable(")
308 || MName.startsWith("fillInStackTrace("))))
310 last_throw = i;
311 // Reset counting of unknown and internal frames.
312 unknown = 0;
313 internal = 0;
315 else if (remove_unknown && CName == null
316 && (MName == null || MName.startsWith("0x")))
317 unknown++;
318 else if (remove_internal
319 && ((CName == null
320 && MName != null && MName.startsWith("ffi_"))
321 || (CName != null && CName.startsWith("_Jv_"))
322 || (CName == null && MName != null
323 && MName.startsWith("_Jv_"))))
324 internal++;
325 else if (("java.lang.Thread".equals(CName)
326 || "gnu.java.lang.MainThread".equals(CName))
327 && "run()".equals(MName))
329 end = i;
330 break;
333 int begin = last_throw+1;
335 // Now filter out everything at the start and the end that is not part
336 // of the "normal" user program including any elements that are internal
337 // calls or have no usefull information whatsoever.
338 // Unless that means we filter out all info.
339 int nr_elements = end - begin - unknown - internal + 1;
340 if ((begin > 0 || end < length-1 || unknown > 0 || internal > 0)
341 && nr_elements > 0)
343 stack = new StackTraceElement[nr_elements];
344 int pos =0;
345 for (int i=begin; i<=end; i++)
347 String MName = elements[i].getMethodName();
348 String CName = elements[i].getClassName();
349 if (remove_unknown && CName == null
350 && (MName == null || MName.startsWith("0x")))
351 ; // Skip unknown frame
352 else if (remove_internal
353 && ((CName == null
354 && MName != null && MName.startsWith("ffi_"))
355 || (CName != null && CName.startsWith("_Jv_"))
356 || (CName == null && MName != null
357 && MName.startsWith("_Jv_"))))
358 ; // Skip internal runtime frame
359 else
361 // Null Class or Method name in elements are not allowed.
362 if (MName == null || CName == null)
364 MName = MName == null ? "" : MName;
365 CName = CName == null ? "" : CName;
366 stack[pos] = newElement(elements[i].getFileName(),
367 elements[i].getLineNumber(),
368 CName, MName,
369 elements[i].isNativeMethod());
371 else
372 stack[pos] = elements[i];
373 pos++;
377 else
378 stack = elements;
380 return stack;
384 * Native helper method to create a StackTraceElement. Needed to work
385 * around normal Java access restrictions.
387 native static private StackTraceElement newElement(String fileName,
388 int lineNumber,
389 String className,
390 String methName,
391 boolean isNative);
394 * Creates a StackTraceElement given a string and a filename.
395 * Splits the given string into the class and method part.
396 * The string name will be a demangled to a fully qualified java method
397 * string. The string file will be decomposed into a file name and possibly
398 * a line number. The name should never be null, but the file may be if it
399 * is unknown.
401 private StackTraceElement createStackTraceElement(String name, String file)
403 if (!demangle)
404 return newElement(file, -1, null, name, false);
406 String s = demangleName(name);
407 String methodName = s;
408 String className = null;
409 int bracket = s.indexOf('(');
410 if (bracket > 0)
412 int dot = s.lastIndexOf('.', bracket);
413 if (dot > 0)
415 className = s.substring(0, dot);
416 methodName = s.substring(dot+1, s.length());
420 String fileName = file;
421 int line = -1;
422 if (fileName != null)
424 int colon = file.lastIndexOf(':');
425 if (colon > 0)
427 fileName = file.substring(0, colon);
430 line = Integer.parseInt(file.substring(colon+1, file.length()));
432 catch (NumberFormatException nfe) { /* ignore */ }
435 if (line == 0)
436 line =-1;
438 if ("".equals(fileName) || "??".equals(fileName))
439 fileName = null;
440 else if (fileName != null)
444 fileName = new File(fileName).getCanonicalPath();
446 catch (IOException ioe) { /* ignore */ }
450 return newElement(fileName, line, className, methodName, false);
454 * Demangles the given String if possible. Returns the demangled String or
455 * the original string if demangling is impossible.
457 private String demangleName(String s)
459 if (cppfilt != null)
463 cppfiltOut.write(s);
464 cppfiltOut.newLine();
465 cppfiltOut.flush();
466 return cppfiltIn.readLine();
468 catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
471 return s;
475 * Returns human readable method name and aguments given a method type
476 * signature as known to the interpreter and a classname.
478 public static String demangleInterpreterMethod(String m, String cn)
480 int index = 0;
481 int length = m.length();
482 StringBuffer sb = new StringBuffer(length);
484 // Figure out the real method name
485 if (m.startsWith("<init>"))
487 String className;
488 int i = cn.lastIndexOf('.');
489 if (i < 0)
490 className = cn;
491 else
492 className = cn.substring(i + 1);
493 sb.append(className);
494 index += 7;
496 else
498 int i = m.indexOf('(');
499 if (i > 0)
501 sb.append(m.substring(0,i));
502 index += i + 1;
506 sb.append('(');
508 // Demangle the type arguments
509 int arrayDepth = 0;
510 char c = (index < length) ? m.charAt(index) : ')';
511 while (c != ')')
513 String type;
514 switch(c)
516 case 'B':
517 type = "byte";
518 break;
519 case 'C':
520 type = "char";
521 break;
522 case 'D':
523 type = "double";
524 break;
525 case 'F':
526 type = "float";
527 break;
528 case 'I':
529 type = "int";
530 break;
531 case 'J':
532 type = "long";
533 break;
534 case 'S':
535 type = "short";
536 break;
537 case 'Z':
538 type = "boolean";
539 break;
540 case 'L':
541 int i = m.indexOf(';', index);
542 if (i > 0)
544 type = m.substring(index+1, i);
545 index = i;
547 else
548 type = "<unknown ref>";
549 break;
550 case '[':
551 type = "";
552 arrayDepth++;
553 break;
554 default:
555 type = "<unknown " + c + '>';
557 sb.append(type);
559 // Handle arrays
560 if (c != '[' && arrayDepth > 0)
561 while (arrayDepth > 0)
563 sb.append("[]");
564 arrayDepth--;
567 index++;
568 char nc = (index < length) ? m.charAt(index) : ')';
569 if (c != '[' && nc != ')')
570 sb.append(", ");
571 c = nc;
574 // Stop. We are not interested in the return type.
575 sb.append(')');
576 return sb.toString();
580 * Releases all resources used by this NameFinder.
582 public void close()
584 if (cppfilt != null)
585 cppfilt.destroy();
587 if (addr2line != null)
588 addr2line.destroy();
592 * Calls close to get rid of all resources.
594 protected void finalize()
596 close();