/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.java; import com.caucho.bytecode.Attribute; import com.caucho.bytecode.ByteCodeParser; import com.caucho.bytecode.JavaClass; import com.caucho.bytecode.OpaqueAttribute; import com.caucho.loader.SimpleLoader; import com.caucho.util.L10N; import com.caucho.util.Log; import com.caucho.vfs.ReadStream; import com.caucho.vfs.Vfs; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.HashMap; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * Prints a stack trace using JSR-45 */ public class ScriptStackTrace { private static final L10N L = new L10N(ScriptStackTrace.class); private static final Logger log = Log.open(ScriptStackTrace.class); private static WeakHashMap<Class,LineMap> _scriptMap = new WeakHashMap<Class,LineMap>(); /** * Filter a stack trace, replacing names. */ public static void printStackTrace(Throwable e, PrintWriter out) { StackTraceElement lastHead = null; ClassLoader loader = SimpleLoader.create(WorkDir.getLocalWorkDir()); while (true) { if (e.getMessage() != null) out.println(e.getClass().getName() + ": " + e.getMessage()); else out.println(e.getClass().getName()); StackTraceElement []trace = e.getStackTrace(); StackTraceElement nextHead = trace.length > 0 ? trace[0] : null; for (int i = 0; i < trace.length; i++) { if (trace[i].equals(lastHead)) break; out.print("\tat "); printStackTraceElement(trace[i], out, loader); } lastHead = nextHead; Throwable cause = e.getCause(); if (cause != null) { out.print("Caused by: "); e = cause; } else break; } } /** * Prints a single stack trace element. */ private static void printStackTraceElement(StackTraceElement trace, PrintWriter out, ClassLoader loader) { try { LineMap map = getScriptLineMap(trace.getClassName(), loader); if (map != null) { LineMap.Line line = map.getLine(trace.getLineNumber()); if (line != null) { out.print(trace.getClassName() + "." + trace.getMethodName()); out.print("(" + line.getSourceFilename() + ":"); out.println(line.getSourceLine(trace.getLineNumber()) + ")"); return; } } } catch (Throwable e) { } out.println(trace); } /** * Loads the local line map for a class. */ public static LineMap getScriptLineMap(String className, ClassLoader loader) { try { Class cl = loader.loadClass(className); LineMap map = _scriptMap.get(cl); if (map == null) { map = loadScriptMap(cl); _scriptMap.put(cl, map); } return map; } catch (Throwable e) { return null; } } /** * Loads the script map for a class */ private static LineMap loadScriptMap(Class cl) { ClassLoader loader = cl.getClassLoader(); if (loader == null) return new LineMap(); // null map try { String pathName = cl.getName().replace('.', '/') + ".class"; InputStream is = loader.getResourceAsStream(pathName); if (is == null) return null; try { JavaClass jClass = new ByteCodeParser().parse(is); Attribute attr = jClass.getAttribute("SourceDebugExtension"); if (attr == null) { int p = cl.getName().indexOf('$'); if (p > 0) { String className = cl.getName().substring(0, p); return loadScriptMap(loader.loadClass(className)); } return new LineMap(); } else if (attr instanceof OpaqueAttribute) { byte []value = ((OpaqueAttribute) attr).getValue(); ByteArrayInputStream bis = new ByteArrayInputStream(value); ReadStream rs = Vfs.openRead(bis); rs.setEncoding("UTF-8"); try { return parseSmap(rs); } finally { rs.close(); } } else throw new IllegalStateException(L.l("Expected opaque attribute at '{0}'", attr)); } finally { if (is != null) is.close(); } } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); return new LineMap(); // null map } } /** * Parses the SMAP file. */ private static LineMap parseSmap(ReadStream is) throws IOException { String smap = is.readln(); if (! smap.equals("SMAP")) throw new IOException(L.l("Illegal header")); String outputFile = is.readln().trim(); String defaultStratum = is.readln().trim(); String stratum = defaultStratum; HashMap<String,String> fileMap = new HashMap<String,String>(); LineMap lineMap = new LineMap(outputFile); loop: while (true) { int ch = is.read(); if (ch < 0) break; if (ch != '*') throw new IOException(L.l("unexpected character '{0}'", String.valueOf((char) ch))); int code = is.read(); String value = is.readln(); switch (code) { case 'E': break loop; case 'S': stratum = value.trim(); break; case 'F': while ((ch = is.read()) > 0 && ch != '*') { if (ch == '+') { String first = is.readln().trim(); String second = is.readln().trim(); int p = first.indexOf(' '); String key = first.substring(0, p); String file = first.substring(p + 1).trim(); if (fileMap.size() == 0) fileMap.put("", second); fileMap.put(key, second); } else { String first = is.readln().trim(); int p = first.indexOf(' '); String key = first.substring(0, p); String file = first.substring(p + 1).trim(); if (fileMap.size() == 0) fileMap.put("", file); fileMap.put(key, file); } } if (ch == '*') is.unread(); break; case 'L': while ((ch = is.read()) != '*' && ch > 0) { is.unread(); String line = is.readln().trim(); addMap(line, fileMap, lineMap); } if (ch == '*') is.unread(); break; default: while ((ch = is.read()) != '*') { is.readln(); } if (ch == '*') is.unread(); break; } } return lineMap; } private static void addMap(String line, HashMap<String,String> fileMap, LineMap lineMap) { int colon = line.indexOf(':'); if (colon < 0) return; int hash = line.indexOf('#'); int startLine = 0; String fileId = ""; int repeatCount = 1; if (hash < 0) startLine = Integer.parseInt(line.substring(0, colon)); else { startLine = Integer.parseInt(line.substring(0, hash)); int comma = line.indexOf(',', hash); if (comma > 0 && comma < colon) { fileId = line.substring(hash + 1, comma).trim(); repeatCount = Integer.parseInt(line.substring(comma + 1, colon)); } else fileId = line.substring(hash + 1, colon).trim(); } int outputLine = -1; int outputIncrement = 1; int comma = line.indexOf(',', colon); if (comma > 0) { outputLine = Integer.parseInt(line.substring(colon + 1, comma)); outputIncrement = Integer.parseInt(line.substring(comma + 1)); } else outputLine = Integer.parseInt(line.substring(colon + 1)); String file = fileMap.get(fileId); lineMap.addLine(startLine, file, repeatCount, outputLine, outputIncrement); } }