- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
当使用 JSP 或其他转换为 Java 源代码(或 stub )的语言时,通常会生成一个 SMAP 文件,稍后可以将其嵌入到类文件中以供调试器显示更好的堆栈跟踪(或在对于 Jasper,它会自动嵌入)。
有一个old JVM bug (or RFE)添加支持以在堆栈跟踪中包含 SMAP 信息,但由于缺乏 Activity ,Sun/Oracle 人员似乎更喜欢每个人自己对堆栈跟踪进行后处理。
所以这是我的问题:如何做到这一点?周围是否有图书馆为您完成繁重的工作,还是您必须自己实现一切?
我已经找到了一个可以访问异常对象和加载“启用 SMAP”类的类加载器的好地方。现在我必须
因为这似乎是一项乏味且容易出错的任务,我希望有人已经这样做了,我只需要向我的依赖项添加一个库并调用一个 makeStacktraceFancy
方法我的异常(exception)是在我记录它们之前让堆栈跟踪看起来很漂亮。
最佳答案
由于似乎没有人知道现有的解决方案,所以我推出了自己的快速而肮脏的解决方案。
它不支持所有 SMAP 功能(它只解析第一层,并忽略供应商部分和默认状态信息),但足以满足我的需求。
由于从类中提取 SMAP 属性的代码只有大约 50 行,我决定重新实现它而不是将 ASM 添加为依赖项。有关如何将其与 ASM 一起使用的代码在评论中。
因为它只进行了很少的测试(在一些测试用例上),如果我遇到任何严重的错误,我会编辑帖子。
代码如下:
/*
* SMAPSourceDebugExtension.java - Parse source debug extensions and
* enhance stack traces.
*
* Copyright (c) 2012 Michael Schierl
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package smap;
import java.io.*;
import java.util.*;
import java.util.regex.*;
/**
* Utility class to parse Source Debug Extensions and enhance stack traces.
*
* Note that only the first stratum is parsed and used.
*
* @author Michael Schierl
*/
public class SMAPSourceDebugExtension {
/**
* Enhance a stack trace with information from source debug extensions.
*
* @param t
* Throwable whose stack trace should be enhanced
* @param cl
* Class loader to load source debug extensions from
* @param keepOriginalFrames
* Whether to keep the original frames referring to Java source
* or drop them
* @param packageNames
* Names of packages that should be scanned for source debug
* extensions, or empty to scan all packages
* @throws IOException
* if an I/O error occurs
*/
public static void enhanceStackTrace(Throwable t, ClassLoader cl, boolean keepOriginalFrames, String... packageNames) throws IOException {
enhanceStackTrace(t, new HashMap<String, SMAPSourceDebugExtension>(), cl, keepOriginalFrames, packageNames);
}
/**
* Enhance a stack trace with information from source debug extensions.
* Provide a custom cache of already resolved and parsed source debug
* extensions, to avoid parsing them for every new exception.
*
* @param t
* Throwable whose stack trace should be enhanced
* @param cache
* Cache to be used and filled
* @param cl
* Class loader to load source debug extensions from
* @param keepOriginalFrames
* Whether to keep the original frames referring to Java source
* or drop them
* @param packageNames
* Names of packages that should be scanned for source debug
* extensions, or empty to scan all packages
* @throws IOException
* if an I/O error occurs
*/
public static void enhanceStackTrace(Throwable t, Map<String, SMAPSourceDebugExtension> cache, ClassLoader cl, boolean keepOriginalFrames, String... packageNames) throws IOException {
StackTraceElement[] elements = t.getStackTrace();
List<StackTraceElement> newElements = null;
for (int i = 0; i < elements.length; i++) {
String className = elements[i].getClassName();
SMAPSourceDebugExtension smap = cache.get(className);
if (smap == null) {
boolean found = false;
for (String packageName : packageNames) {
if (className.startsWith(packageName + ".")) {
found = true;
break;
}
}
if (found || packageNames.length == 0) {
InputStream in = cl.getResourceAsStream(className.replace('.', '/') + ".class");
if (in != null) {
String value = extractSourceDebugExtension(in);
in.close();
if (value != null) {
value = value.replaceAll("\r\n?", "\n");
if (value.startsWith("SMAP\n")) {
smap = new SMAPSourceDebugExtension(value);
cache.put(className, smap);
}
}
}
}
}
StackTraceElement newFrame = null;
if (smap != null) {
int[] inputLineInfo = smap.reverseLineMapping.get(elements[i].getLineNumber());
if (inputLineInfo != null && elements[i].getFileName().equals(smap.generatedFileName)) {
FileInfo inputFileInfo = smap.fileinfo.get(inputLineInfo[0]);
if (inputFileInfo != null) {
newFrame = new StackTraceElement("[" + smap.firstStratum + "]", inputFileInfo.path, inputFileInfo.name, inputLineInfo[1]);
}
}
}
if (newFrame != null) {
if (newElements == null) {
newElements = new ArrayList<StackTraceElement>(Arrays.asList(elements).subList(0, i));
}
if (keepOriginalFrames)
newElements.add(elements[i]);
newElements.add(newFrame);
} else if (newElements != null) {
newElements.add(elements[i]);
}
}
if (newElements != null) {
t.setStackTrace(newElements.toArray(new StackTraceElement[newElements.size()]));
}
if (t.getCause() != null)
enhanceStackTrace(t.getCause(), cache, cl, keepOriginalFrames, packageNames);
}
/**
* Extract source debug extension from a class file, provided as an input
* stream
*
* @param in
* Input stream to read the class file
* @return Source debug extension as a String, or <code>null</code> if none
* was found.
* @throws IOException
* if an I/O error occurs
*/
// // ASM version of the same method:
// private static String extractSourceDebugExtension0(InputStream in) throws IOException {
// ClassReader cr = new ClassReader(in);
// final String[] result = new String[1];
// cr.accept(new ClassVisitor(Opcodes.ASM4) {
// @Override
// public void visitSource(String source, String debug) {
// result[0] = debug;
// }
// }, 0);
// return result[0];
// }
private static String extractSourceDebugExtension(InputStream in) throws IOException {
DataInputStream dis = new DataInputStream(in);
boolean[] isSourceDebugExtension;
dis.skipBytes(8);
// read constant pool
isSourceDebugExtension = new boolean[dis.readUnsignedShort()];
int[] skipSizes = new int[] { 0, 0, 2, 4, 4, 0, 0, 2, 2, 4, 4, 4, 4, 2, 2, 3, 2, 2, 4 };
for (int i = 1; i < isSourceDebugExtension.length; i++) {
byte type = dis.readByte();
int skipSize;
if (type == 1) {
String value = dis.readUTF();
isSourceDebugExtension[i] = value.equals("SourceDebugExtension");
skipSize = 0;
} else if (type == 5 || type == 6) {
skipSize = 8;
i++;
} else if (type > 1 && type < 19) {
skipSize = skipSizes[type];
} else {
skipSize = 2;
}
dis.skipBytes(skipSize);
}
dis.skipBytes(6);
int ifaces = dis.readUnsignedShort();
dis.skipBytes(2 * ifaces);
// skip fields and methods
for (int k = 0; k < 2; k++) {
int count = dis.readUnsignedShort();
for (int i = 0; i < count; i++) {
dis.skipBytes(6);
int attrCount = dis.readUnsignedShort();
for (int j = 0; j < attrCount; j++) {
dis.skipBytes(2);
int skip = dis.readInt();
dis.skipBytes(skip);
}
}
}
// read attributes and find SourceDebugExtension
int attrCount = dis.readUnsignedShort();
for (int i = 0; i < attrCount; i++) {
int idx = dis.readUnsignedShort();
int len = dis.readInt();
if (isSourceDebugExtension[idx]) {
byte[] buf = new byte[len];
dis.readFully(buf);
return new String(buf, "UTF-8");
} else {
dis.skipBytes(len);
}
}
return null;
}
private final String generatedFileName, firstStratum;
private final Map<Integer, FileInfo> fileinfo = new HashMap<Integer, FileInfo>();
private final Map<Integer, int[]> reverseLineMapping = new HashMap<Integer, int[]>();
private static final Pattern LINE_INFO_PATTERN = Pattern.compile("([0-9]+)(?:#([0-9]+))?(?:,([0-9]+))?:([0-9]+)(?:,([0-9]+))?");
private SMAPSourceDebugExtension(String value) {
String[] lines = value.split("\n");
if (!lines[0].equals("SMAP") || !lines[3].startsWith("*S ") || !lines[4].equals("*F"))
throw new IllegalArgumentException(value);
generatedFileName = lines[1];
firstStratum = lines[3].substring(3);
int idx = 5;
while (!lines[idx].startsWith("*")) {
String infoline = lines[idx++], path = null;
if (infoline.startsWith("+ ")) {
path = lines[idx++];
infoline = infoline.substring(2);
}
int pos = infoline.indexOf(" ");
int filenum = Integer.parseInt(infoline.substring(0, pos));
String name = infoline.substring(pos + 1);
fileinfo.put(filenum, new FileInfo(name, path == null ? name : path));
}
if (lines[idx].equals("*L")) {
idx++;
int lastLFI = 0;
while (!lines[idx].startsWith("*")) {
Matcher m = LINE_INFO_PATTERN.matcher(lines[idx++]);
if (!m.matches())
throw new IllegalArgumentException(lines[idx - 1]);
int inputStartLine = Integer.parseInt(m.group(1));
int lineFileID = m.group(2) == null ? lastLFI : Integer.parseInt(m.group(2));
int repeatCount = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
int outputStartLine = Integer.parseInt(m.group(4));
int outputLineIncrement = m.group(5) == null ? 1 : Integer.parseInt(m.group(5));
for (int i = 0; i < repeatCount; i++) {
int[] inputMapping = new int[] { lineFileID, inputStartLine + i };
int baseOL = outputStartLine + i * outputLineIncrement;
for (int ol = baseOL; ol < baseOL + outputLineIncrement; ol++) {
if (!reverseLineMapping.containsKey(ol))
reverseLineMapping.put(ol, inputMapping);
}
}
lastLFI = lineFileID;
}
}
}
private static class FileInfo {
public final String name, path;
public FileInfo(String name, String path) {
this.name = name;
this.path = path;
}
}
}
关于java - 如何在运行时将 JSR-045 SMAP 信息添加到 Java 堆栈跟踪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11250834/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!