001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.java;
031:
032: import com.caucho.bytecode.Attribute;
033: import com.caucho.bytecode.ByteCodeParser;
034: import com.caucho.bytecode.JavaClass;
035: import com.caucho.bytecode.OpaqueAttribute;
036: import com.caucho.loader.SimpleLoader;
037: import com.caucho.util.L10N;
038: import com.caucho.util.Log;
039: import com.caucho.vfs.ReadStream;
040: import com.caucho.vfs.Vfs;
041:
042: import java.io.ByteArrayInputStream;
043: import java.io.IOException;
044: import java.io.InputStream;
045: import java.io.PrintWriter;
046: import java.util.HashMap;
047: import java.util.WeakHashMap;
048: import java.util.logging.Level;
049: import java.util.logging.Logger;
050:
051: /**
052: * Prints a stack trace using JSR-45
053: */
054: public class ScriptStackTrace {
055: private static final L10N L = new L10N(ScriptStackTrace.class);
056: private static final Logger log = Log.open(ScriptStackTrace.class);
057:
058: private static WeakHashMap<Class, LineMap> _scriptMap = new WeakHashMap<Class, LineMap>();
059:
060: /**
061: * Filter a stack trace, replacing names.
062: */
063: public static void printStackTrace(Throwable e, PrintWriter out) {
064: StackTraceElement lastHead = null;
065:
066: ClassLoader loader = SimpleLoader.create(WorkDir
067: .getLocalWorkDir());
068:
069: while (true) {
070: if (e.getMessage() != null)
071: out.println(e.getClass().getName() + ": "
072: + e.getMessage());
073: else
074: out.println(e.getClass().getName());
075:
076: StackTraceElement[] trace = e.getStackTrace();
077: StackTraceElement nextHead = trace.length > 0 ? trace[0]
078: : null;
079:
080: for (int i = 0; i < trace.length; i++) {
081: if (trace[i].equals(lastHead))
082: break;
083:
084: out.print("\tat ");
085:
086: printStackTraceElement(trace[i], out, loader);
087: }
088:
089: lastHead = nextHead;
090:
091: Throwable cause = e.getCause();
092:
093: if (cause != null) {
094: out.print("Caused by: ");
095: e = cause;
096: } else
097: break;
098: }
099: }
100:
101: /**
102: * Prints a single stack trace element.
103: */
104: private static void printStackTraceElement(StackTraceElement trace,
105: PrintWriter out, ClassLoader loader) {
106: try {
107: LineMap map = getScriptLineMap(trace.getClassName(), loader);
108:
109: if (map != null) {
110: LineMap.Line line = map.getLine(trace.getLineNumber());
111: if (line != null) {
112: out.print(trace.getClassName() + "."
113: + trace.getMethodName());
114: out.print("(" + line.getSourceFilename() + ":");
115: out.println(line.getSourceLine(trace
116: .getLineNumber())
117: + ")");
118: return;
119: }
120: }
121: } catch (Throwable e) {
122: }
123:
124: out.println(trace);
125: }
126:
127: /**
128: * Loads the local line map for a class.
129: */
130: public static LineMap getScriptLineMap(String className,
131: ClassLoader loader) {
132: try {
133: Class cl = loader.loadClass(className);
134:
135: LineMap map = _scriptMap.get(cl);
136:
137: if (map == null) {
138: map = loadScriptMap(cl);
139: _scriptMap.put(cl, map);
140: }
141:
142: return map;
143: } catch (Throwable e) {
144: return null;
145: }
146: }
147:
148: /**
149: * Loads the script map for a class
150: */
151: private static LineMap loadScriptMap(Class cl) {
152: ClassLoader loader = cl.getClassLoader();
153:
154: if (loader == null)
155: return new LineMap(); // null map
156:
157: try {
158: String pathName = cl.getName().replace('.', '/') + ".class";
159:
160: InputStream is = loader.getResourceAsStream(pathName);
161:
162: if (is == null)
163: return null;
164:
165: try {
166: JavaClass jClass = new ByteCodeParser().parse(is);
167:
168: Attribute attr = jClass
169: .getAttribute("SourceDebugExtension");
170:
171: if (attr == null) {
172: int p = cl.getName().indexOf('$');
173:
174: if (p > 0) {
175: String className = cl.getName().substring(0, p);
176:
177: return loadScriptMap(loader
178: .loadClass(className));
179: }
180:
181: return new LineMap();
182: } else if (attr instanceof OpaqueAttribute) {
183: byte[] value = ((OpaqueAttribute) attr).getValue();
184:
185: ByteArrayInputStream bis = new ByteArrayInputStream(
186: value);
187:
188: ReadStream rs = Vfs.openRead(bis);
189: rs.setEncoding("UTF-8");
190:
191: try {
192: return parseSmap(rs);
193: } finally {
194: rs.close();
195: }
196: } else
197: throw new IllegalStateException(L.l(
198: "Expected opaque attribute at '{0}'", attr));
199: } finally {
200: if (is != null)
201: is.close();
202: }
203: } catch (Throwable e) {
204: log.log(Level.FINER, e.toString(), e);
205:
206: return new LineMap(); // null map
207: }
208: }
209:
210: /**
211: * Parses the SMAP file.
212: */
213: private static LineMap parseSmap(ReadStream is) throws IOException {
214: String smap = is.readln();
215:
216: if (!smap.equals("SMAP"))
217: throw new IOException(L.l("Illegal header"));
218:
219: String outputFile = is.readln().trim();
220: String defaultStratum = is.readln().trim();
221:
222: String stratum = defaultStratum;
223: HashMap<String, String> fileMap = new HashMap<String, String>();
224:
225: LineMap lineMap = new LineMap(outputFile);
226:
227: loop: while (true) {
228: int ch = is.read();
229:
230: if (ch < 0)
231: break;
232:
233: if (ch != '*')
234: throw new IOException(L.l("unexpected character '{0}'",
235: String.valueOf((char) ch)));
236:
237: int code = is.read();
238: String value = is.readln();
239:
240: switch (code) {
241: case 'E':
242: break loop;
243:
244: case 'S':
245: stratum = value.trim();
246: break;
247:
248: case 'F':
249: while ((ch = is.read()) > 0 && ch != '*') {
250: if (ch == '+') {
251: String first = is.readln().trim();
252: String second = is.readln().trim();
253:
254: int p = first.indexOf(' ');
255: String key = first.substring(0, p);
256: String file = first.substring(p + 1).trim();
257:
258: if (fileMap.size() == 0)
259: fileMap.put("", second);
260:
261: fileMap.put(key, second);
262: } else {
263: String first = is.readln().trim();
264:
265: int p = first.indexOf(' ');
266: String key = first.substring(0, p);
267: String file = first.substring(p + 1).trim();
268:
269: if (fileMap.size() == 0)
270: fileMap.put("", file);
271:
272: fileMap.put(key, file);
273: }
274: }
275: if (ch == '*')
276: is.unread();
277: break;
278:
279: case 'L':
280: while ((ch = is.read()) != '*' && ch > 0) {
281: is.unread();
282:
283: String line = is.readln().trim();
284:
285: addMap(line, fileMap, lineMap);
286: }
287: if (ch == '*')
288: is.unread();
289: break;
290:
291: default:
292: while ((ch = is.read()) != '*') {
293: is.readln();
294: }
295: if (ch == '*')
296: is.unread();
297: break;
298: }
299: }
300:
301: return lineMap;
302: }
303:
304: private static void addMap(String line,
305: HashMap<String, String> fileMap, LineMap lineMap) {
306: int colon = line.indexOf(':');
307:
308: if (colon < 0)
309: return;
310:
311: int hash = line.indexOf('#');
312: int startLine = 0;
313: String fileId = "";
314: int repeatCount = 1;
315:
316: if (hash < 0)
317: startLine = Integer.parseInt(line.substring(0, colon));
318: else {
319: startLine = Integer.parseInt(line.substring(0, hash));
320:
321: int comma = line.indexOf(',', hash);
322:
323: if (comma > 0 && comma < colon) {
324: fileId = line.substring(hash + 1, comma).trim();
325: repeatCount = Integer.parseInt(line.substring(
326: comma + 1, colon));
327: } else
328: fileId = line.substring(hash + 1, colon).trim();
329: }
330:
331: int outputLine = -1;
332: int outputIncrement = 1;
333:
334: int comma = line.indexOf(',', colon);
335:
336: if (comma > 0) {
337: outputLine = Integer.parseInt(line.substring(colon + 1,
338: comma));
339: outputIncrement = Integer.parseInt(line
340: .substring(comma + 1));
341: } else
342: outputLine = Integer.parseInt(line.substring(colon + 1));
343:
344: String file = fileMap.get(fileId);
345:
346: lineMap.addLine(startLine, file, repeatCount, outputLine,
347: outputIncrement);
348: }
349: }
|