Skip to content

Commit 23c4f00

Browse files
committed
WIP: Validator
1 parent a8b33d5 commit 23c4f00

File tree

5 files changed

+109
-18
lines changed

5 files changed

+109
-18
lines changed

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/ExceptionHandlers.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import datadog.trace.api.ProductActivation;
55
import datadog.trace.bootstrap.ExceptionLogger;
66
import net.bytebuddy.ClassFileVersion;
7-
import net.bytebuddy.asm.Advice.ExceptionHandler;
7+
import net.bytebuddy.asm.Advice;
88
import net.bytebuddy.implementation.Implementation;
99
import net.bytebuddy.implementation.bytecode.StackManipulation;
1010
import net.bytebuddy.jar.asm.Label;
@@ -20,8 +20,8 @@ public class ExceptionHandlers {
2020
// Bootstrap ExceptionHandler.class will always be resolvable, so we'll use it in the log name
2121
private static final String HANDLER_NAME = ExceptionLogger.class.getName().replace('.', '/');
2222

23-
private static final ExceptionHandler EXCEPTION_STACK_HANDLER =
24-
new ExceptionHandler.Simple(
23+
private static final Advice.ExceptionHandler EXCEPTION_STACK_HANDLER =
24+
new Advice.ExceptionHandler.Simple(
2525
new StackManipulation() {
2626
// Pops one Throwable off the stack. Maxes the stack to at least 3.
2727
private final Size size = new StackManipulation.Size(-1, 3);
@@ -131,7 +131,7 @@ public Size apply(final MethodVisitor mv, final Implementation.Context context)
131131
}
132132
});
133133

134-
public static ExceptionHandler defaultExceptionHandler() {
134+
public static Advice.ExceptionHandler defaultExceptionHandler() {
135135
return EXCEPTION_STACK_HANDLER;
136136
}
137137
}

dd-java-agent/instrumentation/aws-lambda-handler/src/main/java/datadog/trace/instrumentation/aws/v1/lambda/LambdaHandlerInstrumentation.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
66
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
77
import static datadog.trace.instrumentation.aws.v1.lambda.LambdaHandlerDecorator.INVOCATION_SPAN_NAME;
8-
import static net.bytebuddy.asm.Advice.Enter;
9-
import static net.bytebuddy.asm.Advice.OnMethodEnter;
10-
import static net.bytebuddy.asm.Advice.OnMethodExit;
11-
import static net.bytebuddy.asm.Advice.Origin;
12-
import static net.bytebuddy.asm.Advice.This;
138
import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
149
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1510
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@@ -82,11 +77,11 @@ public void methodAdvice(MethodTransformer transformer) {
8277
}
8378

8479
public static class ExtensionCommunicationAdvice {
85-
@OnMethodEnter
80+
@Advice.OnMethodEnter
8681
static AgentScope enter(
87-
@This final Object that,
82+
@Advice.This final Object that,
8883
@Advice.Argument(0) final Object event,
89-
@Origin("#m") final String methodName) {
84+
@Advice.Origin("#m") final String methodName) {
9085

9186
if (CallDepthThreadLocalMap.incrementCallDepth(RequestHandler.class) > 0) {
9287
return null;
@@ -103,10 +98,10 @@ static AgentScope enter(
10398
return scope;
10499
}
105100

106-
@OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
101+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
107102
static void exit(
108-
@Origin String method,
109-
@Enter final AgentScope scope,
103+
@Advice.Origin String method,
104+
@Advice.Enter final AgentScope scope,
110105
@Advice.Return(typing = DYNAMIC) final Object result,
111106
@Advice.Thrown final Throwable throwable) {
112107

dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/CodeOriginInstrumentation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package datadog.trace.instrumentation.codeorigin;
22

33
import datadog.trace.agent.tooling.Instrumenter;
4-
import datadog.trace.agent.tooling.InstrumenterModule.Tracing;
4+
import datadog.trace.agent.tooling.InstrumenterModule;
55
import datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers;
66
import datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers;
77
import datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.OneOf;
@@ -12,7 +12,7 @@
1212
import net.bytebuddy.description.type.TypeDescription;
1313
import net.bytebuddy.matcher.ElementMatcher;
1414

15-
public abstract class CodeOriginInstrumentation extends Tracing
15+
public abstract class CodeOriginInstrumentation extends InstrumenterModule.Tracing
1616
implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice {
1717

1818
private final OneOf<NamedElement> matcher;

dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceConfigInstrumentation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public List<Instrumenter> typeInstrumentations() {
7373
}
7474

7575
// Not Using AutoService to hook up this instrumentation
76-
public static class TracerClassInstrumentation implements ForTypeHierarchy, HasMethodAdvice {
76+
public static class TracerClassInstrumentation implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice {
7777
private final String className;
7878
private final Set<String> methodNames;
7979

validate.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import javalang
2+
import os
3+
import re
4+
5+
6+
# Helper function to validate kebab-case
7+
def is_kebab_case(name):
8+
return re.match(r'^[a-z0-9]+(-[a-z0-9.]+)*$', name) is not None
9+
10+
def find_java_files(directory):
11+
java_files = []
12+
for root, _, files in os.walk(directory):
13+
for file in files:
14+
if file.endswith(".java"):
15+
java_files.append(os.path.join(root, file))
16+
return java_files
17+
18+
def parse_java_file(file_path):
19+
with open(file_path, 'r', encoding='utf-8') as file:
20+
return javalang.parse.parse(file.read())
21+
22+
def validate_inheritance(tree, file_name):
23+
issues = []
24+
for path, node in tree:
25+
if isinstance(node, javalang.tree.ClassDeclaration):
26+
# Skip static inner classes
27+
if any(modifier for modifier in node.modifiers if modifier == "static") and '.' in node.name:
28+
continue
29+
30+
if node.name.endswith("Decorator"):
31+
if not node.extends or "Decorator" not in node.extends.name:
32+
issues.append(f"{file_name}: Class '{node.name}' should extend a class with 'Decorator' in its name.")
33+
elif node.extends and "Decorator" in node.extends.name:
34+
if not node.name.endswith("Decorator"):
35+
issues.append(f"{file_name}: Class '{node.name}' extends a 'Decorator' class but does not end with 'Decorator'.")
36+
37+
if node.name.endswith("Instrumentation"):
38+
# should extend something
39+
if not node.extends and not node.implements:
40+
issues.append(f"{file_name}: Class '{node.name}' should extend or implement a class with 'Instrument' in its name.")
41+
# if it extends something, it should extend an instrument
42+
elif not ((node.extends and "Instrument" in node.extends.name) or (node.implements and any("Instrument" in impl.name for impl in node.implements))):
43+
issues.append(f"{file_name}: Class '{node.name}' should extend a class with 'Instrument' in its name.")
44+
elif node.extends and "Instrument" in node.extends.name:
45+
if not node.name.endswith("Instrumentation"):
46+
issues.append(f"{file_name}: Class '{node.name}' extends a 'Instrument' class but does not end with 'Instrumentation'.")
47+
elif node.implements and any("Instrument" in impl for impl in node.implements):
48+
if not node.name.endswith("Instrumentation"):
49+
issues.append(f"{file_name}: Class '{node.name}' implements a 'Instrument' class but does not end with 'Instrumentation'.")
50+
51+
if node.name.endswith("Advice"):
52+
# Check for methods with an @Advice annotation
53+
if not any(
54+
isinstance(member, javalang.tree.MethodDeclaration) and
55+
any(anno.name.startswith("Advice") for anno in member.annotations)
56+
for member in node.body
57+
):
58+
issues.append(f"{file_name}: Class '{node.name}' does not have a method tagged with an @Advice annotation.")
59+
return issues
60+
61+
def validate_project_structure(base_directory):
62+
issues = []
63+
64+
# Check kebab-case for subdirectories
65+
instrumentation_path = os.path.join(base_directory, "dd-java-agent", "instrumentation")
66+
if not os.path.exists(instrumentation_path):
67+
issues.append(f"Path '{instrumentation_path}' does not exist.")
68+
return issues
69+
70+
for subdir in os.listdir(instrumentation_path):
71+
if os.path.isdir(os.path.join(instrumentation_path, subdir)):
72+
if not is_kebab_case(subdir):
73+
issues.append(f"Subdirectory '{subdir}' is not in kebab-case.")
74+
75+
# Validate Java files in subdirectories
76+
for subdir in os.listdir(instrumentation_path):
77+
subdir_path = os.path.join(instrumentation_path, subdir)
78+
if os.path.isdir(subdir_path):
79+
java_files = find_java_files(os.path.join(subdir_path, "src", "main", "java"))
80+
for java_file in java_files:
81+
try:
82+
tree = parse_java_file(java_file)
83+
issues.extend(validate_inheritance(tree, java_file))
84+
except (javalang.parser.JavaSyntaxError, UnicodeDecodeError):
85+
issues.append(f"Could not parse Java file '{java_file}'.")
86+
87+
return issues
88+
89+
if __name__ == "__main__":
90+
base_directory = "." # Change this to the root directory of your project
91+
problems = validate_project_structure(base_directory)
92+
93+
if problems:
94+
print("\n".join(problems))
95+
else:
96+
print("All checks passed!")

0 commit comments

Comments
 (0)