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 @@ -30,6 +30,7 @@
import com.datadog.debugger.probe.Where;
import com.datadog.debugger.sink.Snapshot;
import com.datadog.debugger.util.ClassFileLines;
import com.datadog.debugger.util.JvmLanguage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

odd that this is the sole change in this file...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it used in this file and before it was an inner protected class from an inherited class (Instrumenter) so before no need an import.
not trivial, I agree 😁

import datadog.trace.api.Config;
import datadog.trace.bootstrap.debugger.Limits;
import datadog.trace.bootstrap.debugger.MethodLocation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.datadog.debugger.instrumentation.DiagnosticMessage.Kind;
import com.datadog.debugger.probe.ProbeDefinition;
import com.datadog.debugger.util.ClassFileLines;
import com.datadog.debugger.util.JvmLanguage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -69,7 +70,7 @@ public Instrumenter(
argOffset += t.getSize();
}
localVarsBySlotArray = extractLocalVariables(argTypes);
this.language = JvmLanguage.of(classNode);
this.language = JvmLanguage.of(classNode.sourceFile);
}

public abstract InstrumentationResult.Status instrument();
Expand Down Expand Up @@ -295,31 +296,4 @@ public FinallyBlock(LabelNode startLabel, LabelNode endLabel, LabelNode handlerL
this.handlerLabel = handlerLabel;
}
}

protected enum JvmLanguage {
JAVA,
KOTLIN,
SCALA,
GROOVY,
UNKNOWN;

public static JvmLanguage of(ClassNode classNode) {
if (classNode.sourceFile == null) {
return UNKNOWN;
}
if (classNode.sourceFile.endsWith(".java")) {
return JAVA;
}
if (classNode.sourceFile.endsWith(".kt")) {
return KOTLIN;
}
if (classNode.sourceFile.endsWith(".scala")) {
return SCALA;
}
if (classNode.sourceFile.endsWith(".groovy")) {
return GROOVY;
}
return UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.datadog.debugger.symbol;

import com.datadog.debugger.util.JvmLanguage;
import datadog.trace.agent.tooling.stratum.SourceMap;
import datadog.trace.agent.tooling.stratum.StratumExt;
import datadog.trace.api.Pair;

public interface SourceRemapper {

int remapSourceLine(int line);

static SourceRemapper getSourceRemapper(String sourceFile, SourceMap sourceMap) {
JvmLanguage jvmLanguage = JvmLanguage.of(sourceFile);
switch (jvmLanguage) {
case KOTLIN:
StratumExt stratum = sourceMap.getStratum("KotlinDebug");
if (stratum == null) {
throw new IllegalArgumentException("No stratum found for KotlinDebug");
}
return new KotlinSourceRemapper(stratum);
default:
return NOOP_REMAPPER;
}
}

SourceRemapper NOOP_REMAPPER = new NoopSourceRemapper();

class NoopSourceRemapper implements SourceRemapper {
@Override
public int remapSourceLine(int line) {
return line;
}
}

class KotlinSourceRemapper implements SourceRemapper {
private final StratumExt stratum;

public KotlinSourceRemapper(StratumExt stratum) {
this.stratum = stratum;
}

@Override
public int remapSourceLine(int line) {
Pair<String, Integer> pair = stratum.getInputLine(line);
if (pair == null || pair.getRight() == null) {
return line;
}
return pair.getRight();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import static com.datadog.debugger.instrumentation.ASMHelper.sortLocalVariables;

import com.datadog.debugger.instrumentation.ASMHelper;
import datadog.trace.agent.tooling.stratum.SourceMap;
import datadog.trace.agent.tooling.stratum.parser.Parser;
import datadog.trace.util.Strings;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -28,9 +30,11 @@
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SymbolExtractor {
private static final Logger LOGGER = LoggerFactory.getLogger(SymbolExtractor.class);

public static Scope extract(byte[] classFileBuffer, String jarName) {
ClassNode classNode = parseClassFile(classFileBuffer);
Expand All @@ -40,7 +44,16 @@ public static Scope extract(byte[] classFileBuffer, String jarName) {
private static Scope extractScopes(ClassNode classNode, String jarName) {
try {
String sourceFile = extractSourceFile(classNode);
List<Scope> methodScopes = extractMethods(classNode, sourceFile);
SourceRemapper sourceRemapper = SourceRemapper.NOOP_REMAPPER;
if (classNode.sourceDebug != null) {
List<SourceMap> sourceMaps = Parser.parse(classNode.sourceDebug);
if (sourceMaps.isEmpty()) {
throw new IllegalStateException("No source maps found for " + classNode.name);
}
SourceMap sourceMap = sourceMaps.get(0);
sourceRemapper = SourceRemapper.getSourceRemapper(classNode.sourceFile, sourceMap);
}
List<Scope> methodScopes = extractMethods(classNode, sourceFile, sourceRemapper);
int classStartLine = Integer.MAX_VALUE;
int classEndLine = 0;
for (Scope scope : methodScopes) {
Expand All @@ -67,9 +80,8 @@ private static Scope extractScopes(ClassNode classNode, String jarName) {
.scopes(new ArrayList<>(Collections.singletonList(classScope)))
.build();
} catch (Exception ex) {
LoggerFactory.getLogger(SymbolExtractor.class)
.debug(
"Extracting scopes for class[{}] in jar[{}] failed: ", classNode.name, jarName, ex);
LOGGER.debug(
"Extracting scopes for class[{}] in jar[{}] failed: ", classNode.name, jarName, ex);
return null;
}
}
Expand Down Expand Up @@ -102,10 +114,11 @@ private static List<Symbol> extractFields(ClassNode classNode) {
return fields;
}

private static List<Scope> extractMethods(ClassNode classNode, String sourceFile) {
private static List<Scope> extractMethods(
ClassNode classNode, String sourceFile, SourceRemapper sourceRemapper) {
List<Scope> methodScopes = new ArrayList<>();
for (MethodNode method : classNode.methods) {
MethodLineInfo methodLineInfo = extractMethodLineInfo(method);
MethodLineInfo methodLineInfo = extractMethodLineInfo(method, sourceRemapper);
List<Scope> varScopes = new ArrayList<>();
List<Symbol> methodSymbols = new ArrayList<>();
int localVarBaseSlot = extractArgs(method, methodSymbols, methodLineInfo.start);
Expand Down Expand Up @@ -464,7 +477,8 @@ static List<Scope.LineRange> buildRanges(List<Integer> sortedLineNo) {
return ranges;
}

private static MethodLineInfo extractMethodLineInfo(MethodNode methodNode) {
private static MethodLineInfo extractMethodLineInfo(
MethodNode methodNode, SourceRemapper sourceRemapper) {
Map<Label, Integer> map = new HashMap<>();
List<Integer> lineNo = new ArrayList<>();
Set<Integer> dedupSet = new HashSet<>();
Expand All @@ -473,10 +487,11 @@ private static MethodLineInfo extractMethodLineInfo(MethodNode methodNode) {
while (node != null) {
if (node.getType() == AbstractInsnNode.LINE) {
LineNumberNode lineNumberNode = (LineNumberNode) node;
if (dedupSet.add(lineNumberNode.line)) {
lineNo.add(lineNumberNode.line);
int newLine = sourceRemapper.remapSourceLine(lineNumberNode.line);
if (dedupSet.add(newLine)) {
lineNo.add(newLine);
}
maxLine = Math.max(lineNumberNode.line, maxLine);
maxLine = Math.max(newLine, maxLine);
}
if (node.getType() == AbstractInsnNode.LABEL) {
if (node instanceof LabelNode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.datadog.debugger.util;

public enum JvmLanguage {
JAVA,
KOTLIN,
SCALA,
GROOVY,
UNKNOWN;

public static JvmLanguage of(String sourceFile) {
if (sourceFile == null) {
return UNKNOWN;
}
if (sourceFile.endsWith(".java")) {
return JAVA;
}
if (sourceFile.endsWith(".kt")) {
return KOTLIN;
}
if (sourceFile.endsWith(".scala")) {
return SCALA;
}
if (sourceFile.endsWith(".groovy")) {
return GROOVY;
}
return UNKNOWN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,18 @@
import datadog.trace.bootstrap.debugger.ProbeId;
import datadog.trace.bootstrap.debugger.ProbeRateLimiter;
import datadog.trace.bootstrap.debugger.util.Redaction;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.config.Services;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -415,49 +403,4 @@ public void instrumentationResult(ProbeDefinition definition, InstrumentationRes
results.put(definition.getId(), result);
}
}

static class KotlinHelper {
public static Class<?> compileAndLoad(
String className, String sourceFileName, List<File> outputFilesToDelete) {
K2JVMCompiler compiler = new K2JVMCompiler();
K2JVMCompilerArguments args = compiler.createArguments();
args.setFreeArgs(Collections.singletonList(sourceFileName));
String compilerOutputDir = "/tmp/" + CapturedSnapshotTest.class.getSimpleName() + "-kotlin";
args.setDestination(compilerOutputDir);
args.setClasspath(System.getProperty("java.class.path"));
ExitCode exitCode =
compiler.exec(
new PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, true),
Services.EMPTY,
args);

if (exitCode.getCode() != 0) {
throw new RuntimeException("Kotlin compilation failed");
}
File compileOutputDirFile = new File(compilerOutputDir);
try {
URLClassLoader urlClassLoader =
new URLClassLoader(new URL[] {compileOutputDirFile.toURI().toURL()});
return urlClassLoader.loadClass(className);
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
registerFilesToDeleteDir(compileOutputDirFile, outputFilesToDelete);
}
}

public static void registerFilesToDeleteDir(File dir, List<File> outputFilesToDelete) {
if (!dir.exists()) {
return;
}
try {
Files.walk(dir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(outputFilesToDelete::add);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.datadog.debugger.agent;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.config.Services;

public class KotlinHelper {
public static Class<?> compileAndLoad(
String className, String sourceFileName, List<File> outputFilesToDelete) {
K2JVMCompiler compiler = new K2JVMCompiler();
K2JVMCompilerArguments args = compiler.createArguments();
args.setFreeArgs(Collections.singletonList(sourceFileName));
String compilerOutputDir = "/tmp/" + CapturedSnapshotTest.class.getSimpleName() + "-kotlin";
args.setDestination(compilerOutputDir);
args.setClasspath(System.getProperty("java.class.path"));
ExitCode exitCode =
compiler.exec(
new PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, true),
Services.EMPTY,
args);

if (exitCode.getCode() != 0) {
throw new RuntimeException("Kotlin compilation failed");
}
File compileOutputDirFile = new File(compilerOutputDir);
try {
URLClassLoader urlClassLoader =
new URLClassLoader(new URL[] {compileOutputDirFile.toURI().toURL()});
return urlClassLoader.loadClass(className);
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
registerFilesToDeleteDir(compileOutputDirFile, outputFilesToDelete);
}
}

public static void registerFilesToDeleteDir(File dir, List<File> outputFilesToDelete) {
if (!dir.exists()) {
return;
}
try {
Files.walk(dir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(outputFilesToDelete::add);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Loading
Loading