Skip to content

Commit

Permalink
Merge branch 'master' into alejandro.gonzalez/session_rewriting_detec…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
jandro996 authored Mar 12, 2024
2 parents eba1968 + 0d49c12 commit 7456e4f
Show file tree
Hide file tree
Showing 145 changed files with 6,794 additions and 1,713 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main

- name: Build dd-trace-java for creating the CodeQL database
run: JAVA_HOME=$JAVA_HOME_8_X64 JAVA_8_HOME=$JAVA_HOME_8_X64 JAVA_11_HOME=$JAVA_HOME_11_X64 JAVA_17_HOME=$JAVA_HOME_17_X64 ./gradlew clean :dd-java-agent:shadowJar --build-cache --parallel --stacktrace --no-daemon --max-workers=8
run: JAVA_HOME=$JAVA_HOME_8_X64 JAVA_8_HOME=$JAVA_HOME_8_X64 JAVA_11_HOME=$JAVA_HOME_11_X64 JAVA_17_HOME=$JAVA_HOME_17_X64 JAVA_21_HOME=$JAVA_HOME_21_X64 ./gradlew clean :dd-java-agent:shadowJar --build-cache --parallel --stacktrace --no-daemon --max-workers=8

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@1a927e9307bc11970b2c679922ebc4d03a5bd980 # 1.0.31
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/trivy-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Build and publish artifacts locally
run: |
GRADLE_OPTS="-Dorg.gradle.jvmargs='-Xmx2G -Xms2G'" JAVA_HOME=$JAVA_HOME_8_X64 JAVA_8_HOME=$JAVA_HOME_8_X64 JAVA_11_HOME=$JAVA_HOME_11_X64 JAVA_17_HOME=$JAVA_HOME_17_X64 ./gradlew clean publishToMavenLocal --build-cache --parallel --stacktrace --no-daemon --max-workers=4
GRADLE_OPTS="-Dorg.gradle.jvmargs='-Xmx2G -Xms2G'" JAVA_HOME=$JAVA_HOME_8_X64 JAVA_8_HOME=$JAVA_HOME_8_X64 JAVA_11_HOME=$JAVA_HOME_11_X64 JAVA_17_HOME=$JAVA_HOME_17_X64 JAVA_21_HOME=$JAVA_HOME_21_X64 ./gradlew clean publishToMavenLocal --build-cache --parallel --stacktrace --no-daemon --max-workers=4
- name: Copy published artifacts
run: |
Expand Down
172 changes: 45 additions & 127 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Contributing

Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new)
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review.
Pull requests for bug fixes are welcome, but before submitting new features or changes to current
functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new)
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for
review.

When opening a pull request, please open it as a [draft](https://github.blog/2019-02-14-introducing-draft-pull-requests/) to not auto assign reviewers before you feel the pull request is in a reviewable state.
When opening a pull request, please open it as
a [draft](https://github.blog/2019-02-14-introducing-draft-pull-requests/) to not auto assign reviewers before you feel
the pull request is in a reviewable state.

## Requirements

Expand All @@ -22,150 +26,57 @@ To build the full project:

MacOS users, remember that `/usr/libexec/java_home` may control which JDK is in your path.

In contrast to the [IntelliJ IDEA setup](#intellij-idea) the default JVM to build and run tests from the command line should be Java 8.
In contrast to the [IntelliJ IDEA set up](#intellij-idea) the default JVM to build and run tests from the command line
should be Java 8.

There is no Oracle JDK v8 for ARM. ARM users might want to use [Azul's Zulu](/Users/albert.cintora/go/src/github.com/DataDog/dd-trace-java/dd-java-agent/instrumentation/build.gradle) builds of Java 8. On MacOS, they can be installed using `brew tap homebrew/cask-versions && brew install --cask zulu8`. [Amazon Corretto](https://aws.amazon.com/corretto/) builds have also been proven to work.
There is no Oracle JDK v8 for ARM. ARM users might want to
use [Azul's Zulu](https://www.azul.com/downloads/?version=java-8-lts&architecture=arm-64-bit&package=jdk#zulu)
builds of Java 8. On macOS, they can be installed
using `brew tap homebrew/cask-versions && brew install --cask zulu8`. [Amazon Corretto](https://aws.amazon.com/corretto/)
builds have also been proven to work.

# Building

To build the project without running tests run:

```bash
./gradlew clean assemble
```

To build the entire project with tests (this can take a very long time) run:

```bash
./gradlew clean build
```

# Adding Instrumentations

All instrumentations are in the directory `/dd-java-agent/instrumentation/$framework?/$framework-$minVersion`, where `$framework` is the framework name, and `$minVersion` is the minimum version of the framework supported by the instrumentation.
In some cases, such as [Hibernate](https://github.com/DataDog/dd-trace-java/tree/master/dd-java-agent/instrumentation/hibernate), there is a submodule containing different version-specific instrumentations, but typically a version-specific module is enough when there is only one instrumentation implemented (e.g. [Akka-HTTP](https://github.com/DataDog/dd-trace-java/tree/master/dd-java-agent/instrumentation/akka-http-10.0)).
When adding an instrumentation to `/dd-java-agent/instrumentation/$framework?/$framework-$minVersion`, an include must be added to [`settings.gradle`](https://github.com/DataDog/dd-trace-java/blob/master/settings.gradle):

```groovy
include ':dd-java-agent:instrumentation:$framework?:$framework-$minVersion'
```

Note that the includes are maintained in alphabetical order.

An instrumentation consists of the following components:

* An _Instrumentation_
* Must implement `Instrumenter` - note that it is recommended to implement `Instrumenter.Default` in every case.
* The instrumentation must be annotated with `@AutoService(Instrumenter.class)` for annotation processing.
* The instrumentation must declare a type matcher by implementing the method `typeMatcher()`, which matches the types the instrumentation will transform.
* The instrumentation must declare every class it needs to load (except for Datadog bootstrap classes, and for the framework itself) in `helperClassNames()`.
It is recommended to keep the number of classes to a minimum both to reduce deployment size and optimise startup time.
* If state must be associated with instances of framework types, a definition of the _context store_ by implementing `contextStore()`.
* The method `transformers()`: this is a map of method matchers to the Bytebuddy advice class which handles matching methods.
For example, if you want to inject an instance of `com.foo.Foo` into a `com.bar.Bar` (or fall back to a weak map backed association if this is impossible) you would return `singletonMap("com.foo.Foo", "com.bar.Bar")`.
It may be tempting to write `Foo.class.getName()`, but this will lead to the class being loaded during bootstrapping, which is usually not safe.
See the section on the [context store](#context-store) for more details.
* A _Decorator_.
* This will typically extend one of decorator [implementations](https://github.com/DataDog/dd-trace-java/tree/master/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator), which provide templates for span enrichment behaviour.
For example, all instrumentations for HTTP server frameworks have decorators which extend [`HttpServerDecorator`](https://github.com/DataDog/dd-trace-java/blob/master/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java).
* The name of this class must be included in the instrumentation's helper class names, as it will need to be loaded with the instrumentation.
* _Advice_
* Snippets of code to be inserted at the entry or exit of a method.
* Associated with the methods they apply to by the instrumentation's `transformers()` method.
* Any more classes required to implement the instrumentation, which must be included in the instrumentation's helper class names.

## Verifying Instrumentations

There are four verification strategies, three of which are mandatory.

### Muzzle directive
A _muzzle directive_ which checks for a range of framework versions that it would be safe to load the instrumentation.
At the top of the instrumentation's gradle file, the following would be added (see [rediscala](https://github.com/DataDog/dd-trace-java/blob/master/dd-java-agent/instrumentation/rediscala-1.8.0/rediscala-1.8.0.gradle))
```groovy
muzzle {
pass {
group = "com.github.etaty"
module = "rediscala_2.11"
versions = "[1.5.0,)"
assertInverse = true
}
pass {
group = "com.github.etaty"
module = "rediscala_2.12"
versions = "[1.8.0,)"
assertInverse = true
}
}
```
This means that the instrumentation should be safe with `rediscala_2.11` from version `1.5.0` and all later versions, but should fail (and so will not be loaded), for older versions (see `assertInverse`).
A similar range of versions is specified for `rediscala_2.12`.
When the agent is built, the muzzle plugin will download versions of the framework and check these directives hold.
To run muzzle on your instrumentation, run:

```groovy
./gradlew :dd-java-agent:instrumentation:rediscala-1.8.0:muzzle
```
* ⚠️ Muzzle does _not_ run tests.
It checks that the types and methods used by the instrumentation are present in particular versions of libraries.
It can be subverted with `MethodHandle` and reflection, so muzzle passing is not the end of the story.
See [Adding a New Instrumentation](./docs/add_new_instrumentation.md) for instructions on adding a new instrumentation.

### Instrumentation Tests

Tests are written in Groovy using the [Spock framework](http://spockframework.org/).
For instrumentations, `AgentTestRunner` must be extended by the test fixture.
For e.g. HTTP server frameworks, there are base tests which enforce consistency between different implementations - see [HttpServerTest](https://github.com/DataDog/dd-trace-java/blob/master/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpServerTest.groovy)

When writing an instrumentation it is much faster to test just the instrumentation rather than build the entire project, for example:

```bash
./gradlew :dd-java-agent:instrumentation:play-ws-2.1:test
```

### Latest Dependency Tests

Adding a directive to the build file lets us get early warning when breaking changes are released by framework maintainers.
For example, for Play 2.4, based on the following:

```groovy
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java_2.11', version: '2.5.+'
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java-ws_2.11', version: '2.5.+'
latestDepTestCompile(group: 'com.typesafe.play', name: 'play-test_2.11', version: '2.5.+') {
exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'
}
```

We download the latest dependency and run tests against it.

### Smoke tests

These are tests which run with a real agent jar file set as the `javaagent`.
See [here](https://github.com/DataDog/dd-trace-java/tree/master/dd-smoke-tests).
These are optional and not all frameworks have these, but contributions are very welcome.
See [How Instrumentations Work](./docs/how_instrumentations_work.md) for a deep dive into how instrumentations work.

# Automatic code formatting

This project includes a `.editorconfig` file for basic editor settings. This file is supported by most common text editors.
This project includes a `.editorconfig` file for basic editor settings. This file is supported by most common text
editors.

We have automatic code formatting enabled in Gradle configuration using [Spotless](https://github.com/diffplug/spotless)
[Gradle plugin](https://github.com/diffplug/spotless/tree/master/plugin-gradle).
Main goal is to avoid extensive reformatting caused by different IDEs having different opinion about how things should
be formatted by establishing single 'point of truth'.

Running
To reformat all the files that need reformatting.

```bash
./gradlew spotlessApply
```

reformats all the files that need reformatting.

Running
To run formatting verify task only.

```bash
./gradlew spotlessCheck
```

runs formatting verify task only.

## Pre-commit hook

There is a pre-commit hook setup to verify formatting before committing. It can be activated with this command:
Expand All @@ -178,53 +89,60 @@ git config core.hooksPath .githooks

Git does not automatically update submodules when switching branches.

Add the following configuration setting or you will need to remember to add `--recurse-submodules` to `git checkout` when switching to old branches.
Add the following configuration setting, or you will need to remember to add `--recurse-submodules` to `git checkout`
when switching to old branches.

```bash
git config --local submodule.recurse true
```

This will keep the submodule in `dd-java-agent/agent-jmxfetch/integrations-core` up to date.


## Intellij IDEA

Compiler settings:

* OpenJDK 11 must be installed to build the entire project. Under `SDKs` it must have the name `11`.
* OpenJDK 11 must be installed to build the entire project. Under `SDKs` it must have the name `11`.
* Under `Build, Execution, Deployment > Compiler > Java Compiler` disable `Use '--release' option for cross-compilation`

Suggested plugins and settings:

* Editor > Code Style > Java/Groovy > Imports
* Class count to use import with '*': `9999` (some number sufficiently large that is unlikely to matter)
* Names count to use static import with '*': `9999`
* With java use the following import layout (groovy should still use the default) to ensure consistency with google-java-format:
![import layout](https://user-images.githubusercontent.com/734411/43430811-28442636-94ae-11e8-86f1-f270ddcba023.png)
* Class count to use import with '*': `9999` (some number sufficiently large that is unlikely to matter)
* Names count to use static import with '*': `9999`
* With java use the following import layout (groovy should still use the default) to ensure consistency with
google-java-format:
![import layout](https://user-images.githubusercontent.com/734411/43430811-28442636-94ae-11e8-86f1-f270ddcba023.png)
* [Google Java Format](https://plugins.jetbrains.com/plugin/8527-google-java-format)

## Troubleshooting

* When Gradle is building the project, the error `Could not find netty-transport-native-epoll-4.1.43.Final-linux-x86_64.jar` is shown.
* Execute `rm -rf ~/.m2/repository/io/netty/netty-transport*` in a Terminal and re-build again.
* When Gradle is building the project, the
error `Could not find netty-transport-native-epoll-4.1.43.Final-linux-x86_64.jar` is shown.
* Execute `rm -rf ~/.m2/repository/io/netty/netty-transport*` in a Terminal and re-build again.

* IntelliJ 2021.3 complains `Failed to find KotlinGradleProjectData for GradleSourceSetData` https://youtrack.jetbrains.com/issue/KTIJ-20173
* Switch to `IntelliJ IDEA CE 2021.2.3`
* IntelliJ 2021.3
complains `Failed to find KotlinGradleProjectData for GradleSourceSetData` https://youtrack.jetbrains.com/issue/KTIJ-20173
* Switch to `IntelliJ IDEA CE 2021.2.3`

* IntelliJ Gradle fails to import the project with `JAVA_11_HOME must be set to build Java 11 code`
* A workaround is to run IntelliJ from terminal with `JAVA_11_HOME`
* In order to verify what's visible from IntelliJ use `Add Configuration` bar and go to `Add New` -> `Gradle` -> `Environmental Variables`
* A workaround is to run IntelliJ from terminal with `JAVA_11_HOME`
* In order to verify what's visible from IntelliJ use `Add Configuration` bar and go
to `Add New` -> `Gradle` -> `Environmental Variables`

* Gradle fails with a "too many open files" error.
* You can check the `ulimit` for open files in your current shell by doing `ulimit -n` and raise it by calling `ulimit -n <new number>`
* You can check the `ulimit` for open files in your current shell by doing `ulimit -n` and raise it by
calling `ulimit -n <new number>`

## Running tests on another JVM

To run tests on a different JVM than the one used for doing the build, you need two things:

1) An environment variable pointing to the JVM to use on the form `JAVA_[JDKNAME]_HOME`, e.g. `JAVA_ZULU15_HOME`, `JAVA_GRAALVM17_HOME`
1) An environment variable pointing to the JVM to use on the form `JAVA_[JDKNAME]_HOME`,
e.g. `JAVA_ZULU15_HOME`, `JAVA_GRAALVM17_HOME`

2) A command line option to the gradle task on the form `-PtestJvm=[JDKNAME]`, e.g. `-PtestJvm=ZULU15`, `-PtestJvm=GRAALVM17`
2) A command line option to the gradle task on the form `-PtestJvm=[JDKNAME]`,
e.g. `-PtestJvm=ZULU15`, `-PtestJvm=GRAALVM17`

Please note that the JDK name needs to end with the JDK version, e.g. `11`, `ZULU15`, `ORACLE8`, `GRAALVM17`, etc.

Expand Down
4 changes: 3 additions & 1 deletion buildSrc/src/main/groovy/MuzzlePlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ class MuzzlePlugin implements Plugin<Project> {
] as BiFunction)
}
}

dumpVersionsToCsv(project, map)
}

Expand All @@ -255,7 +256,7 @@ class MuzzlePlugin implements Plugin<Project> {
final RepositorySystem system = newRepositorySystem()
final RepositorySystemSession session = newRepositorySystemSession(system)
def versions = new TreeMap<String, TestedArtifact>()
project.muzzle.directives.findAll { !((MuzzleDirective) it).isCoreJdk() }.each {
project.muzzle.directives.findAll { !((MuzzleDirective) it).isCoreJdk() && !((MuzzleDirective) it).isSkipFromReport() }.each {
def range = resolveVersionRange(it as MuzzleDirective, system, session)
def cp = project.sourceSets.main.runtimeClasspath
def cl = new URLClassLoader(cp*.toURI()*.toURL() as URL[], null as ClassLoader)
Expand Down Expand Up @@ -552,6 +553,7 @@ class MuzzleDirective {
List<String> excludedDependencies = new ArrayList<>()
boolean assertPass
boolean assertInverse = false
boolean skipFromReport = false
boolean coreJdk = false
String javaVersion

Expand Down
Loading

0 comments on commit 7456e4f

Please sign in to comment.