Source for gnu.gcj.runtime.NameFinder

   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: }