Skip to content

Commit cc78e05

Browse files
msailesmaxday
authored andcommitted
feat: profiler
1 parent 0763aa9 commit cc78e05

Some content is hidden

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

42 files changed

+1902
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Run integration tests for aws-lambda-java-profiler
2+
3+
on:
4+
push:
5+
branches: ['*']
6+
paths:
7+
- 'aws-lambda-java-profiler/**'
8+
- '.github/workflows/aws-lambda-java-profiler.yml'
9+
workflow_dispatch:
10+
11+
jobs:
12+
13+
publish:
14+
runs-on: ubuntu-latest
15+
16+
permissions:
17+
id-token: write
18+
contents: read
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Set up JDK
24+
uses: actions/setup-java@v4
25+
with:
26+
java-version: 21
27+
distribution: corretto
28+
29+
- name: Issue AWS credentials
30+
uses: aws-actions/configure-aws-credentials@v4
31+
with:
32+
aws-region: ${{ secrets.AWS_REGION }}
33+
role-to-assume: ${{ secrets.AWS_ROLE }}
34+
role-session-name: GitHubActionsRunIntegrationTests
35+
role-duration-seconds: 900
36+
37+
- name: Build layer
38+
working-directory: ./aws-lambda-java-profiler/extension
39+
run: ./build_layer.sh
40+
41+
- name: Publish layer
42+
working-directory: ./aws-lambda-java-profiler
43+
run: ./integration_tests/publish_layer.sh
44+
45+
- name: Create the bucket layer
46+
working-directory: ./aws-lambda-java-profiler
47+
run: ./integration_tests/create_bucket.sh
48+
49+
- name: Create Java function
50+
working-directory: ./aws-lambda-java-profiler
51+
run: ./integration_tests/create_function.sh
52+
53+
- name: Invoke Java function
54+
working-directory: ./aws-lambda-java-profiler
55+
run: ./integration_tests/invoke_function.sh
56+
57+
- name: Download from s3
58+
working-directory: ./aws-lambda-java-profiler
59+
run: ./integration_tests/download_from_s3.sh
60+
61+
- name: Upload profiles
62+
uses: actions/upload-artifact@v4
63+
with:
64+
name: profiles
65+
path: /tmp/s3-artifacts
66+
67+
- name: cleanup
68+
if: always()
69+
working-directory: ./aws-lambda-java-profiler
70+
run: ./integration_tests/cleanup.sh

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ dependency-reduced-pom.xml
2828

2929
# snapshot process
3030
aws-lambda-java-runtime-interface-client/pom.xml.versionsBackup
31+
32+
# profiler
33+
aws-lambda-java-profiler/integration_tests/helloworld/build
34+
aws-lambda-java-profiler/extension/build/
35+
/scratch/

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,18 @@ See the [README](aws-lambda-java-log4j2/README.md) or the [official documentatio
139139
</dependency>
140140
```
141141

142+
## Lambda Profiler Extension for Java - aws-lambda-java-profiler
143+
144+
<p align="center">
145+
<img src="aws-lambda-java-profiler/docs/example-cold-start-flame-graph-small.png" alt="A flame graph of a Java Lambda function">
146+
</p>
147+
148+
This project allows you to profile your Java functions invoke by invoke, with high fidelity, and no code changes. It
149+
uses the [async-profiler](https://github.com/async-profiler/async-profiler) project to produce profiling data and
150+
automatically uploads the data as flame graphs to S3.
151+
152+
Follow our [Quick Start](aws-lambda-java-profiler) to profile your functions.
153+
142154
## Java implementation of the Runtime Interface Client API - aws-lambda-java-runtime-interface-client
143155
[![Maven](https://img.shields.io/maven-central/v/com.amazonaws/aws-lambda-java-runtime-interface-client.svg?label=Maven)](https://central.sonatype.com/artifact/com.amazonaws/aws-lambda-java-runtime-interface-client)
144156

aws-lambda-java-profiler/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.zip
2+
/.idea/
3+
/target/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
wrapperVersion=3.3.2
18+
distributionType=only-script
19+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip

aws-lambda-java-profiler/README.md

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<p align="center">
2+
<img src="docs/Arch_AWS-Lambda_64.svg" alt="AWS Lambda service icon">
3+
</p>
4+
5+
<h2 align="center">AWS Lambda Profiler Extension for Java</h2>
6+
7+
The Lambda profiler extension allows you to profile your Java functions invoke by invoke, with high fidelity, and no
8+
code changes. It uses the [async-profiler](https://github.com/async-profiler/async-profiler) project to produce
9+
profiling data and automatically uploads the data as flame graphs to S3.
10+
11+
<p align="center">
12+
<img src="docs/example-cold-start-flame-graph.png" alt="A flame graph of a Java Lambda function">
13+
</p>
14+
15+
### Usage
16+
17+
To use the profiler you need to
18+
19+
1. Build the extension in this repo
20+
2. Deploy it as a Lambda Layer
21+
3. Create an S3 bucket for the results, or reuse an existing one
22+
4. Give your function permission to write to the bucket
23+
5. Configure the required environment variables.
24+
25+
The following [Quick Start](#quick-start) will give you AWS CLI commands to run to get started. There are also [examples](examples)
26+
using infrastructure as code for you to refer to.
27+
28+
### Quick Start
29+
30+
- Clone the repo
31+
32+
```bash
33+
git clone https://github.com/aws/aws-lambda-java-libs
34+
```
35+
36+
- Build the extension
37+
38+
```bash
39+
cd aws-lambda-java-profiler/extension
40+
./build_layer.sh
41+
```
42+
43+
- Run the `update-function.sh` script which will create a new S3 bucket, Lambda layer and all the configuration required.
44+
45+
```bash
46+
cd ..
47+
./update-function.sh YOUR_FUNCTION_NAME
48+
```
49+
50+
### Configuration
51+
52+
#### Required Environment Variables
53+
54+
| Name | Value |
55+
|------------------------------|-----------------------------------------------------------------------------------------------|
56+
| PROFILER_RESULTS_BUCKET_NAME | Your unique bucket name |
57+
| JAVA_TOOL_OPTIONS | -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -javaagent:/opt/profiler-extension.jar |
58+
59+
#### Optional Environment Variables
60+
61+
| Name | Default Value | Options |
62+
|-------------------------------|-----------------------------------------------------------|--------------------------------|
63+
| PROFILER_START_COMMAND | start,event=wall,interval=1us | |
64+
| PROFILER_STOP_COMMAND | stop,file=%s,include=*AWSLambda.main,include=start_thread | file=%s is required |
65+
| PROFILER_DEBUG | false | true - to enable debug logging |
66+
| PROFILER_COMMUNICATION_PORT | 1234 | a valid port number |
67+
68+
### How does it work?
69+
70+
In `/src` is the code for a Java agent. It's entry point `AgentEntry.premain()` is executed as the runtime starts up.
71+
The environment variable `JAVA_TOOL_OPTIONS` is used to specify which .jar file the agent is in. The `MANIFEST.MF` file
72+
is used to specify the pre-main class.
73+
74+
When the agent is constructed, it starts the profiler and registers itself as a Lambda extension for `INVOKE` request.
75+
76+
A new thread is created to handle calling `/next` and uploading the results of the profiler to S3. The bucket to upload
77+
the result to is configurable using an environment variable.
78+
79+
### Project Structure
80+
81+
- `Agent.java`: Main class that coordinates profiling and S3 uploads.
82+
- `AgentEntry.java`: Entry point for the Java agent.
83+
- `ExtensionClient.java`: Handles communication with the Lambda Extensions API.
84+
85+
### Troubleshooting
86+
87+
- Ensure the Lambda function has the necessary permissions to write to the S3 bucket.
88+
- Verify that the environment variables are correctly set in your Lambda function configuration.
89+
- Check CloudWatch logs for any error messages from the extension.
90+
91+
### Contributing
92+
93+
Contributions to improve the extension are welcome. Please submit pull requests with your proposed changes.
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.classpath.txt
2+
target
3+
.classpath
4+
.project
5+
.idea
6+
.settings
7+
.vscode
8+
*.iml
9+
10+
# CDK asset staging directory
11+
.cdk.staging
12+
cdk.out
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Welcome to your CDK Java project!
2+
3+
This is a blank project for CDK development with Java.
4+
5+
The `cdk.json` file tells the CDK Toolkit how to execute your app.
6+
7+
It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests.
8+
9+
## Useful commands
10+
11+
* `mvn package` compile and run tests
12+
* `cdk ls` list all stacks in the app
13+
* `cdk synth` emits the synthesized CloudFormation template
14+
* `cdk deploy` deploy this stack to your default AWS account/region
15+
* `cdk diff` compare deployed stack with current state
16+
* `cdk docs` open CDK documentation
17+
18+
Enjoy!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"app": "mvn -e -q compile exec:java",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"target",
11+
"pom.xml",
12+
"src/test"
13+
]
14+
},
15+
"context": {
16+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
17+
"@aws-cdk/core:checkSecretUsage": true,
18+
"@aws-cdk/core:target-partitions": [
19+
"aws",
20+
"aws-cn"
21+
],
22+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
23+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
24+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
25+
"@aws-cdk/aws-iam:minimizePolicies": true,
26+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
27+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
28+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
29+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
30+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
31+
"@aws-cdk/core:enablePartitionLiterals": true,
32+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
33+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
34+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
35+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
36+
"@aws-cdk/aws-route53-patters:useCertificate": true,
37+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
38+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
39+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
40+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
41+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
42+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
43+
"@aws-cdk/aws-redshift:columnId": true,
44+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
45+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
46+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
47+
"@aws-cdk/aws-kms:aliasNameRef": true,
48+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
49+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
50+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
51+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
52+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
53+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
54+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
55+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
56+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
57+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
58+
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
59+
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
60+
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
61+
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
62+
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
63+
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
64+
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
65+
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
66+
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
3+
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.myorg</groupId>
7+
<artifactId>example-cdk-profiler-layer</artifactId>
8+
<version>0.1</version>
9+
10+
<properties>
11+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
12+
<cdk.version>2.155.0</cdk.version>
13+
<constructs.version>[10.0.0,11.0.0)</constructs.version>
14+
<junit.version>5.7.1</junit.version>
15+
</properties>
16+
17+
<build>
18+
<plugins>
19+
<plugin>
20+
<groupId>org.apache.maven.plugins</groupId>
21+
<artifactId>maven-compiler-plugin</artifactId>
22+
<version>3.11.0</version>
23+
<configuration>
24+
<release>17</release>
25+
</configuration>
26+
</plugin>
27+
28+
<plugin>
29+
<groupId>org.codehaus.mojo</groupId>
30+
<artifactId>exec-maven-plugin</artifactId>
31+
<version>3.1.0</version>
32+
<configuration>
33+
<mainClass>com.myorg.InfraApp</mainClass>
34+
</configuration>
35+
</plugin>
36+
</plugins>
37+
</build>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>software.amazon.awscdk</groupId>
42+
<artifactId>aws-cdk-lib</artifactId>
43+
<version>${cdk.version}</version>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>software.constructs</groupId>
48+
<artifactId>constructs</artifactId>
49+
<version>${constructs.version}</version>
50+
</dependency>
51+
52+
<dependency>
53+
<groupId>org.junit.jupiter</groupId>
54+
<artifactId>junit-jupiter</artifactId>
55+
<version>${junit.version}</version>
56+
<scope>test</scope>
57+
</dependency>
58+
</dependencies>
59+
</project>

0 commit comments

Comments
 (0)