package gnu.gcj.runtime;
import gnu.gcj.RawData;
import java.lang.StringBuffer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.File;
public class NameFinder
{
private static final boolean demangle
= Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.demangle", "true")
).booleanValue();
private static final boolean sanitize
= Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.sanitize", "true")
).booleanValue();
private static final boolean remove_unknown
= Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
).booleanValue();
private static final boolean remove_internal
= (Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.remove_internal", "true")
).booleanValue()
||
Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.remove_interpreter", "true")
).booleanValue()
);
private static final boolean use_addr2line
= Boolean.valueOf(System.getProperty
("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
).booleanValue();
private final String executable;
private Process cppfilt;
private BufferedWriter cppfiltOut;
private BufferedReader cppfiltIn;
private Process addr2line;
private BufferedWriter addr2lineOut;
private BufferedReader addr2lineIn;
private boolean usingAddr2name = false;
public NameFinder()
{
executable = getExecutable();
Runtime runtime = Runtime.getRuntime();
if (demangle)
{
try
{
String[] exec = new String[] {"c++filt", "-s", "java"};
cppfilt = runtime.exec(exec);
cppfiltIn = new BufferedReader
(new InputStreamReader(cppfilt.getInputStream()));
cppfiltOut = new BufferedWriter
(new OutputStreamWriter(cppfilt.getOutputStream()));
}
catch (IOException ioe)
{
if (cppfilt != null)
cppfilt.destroy();
cppfilt = null;
}
}
if (use_addr2line)
{
try
{
String[] exec = new String[] {"addr2line", "-f", "-e", executable};
addr2line = runtime.exec(exec);
}
catch (IOException ioe)
{
try
{
String[] exec = new String[] {"addr2name.awk", executable};
addr2line = runtime.exec(exec);
usingAddr2name = true;
}
catch (IOException ioe2) { addr2line = null; }
}
if (addr2line != null)
{
addr2lineIn = new BufferedReader
(new InputStreamReader(addr2line.getInputStream()));
addr2lineOut = new BufferedWriter
(new OutputStreamWriter(addr2line.getOutputStream()));
}
}
}
native private static String getExecutable();
native private StackTraceElement dladdrLookup(RawData addrs, int n);
native private String getAddrAsString(RawData addrs, int n);
native private String getExternalLabel(String name);
native private StackTraceElement lookupInterp(RawData addrs, int n);
private StackTraceElement lookup(RawData addrs, int n)
{
StackTraceElement result;
result = lookupInterp(addrs, n);
if (result == null)
result = dladdrLookup(addrs, n);
if (result == null)
{
String name = null;
String file = null;
String hex = getAddrAsString(addrs, n);
if (addr2line != null)
{
try
{
addr2lineOut.write(hex);
addr2lineOut.newLine();
addr2lineOut.flush();
name = addr2lineIn.readLine();
file = addr2lineIn.readLine();
if (! usingAddr2name)
if (name != null && ! "??".equals (name))
name = getExternalLabel (name);
}
catch (IOException ioe) { addr2line = null; }
}
if (name == null || "??".equals(name))
name = hex;
result = createStackTraceElement(name, file);
}
return result;
}
public StackTraceElement[] lookup(Throwable t, StackTrace trace)
{
RawData addrs = trace.stackTraceAddrs();
int length = trace.length();
StackTraceElement[] elements = new StackTraceElement[length];
for (int i=0; i < length; i++)
elements[i] = lookup(addrs, i);
if (demangle && sanitize)
return sanitizeStack(elements, t);
else
return elements;
}
private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
Throwable t)
{
StackTraceElement[] stack;
String className = t.getClass().getName();
String consName;
int lastDot = className.lastIndexOf('.');
if (lastDot == -1)
consName = className + '(';
else
consName = className.substring(lastDot + 1) + '(';
int unknown = 0;
int internal = 0;
int last_throw = -1;
int length = elements.length;
int end = length-1;
for (int i = 0; i < length; i++)
{
String CName = elements[i].getClassName();
String MName = elements[i].getMethodName();
if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
||
(CName != null
&& (CName.equals(className)
|| CName.equals("java.lang.Throwable")
|| CName.equals("java.lang.VMThrowable"))
&& MName != null
&& (MName.startsWith(consName)
|| MName.startsWith("Throwable(")
|| MName.startsWith("fillInStackTrace("))))
{
last_throw = i;
unknown = 0;
internal = 0;
}
else if (remove_unknown && CName == null
&& (MName == null || MName.startsWith("0x")))
unknown++;
else if (remove_internal
&& ((CName == null
&& MName != null && MName.startsWith("ffi_"))
|| (CName != null && CName.startsWith("_Jv_"))
|| (CName == null && MName != null
&& MName.startsWith("_Jv_"))))
internal++;
else if (("java.lang.Thread".equals(CName)
|| "gnu.java.lang.MainThread".equals(CName))
&& "run()".equals(MName))
{
end = i;
break;
}
}
int begin = last_throw+1;
int nr_elements = end - begin - unknown - internal + 1;
if ((begin > 0 || end < length-1 || unknown > 0 || internal > 0)
&& nr_elements > 0)
{
stack = new StackTraceElement[nr_elements];
int pos =0;
for (int i=begin; i<=end; i++)
{
String MName = elements[i].getMethodName();
String CName = elements[i].getClassName();
if (remove_unknown && CName == null
&& (MName == null || MName.startsWith("0x")))
; else if (remove_internal
&& ((CName == null
&& MName != null && MName.startsWith("ffi_"))
|| (CName != null && CName.startsWith("_Jv_"))
|| (CName == null && MName != null
&& MName.startsWith("_Jv_"))))
; else
{
if (MName == null || CName == null)
{
MName = MName == null ? "" : MName;
CName = CName == null ? "" : CName;
stack[pos] = newElement(elements[i].getFileName(),
elements[i].getLineNumber(),
CName, MName,
elements[i].isNativeMethod());
}
else
stack[pos] = elements[i];
pos++;
}
}
}
else
stack = elements;
return stack;
}
native static private StackTraceElement newElement(String fileName,
int lineNumber,
String className,
String methName,
boolean isNative);
private StackTraceElement createStackTraceElement(String name, String file)
{
if (!demangle)
return newElement(file, -1, null, name, false);
String s = demangleName(name);
String methodName = s;
String className = null;
int bracket = s.indexOf('(');
if (bracket > 0)
{
int dot = s.lastIndexOf('.', bracket);
if (dot > 0)
{
className = s.substring(0, dot);
methodName = s.substring(dot+1, s.length());
}
}
String fileName = file;
int line = -1;
if (fileName != null)
{
int colon = file.lastIndexOf(':');
if (colon > 0)
{
fileName = file.substring(0, colon);
try
{
line = Integer.parseInt(file.substring(colon+1, file.length()));
}
catch (NumberFormatException nfe) { }
}
if (line == 0)
line =-1;
if ("".equals(fileName) || "??".equals(fileName))
fileName = null;
else if (fileName != null)
{
try
{
fileName = new File(fileName).getCanonicalPath();
}
catch (IOException ioe) { }
}
}
return newElement(fileName, line, className, methodName, false);
}
private String demangleName(String s)
{
if (cppfilt != null)
{
try
{
cppfiltOut.write(s);
cppfiltOut.newLine();
cppfiltOut.flush();
return cppfiltIn.readLine();
}
catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
}
return s;
}
public static String demangleInterpreterMethod(String m, String cn)
{
int index = 0;
int length = m.length();
StringBuffer sb = new StringBuffer(length);
if (m.startsWith("<init>"))
{
String className;
int i = cn.lastIndexOf('.');
if (i < 0)
className = cn;
else
className = cn.substring(i + 1);
sb.append(className);
index += 7;
}
else
{
int i = m.indexOf('(');
if (i > 0)
{
sb.append(m.substring(0,i));
index += i + 1;
}
}
sb.append('(');
int arrayDepth = 0;
char c = (index < length) ? m.charAt(index) : ')';
while (c != ')')
{
String type;
switch(c)
{
case 'B':
type = "byte";
break;
case 'C':
type = "char";
break;
case 'D':
type = "double";
break;
case 'F':
type = "float";
break;
case 'I':
type = "int";
break;
case 'J':
type = "long";
break;
case 'S':
type = "short";
break;
case 'Z':
type = "boolean";
break;
case 'L':
int i = m.indexOf(';', index);
if (i > 0)
{
type = m.substring(index+1, i);
index = i;
}
else
type = "<unknown ref>";
break;
case '[':
type = "";
arrayDepth++;
break;
default:
type = "<unknown " + c + '>';
}
sb.append(type);
if (c != '[' && arrayDepth > 0)
while (arrayDepth > 0)
{
sb.append("[]");
arrayDepth--;
}
index++;
char nc = (index < length) ? m.charAt(index) : ')';
if (c != '[' && nc != ')')
sb.append(", ");
c = nc;
}
sb.append(')');
return sb.toString();
}
public void close()
{
if (cppfilt != null)
cppfilt.destroy();
if (addr2line != null)
addr2line.destroy();
}
protected void finalize()
{
close();
}
}