OSDN Git Service

* include/jvm.h (struct _Jv_frame_info): New structure.
[pf3gnuchains/gcc-fork.git] / libjava / gnu / gcj / runtime / NameFinder.java
1 /* NameFinder.java -- Translates addresses to StackTraceElements.
2    Copyright (C) 2002 Free Software Foundation, Inc.
3
4    This file is part of libgcj.
5
6 This software is copyrighted work licensed under the terms of the
7 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
8 details.  */
9
10 package gnu.gcj.runtime;
11
12 import gnu.gcj.RawData;
13
14 import java.lang.StringBuffer;
15
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;
22
23 /**
24  * Helper class that translates addresses (represented as longs) to a
25  * StackTraceElement array.
26  *
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  *     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
39  *     sanitized.</ul>
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>
44  * </li>
45  *
46  * <code>close()</code> should be called to get rid of all resources.
47  *
48  * This class is used from <code>java.lang.VMThrowable</code>.
49  *
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.
53  *   
54  * @author Mark Wielaard (mark@klomp.org)
55  */
56 public class NameFinder
57 {
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")
62             ).booleanValue();
63   private static final boolean sanitize
64           = Boolean.valueOf(System.getProperty
65                 ("gnu.gcj.runtime.NameFinder.sanitize", "true")
66             ).booleanValue();
67   private static final boolean remove_unknown
68           = Boolean.valueOf(System.getProperty
69                 ("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
70             ).booleanValue();
71   private static final boolean use_addr2line
72           = Boolean.valueOf(System.getProperty
73                 ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
74             ).booleanValue();
75
76   /**
77    * The name of the currently running executable.
78    */
79   private final String executable;
80
81   /**
82    * Process used for demangling names.
83    */
84   private Process cppfilt;
85
86   private BufferedWriter cppfiltOut;
87   private BufferedReader cppfiltIn;
88
89   /**
90    * Process used for translating addresses to function/file names.
91    */
92   private Process addr2line;
93
94   private BufferedWriter addr2lineOut;
95   private BufferedReader addr2lineIn;
96
97   /**
98    * Creates a new NameFinder. Call close to get rid of any resources
99    * created while using the <code>lookup</code> methods.
100    */
101   public NameFinder()
102   {
103     executable = getExecutable();
104     Runtime runtime = Runtime.getRuntime();
105     if (demangle)
106     {
107       try
108         {
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()));
115         }
116       catch (IOException ioe)
117         {
118           if (cppfilt != null)
119             cppfilt.destroy();
120           cppfilt = null;
121         }
122     }
123
124     if (use_addr2line)
125       {
126         try
127           {
128             String[] exec = new String[] {"addr2line", "-f", "-e", executable};
129             addr2line = runtime.exec(exec);
130           }
131         catch (IOException ioe)
132           {
133             try
134               {
135                 String[] exec = new String[] {"addr2name.awk", executable};
136                 addr2line = runtime.exec(exec);
137               }
138             catch (IOException ioe2) { addr2line = null; }
139           }
140
141         if (addr2line != null)
142           {
143             try
144               {
145                 addr2lineIn = new BufferedReader
146                         (new InputStreamReader(addr2line.getInputStream()));
147                 addr2lineOut = new BufferedWriter
148                         (new OutputStreamWriter(addr2line.getOutputStream()));
149               }
150             catch (IOException ioe)
151               {  
152                 addr2line.destroy();
153                 addr2line = null;
154               }
155           }
156       }
157   }
158
159   /**
160    * Returns the name of the currently running process.
161    */
162   native private static String getExecutable();
163
164   /**
165    * Tries to use dladdr to create the nth StackTraceElement from the given
166    * addresses. Returns null on failure.
167    */
168   native private StackTraceElement dladdrLookup(RawData addrs, int n);
169
170   /**
171    * Returns the nth element from the stack as a hex encoded String.
172    */
173   native private String getAddrAsString(RawData addrs, int n);
174
175   /**
176    * If nth element of stack is an interpreted frame, return the
177    * element representing the method being interpreted.
178    */
179   native private StackTraceElement lookupInterp(RawData addrs, int n);
180
181   /**
182    * Creates the nth StackTraceElement from the given native stacktrace.
183    */
184   private StackTraceElement lookup(RawData addrs, int n)
185   {
186     StackTraceElement result;
187
188     result = lookupInterp(addrs, n);
189     if (result == null)
190       result = dladdrLookup(addrs, n);
191     if (result == null)
192       {
193         String name = null;
194         String file = null;
195
196         String hex = getAddrAsString(addrs, n);
197         
198         if (addr2line != null)
199           {
200             try
201               {
202                 addr2lineOut.write(hex);
203                 addr2lineOut.newLine();
204                 addr2lineOut.flush();
205                 name = addr2lineIn.readLine();
206                 file = addr2lineIn.readLine();
207               }
208             catch (IOException ioe) { addr2line = null; }
209           }
210
211         if (name == null || "??".equals(name))
212           name = hex;
213
214         result = createStackTraceElement(name, file);
215       }
216
217     return result;
218   }
219
220   /**
221    * Given an Throwable and a native stacktrace returns an array of
222    * StackTraceElement containing class, method, file and linenumbers.
223    */
224   public StackTraceElement[] lookup(Throwable t, RawData addrs, int length)
225   {
226     StackTraceElement[] elements = new StackTraceElement[length];
227     for (int i=0; i < length; i++)
228       elements[i] = lookup(addrs, i);
229
230     if (demangle && sanitize)
231       return sanitizeStack(elements, t);
232     else
233       return elements;
234   }
235
236   
237   /**
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
244    * _Jv_Throw call.
245    */
246   private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
247                                                    Throwable t)
248   {
249     StackTraceElement[] stack;
250
251     String className = t.getClass().getName();
252     String consName;
253     int lastDot = className.lastIndexOf('.');
254     if (lastDot == -1)
255       consName = className + '(';
256     else
257       consName = className.substring(lastDot + 1) + '(';
258
259     int unknown = 0;
260     int last_throw = -1;
261     int length = elements.length;
262     int end = length-1;
263     for (int i = 0; i < length; i++)
264       {
265         String CName = elements[i].getClassName();
266         String MName = elements[i].getMethodName();
267         if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
268           ||
269            (CName != null
270             && (CName.equals(className)
271                 || CName.equals("java.lang.Throwable")
272                 || CName.equals("java.lang.VMThrowable"))
273             && MName != null
274             && (MName.startsWith(consName)
275                 || MName.startsWith("Throwable(")
276                 || MName.startsWith("fillInStackTrace("))))
277           last_throw = i;
278         else if (remove_unknown && CName == null 
279                  && (MName == null || MName.startsWith("0x")))
280           unknown++;
281         else if ("main(java.lang.String[])".equals(MName))
282           {
283             end = i;
284             break;
285           }
286       }
287     int begin = last_throw+1;
288
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)
294       {
295         stack = new StackTraceElement[nr_elements];
296         int pos =0;
297         for (int i=begin; i<=end; i++)
298           {
299             String MName;
300             if (unknown == 0
301                 || !(elements[i].getClassName() == null
302                      && ((MName = elements[i].getMethodName()) == null
303                          || MName.startsWith("0x"))))
304               {
305                 stack[pos] = elements[i];
306                 pos++;
307               }
308           }
309       }
310     else
311       stack = elements;
312
313     return stack;
314   }
315
316   /**
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
322    * is unknown.
323    */
324   private StackTraceElement createStackTraceElement(String name, String file)
325   {
326     if (!demangle)
327       return new StackTraceElement(file, -1, null, name, false);
328
329     String s = demangleName(name);
330     String methodName = s;
331     String className = null;
332     int bracket = s.indexOf('(');
333     if (bracket > 0)
334       {
335         int dot = s.lastIndexOf('.', bracket);
336         if (dot > 0)
337           {
338             className = s.substring(0, dot);
339             methodName = s.substring(dot+1, s.length());
340           }
341       }
342
343     String fileName = file;
344     int line = -1;
345     if (fileName != null)
346       {
347         int colon = file.indexOf(':');
348         if (colon > 0)
349           {
350             fileName = file.substring(0, colon);
351             try
352               {
353                 line = Integer.parseInt(file.substring(colon+1, file.length()));
354               }
355             catch (NumberFormatException nfe) { /* ignore */ }
356           }
357
358         if (line == 0)
359           line =-1;
360
361         if ("".equals(fileName) || "??".equals(fileName))
362           fileName = null;
363         else if (fileName != null)
364           {
365             try
366               {
367                 fileName = new File(fileName).getCanonicalPath();
368               }
369             catch (IOException ioe) { /* ignore */ }
370           }
371       }
372
373     return new StackTraceElement(fileName, line, className, methodName, false);
374   }
375
376   /**
377    * Demangles the given String if possible. Returns the demangled String or
378    * the original string if demangling is impossible.
379    */
380   private String demangleName(String s)
381   {
382     if (cppfilt != null)
383     {
384       try
385         {
386           cppfiltOut.write(s);
387           cppfiltOut.newLine();
388           cppfiltOut.flush();
389           return cppfiltIn.readLine();
390         }
391       catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
392     }
393
394     return s;
395   }
396
397   /**
398    * Releases all resources used by this NameFinder.
399    */
400   public void close()
401   {
402     if (cppfilt != null)
403       cppfilt.destroy();
404
405     if (addr2line != null)
406       addr2line.destroy();
407   }
408
409   /**
410    * Calls close to get rid of all resources.
411    */
412   protected void finalize()
413   {
414     close();
415   }
416 }