Skip to content

Commit 8d74baa

Browse files
Anuraag Agrawaltrask
Anuraag Agrawal
andauthored
Run tests with javaagent. (#1643)
Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 5b2e4ce commit 8d74baa

File tree

193 files changed

+2790
-1849
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+2790
-1849
lines changed

.github/scripts/deadlock-detector.sh

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
3+
while true
4+
do
5+
sleep 60
6+
file="/tmp/deadlock-detector-$(date +"%Y-%m-%d-%H-%M-%S").txt"
7+
for pid in $(jps | grep -v Jps | awk '{ print $1 }')
8+
do
9+
jcmd $pid VM.command_line >> $file
10+
jcmd $pid Thread.print >> $file
11+
if jcmd $pid Thread.print | grep -q SimpleLogger
12+
then
13+
# check once more to eliminate most of the sporadic finds
14+
if jcmd $pid Thread.print | grep -q SimpleLogger
15+
then
16+
jcmd $pid GC.heap_dump /tmp/deadlock-detector-$pid.hprof
17+
fi
18+
fi
19+
done
20+
done &

.github/workflows/pr.yaml

+23-13
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@ jobs:
1818
with:
1919
job-id: jdk11
2020

21+
- name: Start deadlock detector
22+
run: .github/scripts/deadlock-detector.sh
23+
2124
- name: Build
22-
uses: nick-invision/[email protected]
25+
run: ./gradlew check --stacktrace -x :smoke-tests:test
26+
27+
- name: Upload deadlock detector artifacts
28+
if: always()
29+
uses: actions/upload-artifact@v2
2330
with:
24-
command: ./gradlew check --stacktrace -x :smoke-tests:test
25-
timeout_minutes: 90
26-
max_attempts: 3
31+
name: deadlock-detector-build
32+
path: /tmp/deadlock-detector-*
33+
if-no-files-found: ignore
2734

2835
test:
2936
runs-on: ubuntu-latest
@@ -49,12 +56,19 @@ jobs:
4956
with:
5057
job-id: jdk${{ matrix.java }}
5158

59+
- name: Start deadlock detector
60+
run: .github/scripts/deadlock-detector.sh
61+
5262
- name: Test
53-
uses: nick-invision/[email protected]
63+
run: ./gradlew test -PtestJavaVersion=${{ matrix.java }} --stacktrace -x :smoke-tests:test -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} -Porg.gradle.java.installations.auto-download=false
64+
65+
- name: Upload deadlock detector artifacts
66+
if: always()
67+
uses: actions/upload-artifact@v2
5468
with:
55-
command: ./gradlew test -PtestJavaVersion=${{ matrix.java }} --stacktrace -x :smoke-tests:test -Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }} -Porg.gradle.java.installations.auto-download=false
56-
timeout_minutes: 90
57-
max_attempts: 3
69+
name: deadlock-detector-test-${{ matrix.java }}
70+
path: /tmp/deadlock-detector-*
71+
if-no-files-found: ignore
5872

5973
smoke-test:
6074
runs-on: ubuntu-latest
@@ -72,11 +86,7 @@ jobs:
7286
job-id: smokeTests
7387

7488
- name: Test
75-
uses: nick-invision/[email protected]
76-
with:
77-
command: ./gradlew :smoke-tests:test
78-
timeout_minutes: 90
79-
max_attempts: 3
89+
run: ./gradlew :smoke-tests:test
8090

8191
setup-muzzle-matrix:
8292
runs-on: ubuntu-latest

CONTRIBUTING.md

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ See [Writing instrumentation](docs/contributing/writing-instrumentation.md)
5454

5555
See [Understanding the javaagent components](docs/contributing/javaagent-jar-components.md)
5656

57+
### Understanding the javaagent instrumentation testing components
58+
59+
See [Understanding the javaagent instrumentation testing components](docs/contributing/javaagent-test-infra.md)
60+
5761
### Debugging
5862

5963
See [Debugging](docs/contributing/debugging.md)

buildSrc/build.gradle

-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ gradlePlugin {
2121
id = "muzzle"
2222
implementationClass = "MuzzlePlugin"
2323
}
24-
create("javaagent-instrumentation-plugin") {
25-
id = "io.opentelemetry.javaagent.instrumentation-instrumentation"
26-
implementationClass = "io.opentelemetry.instrumentation.gradle.AutoInstrumentationPlugin"
27-
}
2824
}
2925
}
3026

buildSrc/src/main/java/io/opentelemetry/instrumentation/gradle/AutoInstrumentationPlugin.java

-61
This file was deleted.

docs/contributing/javaagent-jar-components.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
### Understanding the javaagent components
22

3-
OpenTelemetry Auto Instrumentation java agent's jar can logically be divided
4-
into 3 parts:
3+
The javaagent jar can logically be divided into 3 parts:
54

65
* Modules that live in the system class loader
76
* Modules that live in the bootstrap class loader
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
### Understanding the javaagent instrumentation testing components
2+
3+
Javaagent instrumentation tests are run using a fully shaded `-javaagent` in order to perform
4+
the same bytecode instrumentation as when the agent is run against a normal app.
5+
6+
There are a few key components that make this possible, described below.
7+
8+
### gradle/instrumentation.gradle
9+
10+
* shades the instrumentation
11+
* adds jvm args to the test configuration
12+
* -javaagent:[agent for testing]
13+
* -Dotel.initializer.jar=[shaded instrumentation jar]
14+
15+
The `otel.initializer.jar` property is used to load the shaded instrumentation jar into the
16+
`AgentClassLoader`, so that the javaagent jar doesn't need to be re-built each time.
17+
18+
### :testing:agent-exporter
19+
20+
This contains the span and metric exporters that are used.
21+
22+
These are in-memory exporters, so that the tests can verify the spans and metrics being exported.
23+
24+
These exporters and the in-memory data live in the `AgentClassLoader`, so tests must access them
25+
using reflection. To simplify this, they store the in-memory data using the OTLP protobuf objects,
26+
so that they can be serialized into byte arrays inside the `AgentClassLoader`, then passed back
27+
to the tests and deserialized inside their class loader where they can be verified. The
28+
`:testing-common` module (described below) hides this complexity from instrumentation test authors.
29+
30+
### :agent-for-testing
31+
32+
This is a custom distro of the javaagent that embeds the `:testing:agent-exporter`.
33+
34+
### :testing-common
35+
36+
This module provides methods to help verify the span and metric data produced by the
37+
instrumentation, hiding the complexity of accessing the in-memory exporters that live in the
38+
`AgentClassLoader`.

docs/contributing/writing-instrumentation.md

+33-5
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ only depend on the OpenTelemetry API, `instrumentation-common`, and the instrume
7777
[instrumentation-library.gradle](../../gradle/instrumentation-library.gradle) needs to be applied to
7878
configure build tooling for the library.
7979

80-
## Writing unit tests
80+
## Writing instrumentation tests
8181

82-
Once the instrumentation is completed, we add unit tests to the `testing` module. Tests will
82+
Once the instrumentation is completed, we add tests to the `testing` module. Tests will
8383
generally apply to both library and agent instrumentation, with the only difference being how a client
8484
or server is initialized. In a library test, there will be code calling into the instrumentation API,
85-
while in an agent test, it will generally just use the underlying library's API as is. Create unit tests in an
85+
while in an agent test, it will generally just use the underlying library's API as is. Create tests in an
8686
abstract class with an abstract method that returns an instrumented object like a client. The class
8787
should itself extend from `InstrumentationSpecification` to be recognized by Spock and include helper
8888
methods for assertions.
@@ -100,11 +100,11 @@ Now that we have working instrumentation, we can implement agent instrumentation
100100
do not have to modify their apps to use it. Make sure the `javaagent` submodule has a dependency on the
101101
`library` submodule and a test dependency on the `testing` submodule. Agent instrumentation defines
102102
classes to match against to generate bytecode for. You will often match against the class you used
103-
in the unit test for library instrumentation, for example the builder of a client. And then you could
103+
in the test for library instrumentation, for example the builder of a client. And then you could
104104
match against the method that creates the builder, for example its constructor. Agent instrumentation
105105
can inject byte code to be run after the constructor returns, which would invoke e.g.,
106106
`registerInterceptor` and initialize the instrumentation. Often, the code inside the byte code
107-
decorator will be identical to the one in the unit test you wrote above - the agent does the work for
107+
decorator will be identical to the one in the test you wrote above - the agent does the work for
108108
initializing the instrumentation library, so a user doesn't have to.
109109

110110
With that written, let's add tests for the agent instrumentation. We basically want to ensure that
@@ -113,6 +113,34 @@ the base class you wrote earlier, but in this, create a client using none of the
113113
only the ones offered by the library. Implement the `AgentTestRunner` trait for common setup logic,
114114
and try running. All the tests should pass for agent instrumentation too.
115115

116+
Note that all the tests inside the `javaagent` module will be run using the shaded `-javaagent`
117+
in order to perform the same bytecode instrumentation as when the agent is run against a normal app.
118+
This means that the javaagent instrumentation will be inside the javaagent (inside of the
119+
`AgentClassLoader`) and will not be directly accessible to your test code. See the next section in
120+
case you need to write unit tests that directly access the javaagent instrumentation.
121+
122+
## Writing Java agent unit tests
123+
124+
As mentioned above, tests in the `javaagent` module cannot access the javaagent instrumentation
125+
classes directly.
126+
127+
Ideally javaagent instrumentation is just a thin wrapper over library instrumentation, and so there
128+
is no need to write unit tests that directly access the javaagent instrumentation classes.
129+
130+
If you still want to write a unit test against javaagent instrumentation, add another module
131+
named `javaagent-unittests`. Continuing with the example above:
132+
133+
```
134+
instrumentation ->
135+
...
136+
yarpc-1.0 ->
137+
javaagent
138+
yarpc-1.0-javaagent.gradle
139+
javaagent-unittest
140+
yarpc-1.0-javaagent-unittest.gradle
141+
...
142+
```
143+
116144
### Java agent instrumentation gotchas
117145

118146
#### Calling Java 8 default methods from advice

docs/safety-mechanisms.md

+9-14
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,15 @@ affecting it negatively, for example introducing crashes.
66

77
## Instrumentation tests
88

9-
All instrumentation are written with instrumentation tests - these can be considered
10-
the unit tests of this project. Instrumentation tests invoke bytecode manipulation to
11-
actually rewrite classes similar to how the agent would in a normal app. By then
12-
exercising the instrumented library in a way a user would, for example by issuing
13-
requests from an HTTP client, we can assert on the spans that should be generated, including
14-
their semantic attributes. A problem in the instrumentation will generally cause
15-
spans to be reported incorrectly or not reported at all, and we can find these situations
16-
with the instrumentation tests.
17-
18-
Note: the instrumentation tests have significant differences from when run against a
19-
normal app with `-javaagent`, in particular, our usual shading is not applied which
20-
can cause false positives. We have work in progress to actually run tests with `-javaagent`
21-
after applying our shading, in which case these tests will run almost identically to
22-
a normal app and eliminate false positives. https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/1643
9+
All instrumentation are written with instrumentation tests - these can be considered the unit tests
10+
of this project.
11+
12+
Instrumentation tests are run using a fully shaded `-javaagent` in order to perform the same bytecode
13+
instrumentation as when the agent is run against a normal app.
14+
By then exercising the instrumented library in a way a user would, for example by issuing requests
15+
from an HTTP client, we can assert on the spans that should be generated, including their semantic
16+
attributes. A problem in the instrumentation will generally cause spans to be reported incorrectly
17+
or not reported at all, and we can find these situations with the instrumentation tests.
2318

2419
## Latest dep tests
2520

examples/distro/instrumentation/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ shadowJar {
5353
relocate "io.opentelemetry.spi", "io.opentelemetry.javaagent.shaded.io.opentelemetry.spi"
5454
relocate "io.opentelemetry.context", "io.opentelemetry.javaagent.shaded.io.opentelemetry.context"
5555

56+
// relocate OpenTelemetry extensions
57+
relocate "io.opentelemetry.extension.kotlin", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.kotlin"
58+
relocate "io.opentelemetry.extension.trace.propagation", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.trace.propagation"
5659
}

gradle/dependencies.gradle

+21-19
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,28 @@ configurations.all {
88

99
ext {
1010
versions = [
11-
opentelemetry : '0.13.1',
12-
opentelemetryAlpha : "0.13.1-alpha",
11+
opentelemetry : '0.13.1',
12+
opentelemetryAlpha: "0.13.1-alpha",
1313

14-
slf4j : "1.7.30",
15-
guava : "20.0", // Last version to support Java 7
14+
slf4j : "1.7.30",
15+
guava : "20.0", // Last version to support Java 7
1616

17-
spock : "1.3-groovy-$spockGroovyVer",
18-
groovy : groovyVer,
19-
logback : "1.2.3",
20-
bytebuddy : "1.10.18", // Also explicitly specified in buildSrc
21-
scala : "2.11.12", // Last version to support Java 7 (2.12+ require Java 8+)
22-
kotlin : "1.4.0",
23-
coroutines : "1.3.0",
24-
springboot : "2.3.1.RELEASE",
17+
spock : "1.3-groovy-$spockGroovyVer",
18+
groovy : groovyVer,
19+
logback : "1.2.3",
20+
bytebuddy : "1.10.18", // Also explicitly specified in buildSrc
21+
scala : "2.11.12", // Last version to support Java 7 (2.12+ require Java 8+)
22+
kotlin : "1.4.0",
23+
coroutines : "1.3.0",
24+
springboot : "2.3.1.RELEASE",
2525
// TODO(anuraaga): Switch off of milestones, this version fixes compatibility with Spock Unroll
26-
junit5 : "5.7.0-M1",
27-
checkerFramework : "3.6.1",
28-
errorprone : "2.4.0",
29-
nullaway : "0.8.0",
30-
autoValue : "1.7.4",
31-
systemLambda : "1.1.0",
32-
prometheus : "0.9.0"
26+
junit5 : "5.7.0-M1",
27+
checkerFramework : "3.6.1",
28+
errorprone : "2.4.0",
29+
nullaway : "0.8.0",
30+
autoValue : "1.7.4",
31+
systemLambda : "1.1.0",
32+
prometheus : "0.9.0"
3333
]
3434

3535
deps = [
@@ -40,6 +40,7 @@ ext {
4040
opentelemetryKotlin : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-kotlin', version: versions.opentelemetry),
4141
opentelemetryTraceProps : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-trace-propagators', version: versions.opentelemetry),
4242
opentelemetrySdk : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk', version: versions.opentelemetry),
43+
opentelemetrySdkMetrics : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk-metrics', version: versions.opentelemetryAlpha),
4344
opentelemetryJaeger : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporter-jaeger', version: versions.opentelemetry),
4445
opentelemetryJaegerThrift : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporter-jaeger-thrift', version: versions.opentelemetry),
4546
opentelemetryOtlp : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporter-otlp', version: versions.opentelemetry),
@@ -49,6 +50,7 @@ ext {
4950
opentelemetryLogging : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporter-logging', version: versions.opentelemetry),
5051
opentelemetryProto : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-proto', version: versions.opentelemetry),
5152
opentelemetryResources : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk-extension-resources', version: versions.opentelemetry),
53+
opentelemetrySdkTesting : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk-testing', version: versions.opentelemetry),
5254

5355
// General
5456
slf4j : "org.slf4j:slf4j-api:${versions.slf4j}",

0 commit comments

Comments
 (0)