OSDN Git Service

2006-02-16 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / gnu / gcj / runtime / NameFinder.java
1 /* NameFinder.java -- Translates addresses to StackTraceElements.
2    Copyright (C) 2002, 2004 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.classpath.Configuration;
13 import gnu.gcj.RawData;
14
15 import java.lang.StringBuffer;
16
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.InputStreamReader;
20 import java.io.OutputStreamWriter;
21 import java.io.IOException;
22 import java.io.File;
23 import java.util.Iterator;
24 import java.util.HashMap;
25
26
27 /**
28  * Lookup addresses (represented as longs) to find source & line number info.
29  *
30  * The following system property is available (defaults to true):
31  * <li>
32  * <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
33  *     Whether an external process, addr2line, should be used to look up
34  *     source file and line number info. Throwable.printStackTrace() will
35  *     be faster if this property is set to 'false'.
36  * </ul>
37  * <ul><code>gnu.gcj.runtime.NameFinder.remove_unknown</code>
38  *     Whether calls to unknown functions (class and method names are unknown)
39  *     should be removed from the stack trace. </ul>
40  * </li>
41  *
42  * <code>close()</code> should be called to get rid of all resources.
43  *
44  * This class is used from <code>java.lang.VMThrowable</code>.
45  *
46  * @author Mark Wielaard (mark@klomp.org)
47  */
48 public class NameFinder
49 {
50   /**
51    * The name of the binary to look up.
52    */
53   private String binaryFile;
54   private String sourceFile;
55   private int lineNum;
56   private HashMap procs = new HashMap();
57
58   private static final boolean use_addr2line
59           = Boolean.valueOf(System.getProperty
60                 ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
61             ).booleanValue();
62
63   private static final boolean remove_unknown
64           = Boolean.valueOf(System.getProperty
65                 ("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
66             ).booleanValue();
67
68   // Return true if non-Java frames should be removed from stack
69   // traces.
70   static final boolean removeUnknown()
71   {
72     return remove_unknown;
73   }
74
75   class Addr2Line
76   {
77     Process proc;
78     BufferedWriter out;
79     BufferedReader in;
80
81     Addr2Line(String binaryFile)
82     {
83       try
84       {
85         String[] exec = new String[] {"addr2line", "-e", binaryFile};
86         Runtime runtime = Runtime.getRuntime();
87         proc = runtime.exec(exec);
88       }
89       catch (IOException ioe)
90       {
91       }
92
93       if (proc != null)
94       {
95         in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
96         out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
97       }
98     }
99     
100     void close()
101     {
102       try
103       {
104         if (in != null)
105           in.close();
106         if (out != null)
107           out.close();
108       }
109       catch (IOException x) {}
110       if (proc != null)
111         proc.destroy();
112     }
113   }
114
115   /**
116    * Create a new NameFinder to lookup names in binaryFile. Call close to get rid of any 
117    * resources created while using the <code>lookup</code> methods.
118    */
119   public NameFinder()
120   {
121   }
122
123   /**
124    * Returns the source file name if lookup() was successful. If the source file could not be 
125    * determined, the binary name will be returned instead.
126    */
127   public String getSourceFile()
128   {
129     String file;
130     if (sourceFile != null)
131       file = sourceFile;
132     else
133       file = binaryFile;
134     
135     return file.substring(file.lastIndexOf(File.separator) + 1, file.length());
136   }
137
138   /**
139    * If lookup() was successful, returns the line number of addr. If the line number could not
140    * be determined, -1 is returned.
141    */  
142   public int getLineNum()
143   {
144     return lineNum;
145   }
146   
147   public void lookup (String file, long addr)
148   {
149     binaryFile = file;
150     sourceFile = null;
151     lineNum = -1;
152     
153     if (! use_addr2line)
154       return;
155     Addr2Line addr2line = (Addr2Line) procs.get(file);
156     if (addr2line == null)
157       {
158       addr2line = new Addr2Line(file);
159       procs.put(file, addr2line);
160       }
161     
162     if (addr2line.proc == null)      
163       return;
164     
165     String hexAddr = "0x" + Long.toHexString(addr);
166     String name;
167
168     try
169       {
170       addr2line.out.write(hexAddr);
171       addr2line.out.newLine();
172       addr2line.out.flush();
173       String result = addr2line.in.readLine();
174
175       if (result.indexOf("??") == -1)
176         {
177           int split = result.lastIndexOf(':');
178           sourceFile = result.substring(0, split);
179           String lineNumStr = result.substring(split + 1, result.length());
180           lineNum = Integer.parseInt (lineNumStr);
181         }
182       }
183     catch (IOException ioe)
184       {
185       addr2line = null;
186       }
187     catch (NumberFormatException x)
188       {
189       }
190   }
191
192   /**
193    * Returns human readable method name and aguments given a method type
194    * signature as known to the interpreter and a classname.
195    */
196   public static String demangleInterpreterMethod(String m, String cn)
197   {
198     int index = 0;
199     int length = m.length();
200     StringBuffer sb = new StringBuffer(length);
201
202     // Figure out the real method name
203     if (m.startsWith("<init>"))
204       {
205         String className;
206         int i = cn.lastIndexOf('.');
207         if (i < 0)
208           className = cn;
209         else
210           className = cn.substring(i + 1);
211         sb.append(className);
212         index += 7;
213       }
214     else
215       {
216         int i = m.indexOf('(');
217         if (i > 0)
218           {
219             sb.append(m.substring(0,i));
220             index += i + 1;
221           }
222       }
223
224     sb.append('(');
225
226     // Demangle the type arguments
227     int arrayDepth = 0;
228     char c = (index < length) ? m.charAt(index) : ')';
229     while (c != ')')      
230       {
231         String type;
232         switch(c)
233         {
234           case 'B':
235             type = "byte";
236             break;
237           case 'C':
238             type = "char";
239             break;
240           case 'D':
241             type = "double";
242             break;
243           case 'F':
244             type = "float";
245             break;
246           case 'I':
247             type = "int";
248             break;
249           case 'J':
250             type = "long";
251             break;
252           case 'S':
253             type = "short";
254             break;
255           case 'Z':
256             type = "boolean";
257             break;
258           case 'L':
259             int i = m.indexOf(';', index);
260             if (i > 0)
261               {
262                 type = m.substring(index+1, i);
263                 index = i;
264               }
265             else
266               type = "<unknown ref>";
267             break;
268           case '[':
269             type = "";
270             arrayDepth++;
271             break;
272           default:
273             type = "<unknown " + c + '>';
274         }
275         sb.append(type);
276
277         // Handle arrays
278         if (c != '[' && arrayDepth > 0)
279           while (arrayDepth > 0)
280             {
281               sb.append("[]");
282               arrayDepth--;
283             }
284
285         index++;
286         char nc = (index < length) ? m.charAt(index) : ')';
287         if (c != '[' && nc  != ')')
288           sb.append(", ");
289         c = nc;
290       }
291
292     // Stop. We are not interested in the return type.
293     sb.append(')');
294     return sb.toString();
295   }
296
297   /**
298    * Releases all resources used by this NameFinder.
299    */
300   public void close()
301   {
302     Iterator itr = procs.values().iterator();
303     while (itr.hasNext())
304       {
305         Addr2Line proc = (Addr2Line) itr.next();
306         proc.close();
307       }
308   }
309 }