gpt4 book ai didi

java - 如何在运行时将 JSR-045 SMAP 信息添加到 Java 堆栈跟踪?

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:22:26 25 4
gpt4 key购买 nike

当使用 JSP 或其他转换为 Java 源代码(或 stub )的语言时,通常会生成一个 SMAP 文件,稍后可以将其嵌入到类文件中以供调试器显示更好的堆栈跟踪(或在对于 Jasper,它会自动嵌入)。

有一个old JVM bug (or RFE)添加支持以在堆栈跟踪中包含 SMAP 信息,但由于缺乏 Activity ,Sun/Oracle 人员似乎更喜欢每个人自己对堆栈跟踪进行后处理。

所以这是我的问题:如何做到这一点?周围是否有图书馆为您完成繁重的工作,还是您必须自己实现一切?

我已经找到了一个可以访问异常对象和加载“启用 SMAP”类的类加载器的好地方。现在我必须

  • 遍历堆栈跟踪
  • 如果我能找到类,请检查每个条目
  • 用 e 分析类(class)。 G。 ASM提取 SMAP 信息
  • 编写一个 SMAP 解析器,从 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com