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
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
;
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):
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
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
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>
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")
67 private static final boolean sanitize
68 = Boolean
.valueOf(System
.getProperty
69 ("gnu.gcj.runtime.NameFinder.sanitize", "true")
71 private static final boolean remove_unknown
72 = Boolean
.valueOf(System
.getProperty
73 ("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
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")
82 Boolean
.valueOf(System
.getProperty
83 ("gnu.gcj.runtime.NameFinder.remove_interpreter", "true")
87 private static final boolean use_addr2line
88 = Boolean
.valueOf(System
.getProperty
89 ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
93 * The name of the currently running executable.
95 private final String executable
;
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.
124 executable
= getExecutable();
125 Runtime runtime
= Runtime
.getRuntime();
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
)
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
);
209 result
= dladdrLookup(addrs
, n
);
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
))
242 result
= createStackTraceElement(name
, file
);
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
);
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
277 private static StackTraceElement
[] sanitizeStack(StackTraceElement
[] elements
,
280 StackTraceElement
[] stack
;
282 String className
= t
.getClass().getName();
284 int lastDot
= className
.lastIndexOf('.');
286 consName
= className
+ '(';
288 consName
= className
.substring(lastDot
+ 1) + '(';
293 int length
= elements
.length
;
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"))
302 && (CName
.equals(className
)
303 || CName
.equals("java.lang.Throwable")
304 || CName
.equals("java.lang.VMThrowable"))
306 && (MName
.startsWith(consName
)
307 || MName
.startsWith("Throwable(")
308 || MName
.startsWith("fillInStackTrace("))))
311 // Reset counting of unknown and internal frames.
315 else if (remove_unknown
&& CName
== null
316 && (MName
== null || MName
.startsWith("0x")))
318 else if (remove_internal
320 && MName
!= null && MName
.startsWith("ffi_"))
321 || (CName
!= null && CName
.startsWith("_Jv_"))
322 || (CName
== null && MName
!= null
323 && MName
.startsWith("_Jv_"))))
325 else if (("java.lang.Thread".equals(CName
)
326 || "gnu.java.lang.MainThread".equals(CName
))
327 && "run()".equals(MName
))
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)
343 stack
= new StackTraceElement
[nr_elements
];
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
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
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(),
369 elements
[i
].isNativeMethod());
372 stack
[pos
] = elements
[i
];
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
,
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
401 private StackTraceElement
createStackTraceElement(String name
, String file
)
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('(');
412 int dot
= s
.lastIndexOf('.', bracket
);
415 className
= s
.substring(0, dot
);
416 methodName
= s
.substring(dot
+1, s
.length());
420 String fileName
= file
;
422 if (fileName
!= null)
424 int colon
= file
.lastIndexOf(':');
427 fileName
= file
.substring(0, colon
);
430 line
= Integer
.parseInt(file
.substring(colon
+1, file
.length()));
432 catch (NumberFormatException nfe
) { /* ignore */ }
438 if ("".equals(fileName
) || "??".equals(fileName
))
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
)
464 cppfiltOut
.newLine();
466 return cppfiltIn
.readLine();
468 catch (IOException ioe
) { cppfilt
.destroy(); cppfilt
= null; }
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
)
481 int length
= m
.length();
482 StringBuffer sb
= new StringBuffer(length
);
484 // Figure out the real method name
485 if (m
.startsWith("<init>"))
488 int i
= cn
.lastIndexOf('.');
492 className
= cn
.substring(i
+ 1);
493 sb
.append(className
);
498 int i
= m
.indexOf('(');
501 sb
.append(m
.substring(0,i
));
508 // Demangle the type arguments
510 char c
= (index
< length
) ? m
.charAt(index
) : ')';
541 int i
= m
.indexOf(';', index
);
544 type
= m
.substring(index
+1, i
);
548 type
= "<unknown ref>";
555 type
= "<unknown " + c
+ '>';
560 if (c
!= '[' && arrayDepth
> 0)
561 while (arrayDepth
> 0)
568 char nc
= (index
< length
) ? m
.charAt(index
) : ')';
569 if (c
!= '[' && nc
!= ')')
574 // Stop. We are not interested in the return type.
576 return sb
.toString();
580 * Releases all resources used by this NameFinder.
587 if (addr2line
!= null)
592 * Calls close to get rid of all resources.
594 protected void finalize()