Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.jetbrains.capture.org.objectweb.asm.ClassWriter;
import org.jetbrains.capture.org.objectweb.asm.MethodVisitor;
import org.jetbrains.capture.org.objectweb.asm.Opcodes;
import org.jetbrains.capture.org.objectweb.asm.tree.*;

import java.io.FileOutputStream;
import java.lang.instrument.ClassFileTransformer;
Expand Down Expand Up @@ -43,7 +44,7 @@ public byte[] transform(ClassLoader loader,
return transformer.accept(new ClassVisitor(Opcodes.API_VERSION, transformer.writer) {
@Override
public MethodVisitor visitMethod(final int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor superMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
final MethodVisitor superMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
if (!"write".equals(name)) return superMethodVisitor;

// There are also versions like (B)V, but they are uninteresting in terms of logging capture.
Expand All @@ -59,23 +60,11 @@ public MethodVisitor visitMethod(final int access, String name, String descripto
return superMethodVisitor;
}

return new MethodVisitor(api, superMethodVisitor) {
return new MethodNode(api, access, name, descriptor, signature, exceptions) {
@Override
public void visitCode() {
super.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD,
"java/io/FileOutputStream",
"fd", "Ljava/io/FileDescriptor;");
mv.visitVarInsn(Opcodes.ALOAD, 1);
if (isWithOffset) {
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitVarInsn(Opcodes.ILOAD, 3);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
getInternalClsName(LogCaptureStorage.class),
"capture", "(Ljava/io/FileDescriptor;[B" + (isWithOffset ? "II" : "") + ")V",
false);
public void visitEnd() {
insertCaptureCall(instructions, isWithOffset);
accept(superMethodVisitor);
}
};
}
Expand All @@ -88,4 +77,47 @@ public void visitCode() {
}
return null;
}

private static void insertCaptureCall(InsnList instructions, boolean isWithOffset) {
LineNumberNode firstLineNumber = findFirstLineNumber(instructions);
InsnList captureCall = createCaptureCall(firstLineNumber, isWithOffset);
instructions.insert(captureCall);
}

private static LineNumberNode findFirstLineNumber(InsnList instructions) {
for (AbstractInsnNode instruction = instructions.getFirst(); instruction != null; instruction = instruction.getNext()) {
if (instruction instanceof LineNumberNode) {
return (LineNumberNode) instruction;
}
}
return null;
}

/**
* Generates capture call bytecode with a line number mark (if <code>lineNumber</code> is not null).
* <p>
* It makes the instrumented code have a valid line number when a stack trace is collected.
*/
private static InsnList createCaptureCall(LineNumberNode lineNumber, boolean isWithOffset) {
InsnList instructions = new InsnList();
if (lineNumber != null) {
LabelNode labelNode = new LabelNode();
instructions.add(new LineNumberNode(lineNumber.line, labelNode));
instructions.add(labelNode);
}
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
instructions.add(new FieldInsnNode(Opcodes.GETFIELD,
"java/io/FileOutputStream",
"fd", "Ljava/io/FileDescriptor;"));
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
if (isWithOffset) {
instructions.add(new VarInsnNode(Opcodes.ILOAD, 2));
instructions.add(new VarInsnNode(Opcodes.ILOAD, 3));
}
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
getInternalClsName(LogCaptureStorage.class),
"capture", "(Ljava/io/FileDescriptor;[B" + (isWithOffset ? "II" : "") + ")V",
false));
return instructions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPInputStream;

Expand Down Expand Up @@ -121,39 +122,49 @@ public void keepsEventIdsAndOrderingAcrossMultipleFlushes() throws Exception {
}
}

private static DataInputStream openDump(int index) throws IOException {
static DataInputStream openDump(int index) throws IOException {
String output = LogCaptureStorage.outputWrittenDumpForTests.get(index);
return new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(output.getBytes(StandardCharsets.ISO_8859_1))));
}

private static void readAndCheckStdoutEvent(int expectedId, String expectedMsg, DataInputStream is) throws IOException {
static List<StackTraceElement> readAndCheckStdoutEvent(int expectedId, String expectedMsg, DataInputStream is) throws IOException {
assertEquals(expectedId, is.readLong());
assertEquals(LogCaptureStorage.Event.STD_OUTPUT_TYPE, is.readByte());
try (DataInputStream eis = new DataInputStream(new ByteArrayInputStream(readBytesWithSize(is)))) {
readAndCheckMessageAndStack(expectedMsg, eis);
return readAndCheckMessageAndStack(expectedMsg, eis);
}
}

private static void readAndCheckLoggingBreakpointEvent(int expectedId,
int expectedInstrumentationId,
String expectedMsg,
DataInputStream is) throws IOException {
private static List<StackTraceElement> readAndCheckLoggingBreakpointEvent(int expectedId,
int expectedInstrumentationId,
String expectedMsg,
DataInputStream is) throws IOException {
assertEquals(expectedId, is.readLong());
assertEquals(LogCaptureStorage.Event.LOGGING_BREAKPOINT_TYPE, is.readByte());
try (DataInputStream eis = new DataInputStream(new ByteArrayInputStream(readBytesWithSize(is)))) {
assertEquals(expectedInstrumentationId, eis.readInt());
readAndCheckMessageAndStack(expectedMsg, eis);
return readAndCheckMessageAndStack(expectedMsg, eis);
}
}

private static void readAndCheckMessageAndStack(String expectedMsg, DataInputStream is) throws IOException {
static List<StackTraceElement> readAndCheckMessageAndStack(String expectedMsg, DataInputStream is) throws IOException {
byte[] msgBytes = readBytesWithSize(is);
String msg = new String(msgBytes, StandardCharsets.UTF_8);
assertEquals(expectedMsg, msg);
assertTrue("expected encoded stack trace after message", is.available() > 0);
ArrayList<StackTraceElement> stack = new ArrayList<>();
while (is.available() > 0) {
boolean regularFrame = is.readBoolean();
if (regularFrame) {
stack.add(new StackTraceElement(is.readUTF(), is.readUTF(), null, is.readInt()));
} else {
stack.add(null);
}
}
return stack;
}

private static byte[] readBytesWithSize(DataInputStream is) throws IOException {
static byte[] readBytesWithSize(DataInputStream is) throws IOException {
// Performance is not critical, just do it in a loop missing Java 11 readNBytes().
int size = is.readInt();
byte[] bytes = new byte[size];
Expand All @@ -163,7 +174,7 @@ private static byte[] readBytesWithSize(DataInputStream is) throws IOException {
return bytes;
}

private static void resetLogCaptureStorage() {
static void resetLogCaptureStorage() {
LogCaptureStorage.EVENT_COUNTER.set(0);
LogCaptureStorage.LAST_FLUSHED_EVENT_ID.set(-1);
LogCaptureStorage.LAST_LOGGING_BREAKPOINT_EVENT_ID.set(-1);
Expand Down
Loading
Loading