1 /* NameFinder.java -- Translates addresses to StackTraceElements.
2 Copyright (C) 2002 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 * Wheter 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.use_addr2line</code>
41 * Wheter an external process (addr2line or addr2name.awk) should be used
42 * as fallback to convert the addresses to function names when the runtime
43 * is unable to do it through <code>dladdr</code>.</ul>
46 * <code>close()</code> should be called to get rid of all resources.
48 * This class is used from <code>java.lang.VMThrowable</code>.
50 * Currently the <code>lookup(long[])</code> method is not thread safe.
51 * It can easily be made thread safe by synchronizing access to all external
52 * processes when used.
54 * @author Mark Wielaard (mark@klomp.org)
56 public class NameFinder
58 // Set these to false when not needed.
59 private static final boolean demangle
60 = Boolean.valueOf(System.getProperty
61 ("gnu.gcj.runtime.NameFinder.demangle", "true")
63 private static final boolean sanitize
64 = Boolean.valueOf(System.getProperty
65 ("gnu.gcj.runtime.NameFinder.sanitize", "true")
67 private static final boolean remove_unknown
68 = Boolean.valueOf(System.getProperty
69 ("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
71 private static final boolean use_addr2line
72 = Boolean.valueOf(System.getProperty
73 ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
77 * The name of the currently running executable.
79 private final String executable;
82 * Process used for demangling names.
84 private Process cppfilt;
86 private BufferedWriter cppfiltOut;
87 private BufferedReader cppfiltIn;
90 * Process used for translating addresses to function/file names.
92 private Process addr2line;
94 private BufferedWriter addr2lineOut;
95 private BufferedReader addr2lineIn;
98 * Creates a new NameFinder. Call close to get rid of any resources
99 * created while using the <code>lookup</code> methods.
103 executable = getExecutable();
104 Runtime runtime = Runtime.getRuntime();
109 String[] exec = new String[] {"c++filt", "-s", "java"};
110 cppfilt = runtime.exec(exec);
111 cppfiltIn = new BufferedReader
112 (new InputStreamReader(cppfilt.getInputStream()));
113 cppfiltOut = new BufferedWriter
114 (new OutputStreamWriter(cppfilt.getOutputStream()));
116 catch (IOException ioe)
128 String[] exec = new String[] {"addr2line", "-f", "-e", executable};
129 addr2line = runtime.exec(exec);
131 catch (IOException ioe)
135 String[] exec = new String[] {"addr2name.awk", executable};
136 addr2line = runtime.exec(exec);
138 catch (IOException ioe2) { addr2line = null; }
141 if (addr2line != null)
145 addr2lineIn = new BufferedReader
146 (new InputStreamReader(addr2line.getInputStream()));
147 addr2lineOut = new BufferedWriter
148 (new OutputStreamWriter(addr2line.getOutputStream()));
150 catch (IOException ioe)
160 * Returns the name of the currently running process.
162 native private static String getExecutable();
165 * Tries to use dladdr to create the nth StackTraceElement from the given
166 * addresses. Returns null on failure.
168 native private StackTraceElement dladdrLookup(RawData addrs, int n);
171 * Returns the nth element from the stack as a hex encoded String.
173 native private String getAddrAsString(RawData addrs, int n);
176 * If nth element of stack is an interpreted frame, return the
177 * element representing the method being interpreted.
179 native private StackTraceElement lookupInterp(RawData addrs, int n);
182 * Creates the nth StackTraceElement from the given native stacktrace.
184 private StackTraceElement lookup(RawData addrs, int n)
186 StackTraceElement result;
188 result = lookupInterp(addrs, n);
190 result = dladdrLookup(addrs, n);
196 String hex = getAddrAsString(addrs, n);
198 if (addr2line != null)
202 addr2lineOut.write(hex);
203 addr2lineOut.newLine();
204 addr2lineOut.flush();
205 name = addr2lineIn.readLine();
206 file = addr2lineIn.readLine();
208 catch (IOException ioe) { addr2line = null; }
211 if (name == null || "??".equals(name))
214 result = createStackTraceElement(name, file);
221 * Given an Throwable and a native stacktrace returns an array of
222 * StackTraceElement containing class, method, file and linenumbers.
224 public StackTraceElement[] lookup(Throwable t, RawData addrs, int length)
226 StackTraceElement[] elements = new StackTraceElement[length];
227 for (int i=0; i < length; i++)
228 elements[i] = lookup(addrs, i);
230 if (demangle && sanitize)
231 return sanitizeStack(elements, t);
238 * Removes calls to initialize exceptions and the runtime system from
239 * the stack trace including stack frames of which nothing usefull is known.
240 * Throw away the top of the stack till we find the constructor(s)
241 * of this Throwable or at least the contructors of java.lang.Throwable
242 * or the actual fillInStackTrace call.
243 * Also throw away from the top everything before and including a runtime
246 private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
249 StackTraceElement[] stack;
251 String className = t.getClass().getName();
253 int lastDot = className.lastIndexOf('.');
255 consName = className + '(';
257 consName = className.substring(lastDot + 1) + '(';
261 int length = elements.length;
263 for (int i = 0; i < length; i++)
265 String CName = elements[i].getClassName();
266 String MName = elements[i].getMethodName();
267 if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
270 && (CName.equals(className)
271 || CName.equals("java.lang.Throwable")
272 || CName.equals("java.lang.VMThrowable"))
274 && (MName.startsWith(consName)
275 || MName.startsWith("Throwable(")
276 || MName.startsWith("fillInStackTrace("))))
278 else if (remove_unknown && CName == null
279 && (MName == null || MName.startsWith("0x")))
281 else if ("main(java.lang.String[])".equals(MName))
287 int begin = last_throw+1;
289 // Now filter out everything at the start and the end that is not part
290 // of the "normal" user program including any elements that have no
291 // usefull information whatsoever unless that means we filter out all info.
292 int nr_elements = end-begin-unknown+1;
293 if ((begin > 0 || end < length-1 || unknown > 0) && nr_elements > 0)
295 stack = new StackTraceElement[nr_elements];
297 for (int i=begin; i<=end; i++)
301 || !(elements[i].getClassName() == null
302 && ((MName = elements[i].getMethodName()) == null
303 || MName.startsWith("0x"))))
305 stack[pos] = elements[i];
317 * Creates a StackTraceElement given a string and a filename.
318 * Splits the given string into the class and method part.
319 * The string name will be a demangled to a fully qualified java method
320 * string. The string file will be decomposed into a file name and possibly
321 * a line number. The name should never be null, but the file may be if it
324 private StackTraceElement createStackTraceElement(String name, String file)
327 return new StackTraceElement(file, -1, null, name, false);
329 String s = demangleName(name);
330 String methodName = s;
331 String className = null;
332 int bracket = s.indexOf('(');
335 int dot = s.lastIndexOf('.', bracket);
338 className = s.substring(0, dot);
339 methodName = s.substring(dot+1, s.length());
343 String fileName = file;
345 if (fileName != null)
347 int colon = file.indexOf(':');
350 fileName = file.substring(0, colon);
353 line = Integer.parseInt(file.substring(colon+1, file.length()));
355 catch (NumberFormatException nfe) { /* ignore */ }
361 if ("".equals(fileName) || "??".equals(fileName))
363 else if (fileName != null)
367 fileName = new File(fileName).getCanonicalPath();
369 catch (IOException ioe) { /* ignore */ }
373 return new StackTraceElement(fileName, line, className, methodName, false);
377 * Demangles the given String if possible. Returns the demangled String or
378 * the original string if demangling is impossible.
380 private String demangleName(String s)
387 cppfiltOut.newLine();
389 return cppfiltIn.readLine();
391 catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
398 * Releases all resources used by this NameFinder.
405 if (addr2line != null)
410 * Calls close to get rid of all resources.
412 protected void finalize()