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