Skip to content

Commit 5328f4b

Browse files
authored
Merge branch 'main' into develop
2 parents a52db3a + 2229861 commit 5328f4b

File tree

67 files changed

+3087
-60
lines changed

Some content is hidden

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

67 files changed

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

.github/workflows/samples.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ on:
1515
- '.github/workflows/samples.yml'
1616

1717
jobs:
18-
build-kinesis-sample:
18+
build:
1919
runs-on: ubuntu-latest
2020
steps:
2121
- uses: actions/checkout@v4
@@ -28,6 +28,9 @@ jobs:
2828
# Install events module
2929
- name: Install events with Maven
3030
run: mvn -B install --file aws-lambda-java-events/pom.xml
31+
# Install serialization module
32+
- name: Install serialization with Maven
33+
run: mvn -B install --file aws-lambda-java-serialization/pom.xml
3134
# Install tests module
3235
- name: Install tests with Maven
3336
run: mvn -B install --file aws-lambda-java-tests/pom.xml

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ dependency-reduced-pom.xml
2828

2929
# snapshot process
3030
aws-lambda-java-runtime-interface-client/pom.xml.versionsBackup
31+
32+
# profiler
33+
experimental/aws-lambda-java-profiler/integration_tests/helloworld/build
34+
experimental/aws-lambda-java-profiler/extension/build/
35+
experimental/aws-lambda-java-profiler/integration_tests/helloworld/bin
36+
!experimental/aws-lambda-java-profiler/extension/gradle/wrapper/*.jar
37+
/scratch/

README.md

Lines changed: 12 additions & 0 deletions
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="experimental/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](experimental/aws-lambda-java-profiler#quick-start) 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-runtime-interface-client/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@
8787
<version>4.11.0</version>
8888
<scope>test</scope>
8989
</dependency>
90+
<dependency>
91+
<groupId>org.mockito</groupId>
92+
<artifactId>mockito-junit-jupiter</artifactId>
93+
<version>4.11.0</version>
94+
<scope>test</scope>
95+
</dependency>
9096
<dependency>
9197
<groupId>com.squareup.okhttp3</groupId>
9298
<artifactId>mockwebserver</artifactId>

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
SPDX-License-Identifier: Apache-2.0
44
*/
5+
56
package com.amazonaws.services.lambda.runtime.api.client;
67

78
import com.amazonaws.services.lambda.crac.Core;
@@ -35,7 +36,6 @@
3536
import java.security.Security;
3637
import java.util.Properties;
3738

38-
3939
/**
4040
* The entrypoint of this class is {@link AWSLambda#startRuntime}. It performs two main tasks:
4141
*
@@ -137,6 +137,42 @@ private static LambdaRequestHandler findRequestHandler(final String handlerStrin
137137
return requestHandler;
138138
}
139139

140+
private static LambdaRequestHandler getLambdaRequestHandlerObject(String handler, LambdaContextLogger lambdaLogger) throws ClassNotFoundException, IOException {
141+
UnsafeUtil.disableIllegalAccessWarning();
142+
143+
System.setOut(new PrintStream(new LambdaOutputStream(System.out), false, "UTF-8"));
144+
System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8"));
145+
setupRuntimeLogger(lambdaLogger);
146+
147+
runtimeClient = new LambdaRuntimeApiClientImpl(LambdaEnvironment.RUNTIME_API);
148+
149+
String taskRoot = System.getProperty("user.dir");
150+
String libRoot = "/opt/java";
151+
// Make system classloader the customer classloader's parent to ensure any aws-lambda-java-core classes
152+
// are loaded from the system classloader.
153+
customerClassLoader = new CustomerClassLoader(taskRoot, libRoot, ClassLoader.getSystemClassLoader());
154+
Thread.currentThread().setContextClassLoader(customerClassLoader);
155+
156+
// Load the user's handler
157+
LambdaRequestHandler requestHandler = null;
158+
try {
159+
requestHandler = findRequestHandler(handler, customerClassLoader);
160+
} catch (UserFault userFault) {
161+
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
162+
LambdaError error = new LambdaError(
163+
LambdaErrorConverter.fromUserFault(userFault),
164+
RapidErrorType.BadFunctionCode);
165+
runtimeClient.reportInitError(error);
166+
System.exit(1);
167+
}
168+
169+
if (INIT_TYPE_SNAP_START.equals(AWS_LAMBDA_INITIALIZATION_TYPE)) {
170+
onInitComplete(lambdaLogger);
171+
}
172+
173+
return requestHandler;
174+
}
175+
140176
public static void setupRuntimeLogger(LambdaLogger lambdaLogger)
141177
throws ClassNotFoundException {
142178
ReflectUtil.setStaticField(
@@ -176,55 +212,27 @@ private static LogSink createLogSink() {
176212
}
177213
}
178214

179-
public static void main(String[] args) {
180-
startRuntime(args[0]);
181-
}
215+
public static void main(String[] args) throws Throwable {
216+
try (LambdaContextLogger logger = initLogger()) {
217+
LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandlerObject(args[0], logger);
218+
startRuntimeLoop(lambdaRequestHandler, logger);
182219

183-
private static void startRuntime(String handler) {
184-
try (LogSink logSink = createLogSink()) {
185-
LambdaContextLogger logger = new LambdaContextLogger(
186-
logSink,
187-
LogLevel.fromString(LambdaEnvironment.LAMBDA_LOG_LEVEL),
188-
LogFormat.fromString(LambdaEnvironment.LAMBDA_LOG_FORMAT)
189-
);
190-
startRuntime(handler, logger);
191-
} catch (Throwable t) {
220+
} catch (IOException | ClassNotFoundException t) {
192221
throw new Error(t);
193222
}
194223
}
195224

196-
private static void startRuntime(String handler, LambdaContextLogger lambdaLogger) throws Throwable {
197-
UnsafeUtil.disableIllegalAccessWarning();
198-
199-
System.setOut(new PrintStream(new LambdaOutputStream(System.out), false, "UTF-8"));
200-
System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8"));
201-
setupRuntimeLogger(lambdaLogger);
225+
private static LambdaContextLogger initLogger() {
226+
LogSink logSink = createLogSink();
227+
LambdaContextLogger logger = new LambdaContextLogger(
228+
logSink,
229+
LogLevel.fromString(LambdaEnvironment.LAMBDA_LOG_LEVEL),
230+
LogFormat.fromString(LambdaEnvironment.LAMBDA_LOG_FORMAT));
202231

203-
runtimeClient = new LambdaRuntimeApiClientImpl(LambdaEnvironment.RUNTIME_API);
204-
205-
String taskRoot = System.getProperty("user.dir");
206-
String libRoot = "/opt/java";
207-
// Make system classloader the customer classloader's parent to ensure any aws-lambda-java-core classes
208-
// are loaded from the system classloader.
209-
customerClassLoader = new CustomerClassLoader(taskRoot, libRoot, ClassLoader.getSystemClassLoader());
210-
Thread.currentThread().setContextClassLoader(customerClassLoader);
232+
return logger;
233+
}
211234

212-
// Load the user's handler
213-
LambdaRequestHandler requestHandler;
214-
try {
215-
requestHandler = findRequestHandler(handler, customerClassLoader);
216-
} catch (UserFault userFault) {
217-
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
218-
LambdaError error = new LambdaError(
219-
LambdaErrorConverter.fromUserFault(userFault),
220-
RapidErrorType.BadFunctionCode);
221-
runtimeClient.reportInitError(error);
222-
System.exit(1);
223-
return;
224-
}
225-
if (INIT_TYPE_SNAP_START.equals(AWS_LAMBDA_INITIALIZATION_TYPE)) {
226-
onInitComplete(lambdaLogger);
227-
}
235+
private static void startRuntimeLoop(LambdaRequestHandler requestHandler, LambdaContextLogger lambdaLogger) throws Throwable {
228236
boolean shouldExit = false;
229237
while (!shouldExit) {
230238
UserFault userFault = null;
@@ -240,7 +248,7 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge
240248
payload = requestHandler.call(request);
241249
runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray());
242250
// clear interrupted flag in case if it was set by user's code
243-
boolean ignored = Thread.interrupted();
251+
Thread.interrupted();
244252
} catch (UserFault f) {
245253
shouldExit = f.fatal;
246254
userFault = f;
@@ -278,6 +286,7 @@ static void onInitComplete(final LambdaContextLogger lambdaLogger) throws IOExce
278286
RapidErrorType.BeforeCheckpointError));
279287
System.exit(64);
280288
}
289+
281290
try {
282291
Core.getGlobalContext().afterRestore(null);
283292
} catch (Exception restoreExc) {

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ public ByteArrayOutputStream call(InvocationRequest request) throws Error, Excep
581581
cognitoIdentity,
582582
LambdaEnvironment.FUNCTION_VERSION,
583583
request.getInvokedFunctionArn(),
584+
request.getTenantId(),
584585
clientContext
585586
);
586587

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class LambdaContext implements Context {
2222
private final long deadlineTimeInMs;
2323
private final CognitoIdentity cognitoIdentity;
2424
private final ClientContext clientContext;
25+
private final String tenantId;
2526
private final LambdaLogger logger;
2627

2728
public LambdaContext(
@@ -34,6 +35,7 @@ public LambdaContext(
3435
CognitoIdentity identity,
3536
String functionVersion,
3637
String invokedFunctionArn,
38+
String tenantId,
3739
ClientContext clientContext
3840
) {
3941
this.memoryLimit = memoryLimit;
@@ -46,6 +48,7 @@ public LambdaContext(
4648
this.clientContext = clientContext;
4749
this.functionVersion = functionVersion;
4850
this.invokedFunctionArn = invokedFunctionArn;
51+
this.tenantId = tenantId;
4952
this.logger = com.amazonaws.services.lambda.runtime.LambdaRuntime.getLogger();
5053
}
5154

@@ -91,6 +94,10 @@ public int getRemainingTimeInMillis() {
9194
return delta > 0 ? delta : 0;
9295
}
9396

97+
public String getTenantId() {
98+
return tenantId;
99+
}
100+
94101
public LambdaLogger getLogger() {
95102
return logger;
96103
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ private StructuredLogMessage createLogMessage(String message, LogLevel logLevel)
4141

4242
if (lambdaContext != null) {
4343
msg.AWSRequestId = lambdaContext.getAwsRequestId();
44+
msg.tenantId = lambdaContext.getTenantId();
4445
}
4546
return msg;
4647
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77

88
import com.amazonaws.services.lambda.runtime.logging.LogFormat;
99
import com.amazonaws.services.lambda.runtime.logging.LogLevel;
10+
import java.io.Closeable;
11+
import java.io.IOException;
1012
import static java.nio.charset.StandardCharsets.UTF_8;
1113

12-
public class LambdaContextLogger extends AbstractLambdaLogger {
14+
public class LambdaContextLogger extends AbstractLambdaLogger implements Closeable {
1315
// If a null string is passed in, replace it with "null",
1416
// replicating the behavior of System.out.println(null);
1517
private static final byte[] NULL_BYTES_VALUE = "null".getBytes(UTF_8);
@@ -29,4 +31,10 @@ protected void logMessage(byte[] message, LogLevel logLevel) {
2931
sink.log(logLevel, this.logFormat, message);
3032
}
3133
}
34+
35+
@Override
36+
public void close() throws IOException {
37+
sink.close();
38+
39+
}
3240
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StructuredLogMessage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ class StructuredLogMessage {
1212
public String message;
1313
public LogLevel level;
1414
public String AWSRequestId;
15+
public String tenantId;
1516
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public class InvocationRequest {
4040
*/
4141
private String cognitoIdentity;
4242

43+
/**
44+
* The tenant ID associated with the request.
45+
*/
46+
private String tenantId;
47+
4348
private byte[] content;
4449

4550
public String getId() {
@@ -94,6 +99,14 @@ public void setCognitoIdentity(String cognitoIdentity) {
9499
this.cognitoIdentity = cognitoIdentity;
95100
}
96101

102+
public String getTenantId() {
103+
return tenantId;
104+
}
105+
106+
public void setTenantId(String tenantId) {
107+
this.tenantId = tenantId;
108+
}
109+
97110
public byte[] getContent() {
98111
return content;
99112
}

0 commit comments

Comments
 (0)