001 // Copyright 2008 Waterken Inc. under the terms of the MIT X license
002 // found at http://www.opensource.org/licenses/mit-license.html
003 package org.waterken.trace.application;
004
005 import java.io.Serializable;
006 import java.lang.reflect.Member;
007 import java.lang.reflect.Proxy;
008
009 import org.joe_e.Struct;
010 import org.joe_e.array.IntArray;
011 import org.joe_e.array.PowerlessArray;
012 import org.ref_send.log.CallSite;
013 import org.ref_send.log.Trace;
014 import org.waterken.trace.Tracer;
015
016 /**
017 * Produces a stack trace composed of only calls initiated by application code.
018 */
019 public final class
020 ApplicationTracer {
021 private ApplicationTracer() {}
022
023 /**
024 * Constructs an instance.
025 * @param code application class loader
026 */
027 static public Tracer
028 make(final ClassLoader code) {
029 class TracerX extends Struct implements Tracer, Serializable {
030 static private final long serialVersionUID = 1L;
031
032 public long
033 timestamp() { return System.currentTimeMillis(); }
034
035 public String
036 readException(final Throwable e) { return e.getMessage(); }
037
038 public Trace
039 traceException(final Throwable e) {return trace(e.getStackTrace());}
040
041 public Trace
042 traceMember(final Member lambda) {
043 return trace(new StackTraceElement(
044 lambda.getDeclaringClass().getName(), lambda.getName(),
045 null, -1));
046 // TODO: want the line number of the method declaration
047 }
048
049 public Trace
050 traceHere() {return trace(Thread.currentThread().getStackTrace());}
051
052 private Trace
053 trace(final StackTraceElement... frames) {
054 final PowerlessArray.Builder<CallSite> sites =
055 PowerlessArray.builder(frames.length);
056 for (final StackTraceElement frame : frames) {
057 final int line = frame.getLineNumber();
058 if (1 == line) { continue; } // skip synthetic method
059
060 // Is this frame from application code or system code?
061 boolean included = false;
062 try {
063 final Class<?> c = code.loadClass(frame.getClassName());
064 if (!Proxy.isProxyClass(c)) {
065 final ClassLoader lib = c.getClassLoader();
066 final ClassLoader sys =
067 ApplicationTracer.class.getClassLoader();
068 for (ClassLoader i=code; i!=sys; i=i.getParent()) {
069 if (lib == i) {
070 included = true;
071 break;
072 }
073 }
074 }
075 } catch (final ClassNotFoundException e) {
076 // Assume it's not an application code class.
077 }
078 if (!included) { continue; }
079
080 // Describe the application stack frame.
081 final String name; {
082 final String fqn = frame.getClassName();
083 final String cn = fqn.substring(fqn.lastIndexOf('.')+1);
084 final String sn = cn.replace("$1",".").replace("$",".");
085 name = sn + "." + frame.getMethodName();
086 }
087 final String source; {
088 final String fqn = frame.getClassName();
089 final int end = fqn.indexOf('$');
090 final String top = -1==end ? fqn : fqn.substring(0,end);
091 source = top.replace('.', '/') + ".java";
092 }
093 sites.append(new CallSite(name, source, line > 1
094 ? PowerlessArray.array(IntArray.array(line)) : null));
095 }
096 return sites.length() != 0 ? new Trace(sites.snapshot()) : null;
097 }
098 }
099 return new TracerX();
100 }
101 }