Skip to content

Commit

Permalink
feat: replace current JS hack with an Java agent
Browse files Browse the repository at this point in the history
  • Loading branch information
notdryft committed Oct 4, 2024
1 parent 87bf63a commit 08e0efc
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 72 deletions.
2 changes: 1 addition & 1 deletion build-jvm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ root_dir="$(dirname "$(realpath -- "$0")")"

# Publish JVM adapter
cd "$root_dir/jvm"
sbt gatling-jvm-to-js-adapter/publishLocal
sbt gatling-js-agent/publishLocal gatling-jvm-to-js-adapter/publishLocal
4 changes: 1 addition & 3 deletions js/cli/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Command } from "commander";

import registerInstallCommand from "./install";
import registerBuildCommand from "./build";
import registerRunOnlyCommand from "./runOnly";
import registerRunCommand from "./run";
import registerRecorderCommand from "./recorder";
import registerEnterprisePackageCommand from "./enterprisePackage";
Expand All @@ -12,12 +11,11 @@ import { versions } from "../dependencies";

export const program: Command = new Command()
.name("gatling-js-cli")
.version(versions.gatling.jsAdapter)
.version(versions.gatling.js)
.description("The Gatling Javascript run & packaging tool");

registerInstallCommand(program);
registerBuildCommand(program);
registerRunOnlyCommand(program);
registerRunCommand(program);
registerRecorderCommand(program);
registerEnterprisePackageCommand(program);
Expand Down
16 changes: 10 additions & 6 deletions js/cli/src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { installGatlingJs } from "../dependencies";
import { logger } from "../log";
import { bundle } from "../bundle";
import { runSimulation } from "../run";
import { resolveGatlingJsAgent } from "../dependencies/coursier";

export default (program: Command): void => {
program
Expand Down Expand Up @@ -57,16 +58,19 @@ export default (program: Command): void => {
const typescript = typescriptOptionValueWithDefaults(options, simulations);
const simulation = simulationOptionValueWithDefaults(options, simulations, !nonInteractive);

const { graalvmHome, coursierBinary, jvmClasspath } = await installGatlingJs({ gatlingHome });
logger.debug(`graalvmHome=${graalvmHome}`);
logger.debug(`coursierBinary=${coursierBinary}`);
logger.debug(`jvmClasspath=${jvmClasspath}`);
const resolvedDependencies = await installGatlingJs({ gatlingHome });
logger.debug(`graalvmHome=${resolvedDependencies.graalvmHome}`);
logger.debug(`coursierBinary=${resolvedDependencies.coursierBinary}`);
logger.debug(`jvmClasspath=${resolvedDependencies.jvmClasspath}`);
const gatlingJsAgent = await resolveGatlingJsAgent(resolvedDependencies);
logger.debug(`gatlingJsAgent=${gatlingJsAgent}`);

await bundle({ sourcesFolder, bundleFile, typescript, simulations });

await runSimulation({
graalvmHome,
jvmClasspath,
graalvmHome: resolvedDependencies.graalvmHome,
jvmClasspath: resolvedDependencies.jvmClasspath,
jsAgent: gatlingJsAgent,
simulation,
bundleFile,
resourcesFolder,
Expand Down
56 changes: 0 additions & 56 deletions js/cli/src/commands/runOnly.ts

This file was deleted.

12 changes: 11 additions & 1 deletion js/cli/src/dependencies/coursier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { versions } from "./versions";
import { promisify } from "util";
import { exec } from "child_process";
import { osType } from "./os";
import { ResolvedDependencies } from "./index";

export const installCoursier = async (gatlingHomeDir: string, downloadDir: string): Promise<string> => {
const coursierRootPath = `${gatlingHomeDir}/coursier/${versions.coursier}`;
Expand Down Expand Up @@ -45,9 +46,18 @@ export const installCoursier = async (gatlingHomeDir: string, downloadDir: strin
return coursierPath;
};

export const resolveGatlingJsAgent = async (dependencies: ResolvedDependencies): Promise<string> => {
const gatlingJsAgentDep = `"io.gatling:gatling-js-agent:${versions.gatling.js}"`;
const command = `"${dependencies.coursierBinary}" fetch ${gatlingJsAgentDep}`;

// TODO could add a timeout
const { stdout } = await execAsync(command, { env: { ...process.env, JAVA_HOME: dependencies.graalvmHome } });
return stdout.split(/[\r\n]+/g)[0];
};

export const resolveGatlingJsDependencies = async (coursierPath: string, javaHome: string): Promise<string> => {
const gatlingDep = `"io.gatling.highcharts:gatling-charts-highcharts:${versions.gatling.core}"`;
const gatlingAdapterDep = `"io.gatling:gatling-jvm-to-js-adapter:${versions.gatling.jsAdapter}"`;
const gatlingAdapterDep = `"io.gatling:gatling-jvm-to-js-adapter:${versions.gatling.js}"`;
const gatlingEnterprisePluginCommonsDep = `"io.gatling:gatling-enterprise-plugin-commons:${versions.gatling.enterprisePluginCommons}"`;
const graalvmJsDep = `"org.graalvm.polyglot:js-community:${versions.graalvm.js}"`;

Expand Down
4 changes: 2 additions & 2 deletions js/cli/src/enterprise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const generateManifest = (simulationNames: string[]) => {
"Gatling-Context: js",
`Gatling-Version: ${versions.gatling.core}`,
"Gatling-Packager: js-cli",
`Gatling-Packager-Version: ${versions.gatling.jsAdapter}`,
`Gatling-Packager-Version: ${versions.gatling.js}`,
`Gatling-Simulations: ${simulationNames.join(",")}`,
`Java-Version: ${versions.java.compilerRelease}`
];
Expand Down Expand Up @@ -147,7 +147,7 @@ const javaArgsFromPluginOptions = (options: EnterprisePluginOptions) => {
javaArgs.push(`-Dgatling.enterprise.controlPlaneUrl=${options.controlPlaneUrl}`);
}
javaArgs.push("-Dgatling.enterprise.buildTool=js-cli");
javaArgs.push(`-Dgatling.enterprise.pluginVersion=${versions.gatling.jsAdapter}`);
javaArgs.push(`-Dgatling.enterprise.pluginVersion=${versions.gatling.js}`);

if (options.nonInteractive) {
javaArgs.push(`-Dgatling.enterprise.batchMode=true`);
Expand Down
5 changes: 4 additions & 1 deletion js/cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { versions } from "./dependencies";
import { RunJavaProcessOptions, runJavaProcess, runNodeProcess } from "./java";

export interface RunSimulationOptions extends RunJavaProcessOptions {
jsAgent: string;
simulation: string;
bundleFile: string;
resourcesFolder: string;
Expand All @@ -29,11 +30,13 @@ export const runSimulation = async (options: RunSimulationOptions): Promise<void
const memoryArgs = options.memory !== undefined ? [`--vm.Xms${options.memory}M`, `--vm.Xmx${options.memory}M`] : [];
const javaArgs = [
...Object.entries(options.runParameters).map(([key, value]) => `--vm.D${key}=${value}`),
`--vm.javaagent:${options.jsAgent}`,
`--vm.Dgatling.js.bundle.filePath=${options.bundleFile}`,
`--vm.Dgatling.js.simulation=${options.simulation}`,
...jitTuningArgs,
...memoryArgs
];
logger.debug(`javaArgs=${javaArgs}`);
const simulationArgs = [
"--results-folder",
options.resultsFolder,
Expand All @@ -42,7 +45,7 @@ export const runSimulation = async (options: RunSimulationOptions): Promise<void
"--launcher",
"gatling-js-cli",
"--build-tool-version",
versions.gatling.jsAdapter
versions.gatling.js
];

const evalScript = `
Expand Down
78 changes: 78 additions & 0 deletions jvm/agent/src/main/java/io/gatling/js/JavaScriptLanguageHack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2011-2024 GatlingCorp (https://gatling.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.gatling.js;

import static io.gatling.internal.asm.Opcodes.*;

import io.gatling.internal.asm.*;
import io.gatling.internal.asm.ClassReader;
import io.gatling.internal.asm.ClassWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class JavaScriptLanguageHack {
public static void premain(String args, Instrumentation instrumentation) {
instrumentation.addTransformer(new JavaScriptLanguageClassFileTransformer());
try {
Class.forName("com.oracle.truffle.js.lang.JavaScriptLanguage");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(
"JavaScriptLanguage class file not found: GraalJS dependency is probably missing");
}
}

private static class JavaScriptLanguageClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
if (!className.equals("com/oracle/truffle/js/lang/JavaScriptLanguage")) {
return null;
}
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
reader.accept(new JavaScriptLanguageClassVisitor(writer), ClassReader.EXPAND_FRAMES);
return writer.toByteArray();
}
}

private static class JavaScriptLanguageClassVisitor extends ClassVisitor {
public JavaScriptLanguageClassVisitor(ClassVisitor cv) {
super(ASM9, cv);
}

@Override
public void visitEnd() {
final var mv =
cv.visitMethod(
ACC_PROTECTED, "isThreadAccessAllowed", "(Ljava/lang/Thread;Z)Z", null, null);
mv.visitCode();
mv.visitInsn(ICONST_1);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 2);
mv.visitEnd();

super.visitEnd();
}
}
}
30 changes: 28 additions & 2 deletions jvm/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ lazy val adapter = (project in file("adapter"))
libraryDependencies ++= Seq(
"io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion,
"org.graalvm.polyglot" % "js-community" % graalvmJsVersion,
// Dependency of js-agent kept here so we don't have to make a fat jar
"io.gatling" % "gatling-asm-shaded" % "9.7.0"
),
Compile / sourceGenerators += Def.task {
// Bit of a hack, generate a file directly into the CLI project to share version numbers
val path = (ThisBuild / baseDirectory).value / ".." / "js" / "cli" / "src" / "dependencies" / "versions.ts"
val jsAdapterVersion = version.value
val gatlingJsVersion = version.value
val content =
s"""export const versions = {
| graalvm: {
Expand All @@ -61,7 +62,7 @@ lazy val adapter = (project in file("adapter"))
| gatling: {
| core: "$gatlingVersion",
| enterprisePluginCommons: "$gatlingEnterpriseComponentPluginVersion",
| jsAdapter: "$jsAdapterVersion"
| js: "$gatlingJsVersion",
| }
|};
|""".stripMargin
Expand All @@ -73,6 +74,31 @@ lazy val adapter = (project in file("adapter"))
Compile / packageSrc / mappings := Seq.empty
)

lazy val agent = (project in file("agent"))
.withId("gatling-js-agent")
.enablePlugins(GatlingOssPlugin)
.settings(
name := "gatling-js-agent",
gatlingCompilerRelease := compilerRelease,
Compile / javacOptions ++= Seq("-encoding", "utf8", "-Xdoclint:none"), // FIXME: see why -Xdoclint:none does not seem to work
Compile / packageBin / packageOptions +=
Package.ManifestAttributes(
"Premain-Class" -> "io.gatling.js.JavaScriptLanguageHack"
),
Test / javacOptions ++= Seq("-encoding", "utf8"),
spotless := SpotlessConfig(
applyOnCompile = !sys.env.getOrElse("CI", "false").toBoolean
),
spotlessJava := JavaConfig(
googleJavaFormat = GoogleJavaFormatConfig()
),
libraryDependencies ++= Seq(
"io.gatling" % "gatling-asm-shaded" % "9.7.0"
),
Compile / packageDoc / mappings := Seq.empty,
Compile / packageSrc / mappings := Seq.empty
)

lazy val java2ts = (project in file("java2ts"))
.withId("gatling-java2ts")
.settings(
Expand Down

0 comments on commit 08e0efc

Please sign in to comment.