From 008fcd79fda55a964fa6f2c6e8694e5833fd76c5 Mon Sep 17 00:00:00 2001 From: Diego Date: Wed, 8 Feb 2023 20:58:56 -0300 Subject: [PATCH 01/19] * hotcold fix * check if async profiler is supported * clean up --- pom.xml | 3 +-- src/main/java/io/github/dpsoft/ap/Agent.java | 19 ++++++++++--------- .../dpsoft/ap/config/AgentConfiguration.java | 1 - .../hotcold/jfr2hotcoldflame.java | 2 +- .../ap/handler/AsyncProfilerHandler.java | 2 +- .../java/io/github/dpsoft/ap/util/Banner.java | 10 +++++----- .../io/github/dpsoft/ap/util/Profiler.java | 17 +++++++++++++++++ .../ap/{ => util}/ProfilerExecutor.java | 2 +- .../github/dpsoft/ap/{ => util}/Server.java | 6 +++--- 9 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 src/main/java/io/github/dpsoft/ap/util/Profiler.java rename src/main/java/io/github/dpsoft/ap/{ => util}/ProfilerExecutor.java (99%) rename src/main/java/io/github/dpsoft/ap/{ => util}/Server.java (89%) diff --git a/pom.xml b/pom.xml index e5250f5..a136fbd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 0.1.1-SNAPSHOT jar - 2.9-3 + 2.9-4 0.10.3 1.4.2 2.5.0 @@ -103,7 +103,6 @@ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> io.github.dpsoft.ap.Agent - io.github.dpsoft.ap.Agent { + final var configuration = AgentConfiguration.instance(); - Banner.show(configuration); + Banner.show(configuration); - Server.with(configuration, (server) -> { - final var profilerHandler = new AsyncProfilerHandler(profiler, configuration.handler); - server.createContext("/", profilerHandler); + Server.with(configuration, (server) -> { + final var profilerHandler = new AsyncProfilerHandler(profiler, configuration.handler); + server.createContext("/", profilerHandler); + }); }); } } diff --git a/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java b/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java index 761aac7..050f09f 100644 --- a/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java +++ b/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java @@ -31,7 +31,6 @@ public Server(Config config) { } public static class Handler { - public final boolean goMode; public final String goContext; public final String context; diff --git a/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java b/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java index 9212082..385058f 100644 --- a/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java +++ b/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java @@ -44,7 +44,7 @@ */ public class jfr2hotcoldflame { - private static final String[] FRAME_SUFFIX = {"_[j]", "_[j]", "_[i]", "", "", "_[k]"}; + private static final String[] FRAME_SUFFIX = {"_[0]", "_[j]", "_[i]", "", "", "_[k]", "_[1]"}; private final JfrReader jfr; private final Dictionary methodNames = new Dictionary<>(); diff --git a/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java b/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java index 9d53556..039866a 100644 --- a/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java +++ b/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java @@ -2,7 +2,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import io.github.dpsoft.ap.ProfilerExecutor; +import io.github.dpsoft.ap.util.ProfilerExecutor; import io.github.dpsoft.ap.command.Command; import io.github.dpsoft.ap.command.Command.Output; import io.github.dpsoft.ap.config.AgentConfiguration; diff --git a/src/main/java/io/github/dpsoft/ap/util/Banner.java b/src/main/java/io/github/dpsoft/ap/util/Banner.java index fe7ab60..00ba47e 100644 --- a/src/main/java/io/github/dpsoft/ap/util/Banner.java +++ b/src/main/java/io/github/dpsoft/ap/util/Banner.java @@ -2,14 +2,13 @@ import io.github.dpsoft.ap.config.AgentConfiguration; import io.github.dpsoft.ap.functions.Functions; - -import java.io.*; +import io.vavr.control.Try; import java.util.Map; import java.util.Scanner; public final class Banner { - public static void show(AgentConfiguration configuration) throws IOException { + public static void show(AgentConfiguration configuration) { if (configuration.showBanner()) { final var version = (BuildInfo.version() == null ? "" : " v" + BuildInfo.version() + ""); final var loaderVersion = (BuildInfo.apLoaderVersion() == null ? "" : "v" + BuildInfo.apLoaderVersion() + ""); @@ -19,7 +18,7 @@ public static void show(AgentConfiguration configuration) throws IOException { final var substitutor = new StrSubstitutor(Map.of("welcome", welcomeMessage, "powered", poweredByMessage)); - try (var stream = Banner.class.getResourceAsStream("/banner.txt")) { + Try.withResources(() -> Banner.class.getResourceAsStream("/banner.txt")).of((stream) -> { final var banner = substitutor.replace(new String(stream.readAllBytes())); final var scanner = new Scanner(banner); @@ -34,7 +33,8 @@ public static void show(AgentConfiguration configuration) throws IOException { System.out.println(line); } System.out.println(); - } + return null; // void :( + }); } } diff --git a/src/main/java/io/github/dpsoft/ap/util/Profiler.java b/src/main/java/io/github/dpsoft/ap/util/Profiler.java new file mode 100644 index 0000000..e2ddca1 --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/util/Profiler.java @@ -0,0 +1,17 @@ +package io.github.dpsoft.ap.util; + +import io.vavr.control.Try; +import one.profiler.AsyncProfiler; +import one.profiler.AsyncProfilerLoader; +import org.tinylog.Logger; + +import java.util.function.Consumer; + +public final class Profiler { + public static void with(Consumer consumer) { + if (!AsyncProfilerLoader.isSupported()) Logger.error("Async Profiler is not supported on this platform."); + + Try.run(() -> consumer.accept(AsyncProfilerLoader.load())) + .onFailure(cause -> Logger.error(cause, () -> "Error trying to load Async Profiler.")); + } +} diff --git a/src/main/java/io/github/dpsoft/ap/ProfilerExecutor.java b/src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java similarity index 99% rename from src/main/java/io/github/dpsoft/ap/ProfilerExecutor.java rename to src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java index c1d2c29..4998317 100644 --- a/src/main/java/io/github/dpsoft/ap/ProfilerExecutor.java +++ b/src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java @@ -1,4 +1,4 @@ -package io.github.dpsoft.ap; +package io.github.dpsoft.ap.util; import io.github.dpsoft.ap.command.Command; import io.github.dpsoft.ap.command.Command.Output; diff --git a/src/main/java/io/github/dpsoft/ap/Server.java b/src/main/java/io/github/dpsoft/ap/util/Server.java similarity index 89% rename from src/main/java/io/github/dpsoft/ap/Server.java rename to src/main/java/io/github/dpsoft/ap/util/Server.java index 48f12e9..70810fc 100644 --- a/src/main/java/io/github/dpsoft/ap/Server.java +++ b/src/main/java/io/github/dpsoft/ap/util/Server.java @@ -1,4 +1,4 @@ -package io.github.dpsoft.ap; +package io.github.dpsoft.ap.util; import com.sun.net.httpserver.HttpServer; import io.github.dpsoft.ap.config.AgentConfiguration; @@ -9,11 +9,11 @@ import java.util.function.Consumer; public final class Server { - public static void with(AgentConfiguration config, Consumer serverConsumer) { + public static void with(AgentConfiguration config, Consumer consumer) { Try.run(() -> { final var server = HttpServer.create(new InetSocketAddress(config.server.host, config.server.port), 0); Runtime.getRuntime().addShutdownHook(new Thread(() -> server.stop(0))); - serverConsumer.accept(server); + consumer.accept(server); server.start(); }).onSuccess((ignore) -> Logger.info("AP Agent started in: {}:{}{}", config.server.host, config.server.port, config.handler.context())) From 547f82f40844426f537f1bf49d12ec3a0a5d0fca Mon Sep 17 00:00:00 2001 From: Diego Date: Wed, 8 Feb 2023 21:11:29 -0300 Subject: [PATCH 02/19] * warn on banner error --- src/main/java/io/github/dpsoft/ap/util/Banner.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/dpsoft/ap/util/Banner.java b/src/main/java/io/github/dpsoft/ap/util/Banner.java index 00ba47e..15df846 100644 --- a/src/main/java/io/github/dpsoft/ap/util/Banner.java +++ b/src/main/java/io/github/dpsoft/ap/util/Banner.java @@ -3,6 +3,8 @@ import io.github.dpsoft.ap.config.AgentConfiguration; import io.github.dpsoft.ap.functions.Functions; import io.vavr.control.Try; +import org.tinylog.Logger; + import java.util.Map; import java.util.Scanner; @@ -34,7 +36,8 @@ public static void show(AgentConfiguration configuration) { } System.out.println(); return null; // void :( - }); + + }).onFailure((error) -> Logger.warn(error, () -> "It has not been possible to show the banner.")); } } From e7f8e348333b2d5ba3cbeae417a8e70c47ffad5e Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 11:35:35 -0300 Subject: [PATCH 03/19] minor refactor --- src/main/java/io/github/dpsoft/ap/Agent.java | 6 ++---- src/main/java/io/github/dpsoft/ap/util/Banner.java | 1 - .../github/dpsoft/ap/util/{Profiler.java => Runner.java} | 9 +++++---- 3 files changed, 7 insertions(+), 9 deletions(-) rename src/main/java/io/github/dpsoft/ap/util/{Profiler.java => Runner.java} (65%) diff --git a/src/main/java/io/github/dpsoft/ap/Agent.java b/src/main/java/io/github/dpsoft/ap/Agent.java index 03d04ec..442241f 100644 --- a/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/src/main/java/io/github/dpsoft/ap/Agent.java @@ -1,17 +1,15 @@ package io.github.dpsoft.ap; -import io.github.dpsoft.ap.config.AgentConfiguration; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; import io.github.dpsoft.ap.util.Banner; -import io.github.dpsoft.ap.util.Profiler; +import io.github.dpsoft.ap.util.Runner; import io.github.dpsoft.ap.util.Server; import java.lang.instrument.Instrumentation; public final class Agent { public static void premain(String args, Instrumentation inst) { - Profiler.with((profiler) -> { - final var configuration = AgentConfiguration.instance(); + Runner.runWith((profiler, configuration) -> { Banner.show(configuration); diff --git a/src/main/java/io/github/dpsoft/ap/util/Banner.java b/src/main/java/io/github/dpsoft/ap/util/Banner.java index 15df846..02a6ace 100644 --- a/src/main/java/io/github/dpsoft/ap/util/Banner.java +++ b/src/main/java/io/github/dpsoft/ap/util/Banner.java @@ -9,7 +9,6 @@ import java.util.Scanner; public final class Banner { - public static void show(AgentConfiguration configuration) { if (configuration.showBanner()) { final var version = (BuildInfo.version() == null ? "" : " v" + BuildInfo.version() + ""); diff --git a/src/main/java/io/github/dpsoft/ap/util/Profiler.java b/src/main/java/io/github/dpsoft/ap/util/Runner.java similarity index 65% rename from src/main/java/io/github/dpsoft/ap/util/Profiler.java rename to src/main/java/io/github/dpsoft/ap/util/Runner.java index e2ddca1..143fd4f 100644 --- a/src/main/java/io/github/dpsoft/ap/util/Profiler.java +++ b/src/main/java/io/github/dpsoft/ap/util/Runner.java @@ -1,17 +1,18 @@ package io.github.dpsoft.ap.util; +import io.github.dpsoft.ap.config.AgentConfiguration; import io.vavr.control.Try; import one.profiler.AsyncProfiler; import one.profiler.AsyncProfilerLoader; import org.tinylog.Logger; -import java.util.function.Consumer; +import java.util.function.BiConsumer; -public final class Profiler { - public static void with(Consumer consumer) { +public final class Runner { + public static void runWith(BiConsumer consumer) { if (!AsyncProfilerLoader.isSupported()) Logger.error("Async Profiler is not supported on this platform."); - Try.run(() -> consumer.accept(AsyncProfilerLoader.load())) + Try.run(() -> consumer.accept(AsyncProfilerLoader.load(), AgentConfiguration.instance())) .onFailure(cause -> Logger.error(cause, () -> "Error trying to load Async Profiler.")); } } From 881b114eb8bfa7b97dc5fedc1ba974e90c7678c5 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 11:43:20 -0300 Subject: [PATCH 04/19] add startup time --- src/main/java/io/github/dpsoft/ap/Agent.java | 5 +++++ .../java/io/github/dpsoft/ap/util/Runner.java | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/dpsoft/ap/Agent.java b/src/main/java/io/github/dpsoft/ap/Agent.java index 442241f..90d6de5 100644 --- a/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/src/main/java/io/github/dpsoft/ap/Agent.java @@ -19,4 +19,9 @@ public static void premain(String args, Instrumentation inst) { }); }); } + + public static void main(String[] args) { + premain(null, null); + while (true) {} + } } diff --git a/src/main/java/io/github/dpsoft/ap/util/Runner.java b/src/main/java/io/github/dpsoft/ap/util/Runner.java index 143fd4f..8fb8141 100644 --- a/src/main/java/io/github/dpsoft/ap/util/Runner.java +++ b/src/main/java/io/github/dpsoft/ap/util/Runner.java @@ -1,6 +1,7 @@ package io.github.dpsoft.ap.util; import io.github.dpsoft.ap.config.AgentConfiguration; +import io.vavr.CheckedRunnable; import io.vavr.control.Try; import one.profiler.AsyncProfiler; import one.profiler.AsyncProfilerLoader; @@ -8,11 +9,25 @@ import java.util.function.BiConsumer; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + public final class Runner { public static void runWith(BiConsumer consumer) { if (!AsyncProfilerLoader.isSupported()) Logger.error("Async Profiler is not supported on this platform."); - Try.run(() -> consumer.accept(AsyncProfilerLoader.load(), AgentConfiguration.instance())) + Try.run(timed(() -> consumer.accept(AsyncProfilerLoader.load(), AgentConfiguration.instance()))) .onFailure(cause -> Logger.error(cause, () -> "Error trying to load Async Profiler.")); } + + private static CheckedRunnable timed(final CheckedRunnable thunk) { + return () -> { + final var startMillis = System.nanoTime(); + try { thunk.run(); } + finally { + final var timeSpent = MILLISECONDS.convert((System.nanoTime() - startMillis), NANOSECONDS); + Logger.info(() -> "Startup completed in " + timeSpent + " ms"); + } + }; + } } From 718fac579a868dd1ee5f4595278003a50a9f1aba Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 14:53:39 -0300 Subject: [PATCH 05/19] add context --- pom.xml | 16 +++++++ src/main/java/io/github/dpsoft/ap/Agent.java | 25 ++++++++++- .../io/github/dpsoft/ap/ClassToOverride.java | 12 ++++++ .../io/github/dpsoft/ap/context/Context.java | 21 ++++++++++ .../dpsoft/ap/context/ContextStorage.java | 23 ++++++++++ .../io/github/dpsoft/ap/context/Storage.java | 42 +++++++++++++++++++ .../InstrumentationLoader.java | 42 +++++++++++++++++++ 7 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/github/dpsoft/ap/ClassToOverride.java create mode 100644 src/main/java/io/github/dpsoft/ap/context/Context.java create mode 100644 src/main/java/io/github/dpsoft/ap/context/ContextStorage.java create mode 100644 src/main/java/io/github/dpsoft/ap/context/Storage.java create mode 100644 src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java diff --git a/pom.xml b/pom.xml index a136fbd..77663c7 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,7 @@ ap-agent 0.1.1-SNAPSHOT jar + 2.9-4 0.10.3 @@ -60,6 +61,17 @@ jfrtofp ${me.bechberger.version} + + net.bytebuddy + byte-buddy + 1.12.22 + + + + net.bytebuddy + byte-buddy-agent + 1.12.22 + @@ -166,6 +178,10 @@ org.objectweb io.github.dpsoft.ap.libs.org.objectweb + + net.bytebuddy + io.github.dpsoft.ap.libs.net.bytebuddy + diff --git a/src/main/java/io/github/dpsoft/ap/Agent.java b/src/main/java/io/github/dpsoft/ap/Agent.java index 90d6de5..44e57d4 100644 --- a/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/src/main/java/io/github/dpsoft/ap/Agent.java @@ -1,16 +1,24 @@ package io.github.dpsoft.ap; +import io.github.dpsoft.ap.context.Context; +import io.github.dpsoft.ap.context.ContextStorage; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; +import io.github.dpsoft.ap.instrumentation.InstrumentationLoader; import io.github.dpsoft.ap.util.Banner; import io.github.dpsoft.ap.util.Runner; import io.github.dpsoft.ap.util.Server; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.ByteBuddyAgent; import java.lang.instrument.Instrumentation; +import java.util.Map; public final class Agent { public static void premain(String args, Instrumentation inst) { Runner.runWith((profiler, configuration) -> { + InstrumentationLoader.install(inst); + Banner.show(configuration); Server.with(configuration, (server) -> { @@ -21,7 +29,22 @@ public static void premain(String args, Instrumentation inst) { } public static void main(String[] args) { - premain(null, null); + premain(null, ByteBuddyAgent.install()); + + var contextStorage = new ClassToOverride(); + + var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { + var currentContext = contextStorage.currentContext(); + return currentContext; + }); + +// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); + + System.out.println(xx.contextId); + System.out.println(xx.tags); + + System.out.println(contextStorage.currentContext().contextId); + while (true) {} } } diff --git a/src/main/java/io/github/dpsoft/ap/ClassToOverride.java b/src/main/java/io/github/dpsoft/ap/ClassToOverride.java new file mode 100644 index 0000000..208552b --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/ClassToOverride.java @@ -0,0 +1,12 @@ +package io.github.dpsoft.ap; + +import io.github.dpsoft.ap.context.Context; +import io.github.dpsoft.ap.context.Storage; + +import java.util.concurrent.Callable; + +public class ClassToOverride { + public Context currentContext() { return null;} + public Storage.Scope storeContext(Context context) { return null; } + public T runWithContext(Context context, Callable callable) { return null; } +} diff --git a/src/main/java/io/github/dpsoft/ap/context/Context.java b/src/main/java/io/github/dpsoft/ap/context/Context.java new file mode 100644 index 0000000..c080839 --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/context/Context.java @@ -0,0 +1,21 @@ +package io.github.dpsoft.ap.context; + +import java.util.Map; + +public final class Context { + public final long contextId; + public final Map tags; + + public static Context of(long contextId, Map tags) { + return new Context(contextId, tags); + } + + public static Context empty() { + return new Context(0, Map.of()); + } + + public Context(long contextId, Map tags) { + this.contextId = contextId; + this.tags = tags; + } +} diff --git a/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java b/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java new file mode 100644 index 0000000..5c9c135 --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java @@ -0,0 +1,23 @@ +package io.github.dpsoft.ap.context; + +import io.vavr.control.Try; +import org.tinylog.Logger; + +import java.util.concurrent.Callable; + +public final class ContextStorage { + + public static final ContextStorage INSTANCE = new ContextStorage(); + + private final Storage storage = new Storage.ThreadLocalStorage(); + + public Context currentContext() { return storage.current();} + public Storage.Scope storeContext(Context context) { return storage.store(context); } + + public T runWithContext(Context context, Callable callable) { + return Try.withResources(() -> storeContext(context)) + .of(scope -> callable.call()) + .onFailure((cause) -> Logger.error(cause, "Error running callable with context")) + .get(); + } +} diff --git a/src/main/java/io/github/dpsoft/ap/context/Storage.java b/src/main/java/io/github/dpsoft/ap/context/Storage.java new file mode 100644 index 0000000..a41352b --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/context/Storage.java @@ -0,0 +1,42 @@ +package io.github.dpsoft.ap.context; + +import org.tinylog.Logger; + +public interface Storage { + Context current(); + Storage.Scope store(Context context); + + interface Scope extends AutoCloseable { + Context context(); + void close(); + } + + class ThreadLocalStorage implements Storage { + private final ThreadLocal tls = ThreadLocal.withInitial(Context::empty); + + @Override + public Context current() { return tls.get(); } + + @Override + public Scope store(Context context) { + final var previous = tls.get(); + tls.set(context); + Logger.info("store::AsyncProfiler.setContextId(" +context.contextId+")"); + +// AsyncProfilerUtils.load().setContextId(context.contextId); + + return new Scope() { + @Override + public Context context() { return context; } + + @Override + public void close() { + tls.set(previous); + Logger.info("close::AsyncProfiler.setContextId(" +context.contextId+")"); + //AsyncProfiler.setContextId(context.contextId); + } + }; + } + } +} + diff --git a/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java b/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java new file mode 100644 index 0000000..a008922 --- /dev/null +++ b/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java @@ -0,0 +1,42 @@ +package io.github.dpsoft.ap.instrumentation; + +import io.github.dpsoft.ap.context.Context; +import io.github.dpsoft.ap.context.ContextStorage; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; + +import java.lang.instrument.Instrumentation; +import java.util.concurrent.Callable; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; + +public class InstrumentationLoader { + public static void install(Instrumentation instrumentation) { + new AgentBuilder.Default() +// .with(AgentBuilder.Listener.StreamWriting.toSystemError()) + .with(RedefinitionStrategy.RETRANSFORMATION) + .type(named("io.github.dpsoft.ap.ClassToOverride")) + .transform((builder, typeDescription, classLoader, module, transformer) -> + builder.method(namedOneOf("runWithContext", "currentContext", "storeContext")) + .intercept(MethodDelegation.to(new ContextStorageInterceptor()))) + .installOn(instrumentation); + } + + public static class ContextStorageInterceptor { + @RuntimeType + public Object onRunWithContext(Context context, Callable callable) { + return ContextStorage.INSTANCE.runWithContext(context, callable); + } + @RuntimeType + public Context currentContext() { + return ContextStorage.INSTANCE.currentContext(); + } + @RuntimeType + public Object storeContext(Context context) { + return ContextStorage.INSTANCE.storeContext(context); + } + } +} From 1d24012630e1d10fa289357586226c325f1befb8 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 16:49:02 -0300 Subject: [PATCH 06/19] WIP: contextId support --- agent/pom.xml | 202 ++++++++++++++++++ .../main/java/io/github/dpsoft/ap/Agent.java | 39 ++-- .../io/github/dpsoft/ap/command/Command.java | 0 .../dpsoft/ap/config/AgentConfiguration.java | 0 .../dpsoft/ap/context/ContextStorage.java | 4 +- .../dpsoft/ap/context/ThreadLocalStorage.java | 23 +- .../hotcold/HotColdFlameGraph.java | 0 .../hotcold/jfr2hotcoldflame.java | 0 .../converters/experimental/package-info.java | 0 .../github/dpsoft/ap/functions/Functions.java | 0 .../ap/handler/AsyncProfilerHandler.java | 0 .../InstrumentationLoader.java | 6 +- .../java/io/github/dpsoft/ap/util/Banner.java | 0 .../io/github/dpsoft/ap/util/BuildInfo.java | 0 .../dpsoft/ap/util/ProfilerExecutor.java | 0 .../java/io/github/dpsoft/ap/util/Runner.java | 0 .../java/io/github/dpsoft/ap/util/Server.java | 0 .../github/dpsoft/ap/util/StrSubstitutor.java | 0 .../main/java/one/converter/Arguments.java | 0 .../java/one/converter/CollapsedStacks.java | 0 {src => agent/src}/main/resources/banner.txt | 0 .../src}/main/resources/build-info.properties | 0 .../src}/main/resources/reference.conf | 0 .../src}/main/resources/tinylog.properties | 0 context-api/pom.xml | 15 ++ .../dpsoft/ap/context/api}/Context.java | 6 +- .../dpsoft/ap/context/api/ContextHandler.java | 6 +- .../github/dpsoft/ap/context/api/Storage.java | 13 ++ pom.xml | 185 +--------------- 29 files changed, 275 insertions(+), 224 deletions(-) create mode 100644 agent/pom.xml rename {src => agent/src}/main/java/io/github/dpsoft/ap/Agent.java (53%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/command/Command.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/context/ContextStorage.java (81%) rename src/main/java/io/github/dpsoft/ap/context/Storage.java => agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java (64%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/HotColdFlameGraph.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/converters/experimental/package-info.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/functions/Functions.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java (89%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/Banner.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/BuildInfo.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/Runner.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/Server.java (100%) rename {src => agent/src}/main/java/io/github/dpsoft/ap/util/StrSubstitutor.java (100%) rename {src => agent/src}/main/java/one/converter/Arguments.java (100%) rename {src => agent/src}/main/java/one/converter/CollapsedStacks.java (100%) rename {src => agent/src}/main/resources/banner.txt (100%) rename {src => agent/src}/main/resources/build-info.properties (100%) rename {src => agent/src}/main/resources/reference.conf (100%) rename {src => agent/src}/main/resources/tinylog.properties (100%) create mode 100644 context-api/pom.xml rename {src/main/java/io/github/dpsoft/ap/context => context-api/src/main/java/io/github/dpsoft/ap/context/api}/Context.java (91%) rename src/main/java/io/github/dpsoft/ap/ClassToOverride.java => context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java (63%) create mode 100644 context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java diff --git a/agent/pom.xml b/agent/pom.xml new file mode 100644 index 0000000..a1cc286 --- /dev/null +++ b/agent/pom.xml @@ -0,0 +1,202 @@ + + + 4.0.0 + + io.github.dpsoft + ap-agent + 0.1.1-SNAPSHOT + jar + + + 2.9-4 + 0.10.3 + 1.4.2 + 2.5.0 + 0.0.2-SNAPSHOT + 11 + 11 + 11 + + + + + oss-snapshots + Oss Snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + maven-central + Maven central + https://repo.maven.apache.org/maven2/ + + + + + + me.bechberger + ap-loader-all + ${me.bechberger.ap-loader.version} + + + io.vavr + vavr + ${io.vavr.version} + + + com.typesafe + config + ${com.typesafe.config.version} + + + org.tinylog + tinylog-api + ${org.tinylog.version} + + + org.tinylog + tinylog-impl + ${org.tinylog.version} + + + me.bechberger + jfrtofp + ${me.bechberger.version} + + + net.bytebuddy + byte-buddy + 1.12.22 + + + + + + + + + + io.github.dspoft.ap + context-api + 0.1.1-SNAPSHOT + provided + + + + + + src/main/resources + true + + **/build-info.properties + + + + src/main/resources + false + + **/build-info.properties + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + true + ${project.build.directory}/dependency-reduced-pom.xml + true + + + org.codehaus.mojo + + + + + + io.github.dpsoft.ap.Agent + + + + + + + *:* + + META-INF/maven/** + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*.md + META-INF/LICENSE + + + + + + com.typesafe.config + io.github.dpsoft.ap.libs.com.typesafe.config + + + io.vavr + io.github.dpsoft.ap.libs.io.vavr + + + org.tinylog + io.github.dpsoft.ap.libs.org.tinylog + + + one + io.github.dpsoft.ap.libs.one + + + dev.dirs + io.github.dpsoft.ap.libs.dev.dirs + + + me.bechberger + io.github.dpsoft.ap.libs.me.bechberger + + + kotlin + io.github.dpsoft.ap.libs.kotlin + + + kotlinx + io.github.dpsoft.ap.libs.kotlinx + + + org.jetbrains + io.github.dpsoft.ap.libs.org.jetbrains + + + org.jline + io.github.dpsoft.ap.libs.org.jline + + + org.objectweb + io.github.dpsoft.ap.libs.org.objectweb + + + net.bytebuddy + io.github.dpsoft.ap.libs.net.bytebuddy + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/io/github/dpsoft/ap/Agent.java b/agent/src/main/java/io/github/dpsoft/ap/Agent.java similarity index 53% rename from src/main/java/io/github/dpsoft/ap/Agent.java rename to agent/src/main/java/io/github/dpsoft/ap/Agent.java index 44e57d4..6f1d778 100644 --- a/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/agent/src/main/java/io/github/dpsoft/ap/Agent.java @@ -1,17 +1,12 @@ package io.github.dpsoft.ap; -import io.github.dpsoft.ap.context.Context; -import io.github.dpsoft.ap.context.ContextStorage; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; import io.github.dpsoft.ap.instrumentation.InstrumentationLoader; import io.github.dpsoft.ap.util.Banner; import io.github.dpsoft.ap.util.Runner; import io.github.dpsoft.ap.util.Server; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.ByteBuddyAgent; import java.lang.instrument.Instrumentation; -import java.util.Map; public final class Agent { public static void premain(String args, Instrumentation inst) { @@ -29,22 +24,22 @@ public static void premain(String args, Instrumentation inst) { } public static void main(String[] args) { - premain(null, ByteBuddyAgent.install()); - - var contextStorage = new ClassToOverride(); - - var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { - var currentContext = contextStorage.currentContext(); - return currentContext; - }); - -// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); - - System.out.println(xx.contextId); - System.out.println(xx.tags); - - System.out.println(contextStorage.currentContext().contextId); - - while (true) {} +// premain(null, ByteBuddyAgent.install()); +// +// var contextStorage = new ContextHandler(); +// +// var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { +// var currentContext = contextStorage.currentContext(); +// return currentContext; +// }); +// +//// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); +// +// System.out.println(xx.contextId); +// System.out.println(xx.tags); +// +// System.out.println(contextStorage.currentContext().contextId); +// +// while (true) {} } } diff --git a/src/main/java/io/github/dpsoft/ap/command/Command.java b/agent/src/main/java/io/github/dpsoft/ap/command/Command.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/command/Command.java rename to agent/src/main/java/io/github/dpsoft/ap/command/Command.java diff --git a/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java b/agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java rename to agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java diff --git a/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java similarity index 81% rename from src/main/java/io/github/dpsoft/ap/context/ContextStorage.java rename to agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java index 5c9c135..e499c28 100644 --- a/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java @@ -1,5 +1,7 @@ package io.github.dpsoft.ap.context; +import io.github.dpsoft.ap.context.api.Context; +import io.github.dpsoft.ap.context.api.Storage; import io.vavr.control.Try; import org.tinylog.Logger; @@ -9,7 +11,7 @@ public final class ContextStorage { public static final ContextStorage INSTANCE = new ContextStorage(); - private final Storage storage = new Storage.ThreadLocalStorage(); + private final Storage storage = new ThreadLocalStorage(); public Context currentContext() { return storage.current();} public Storage.Scope storeContext(Context context) { return storage.store(context); } diff --git a/src/main/java/io/github/dpsoft/ap/context/Storage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java similarity index 64% rename from src/main/java/io/github/dpsoft/ap/context/Storage.java rename to agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java index a41352b..f4c4172 100644 --- a/src/main/java/io/github/dpsoft/ap/context/Storage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java @@ -1,17 +1,10 @@ package io.github.dpsoft.ap.context; +import io.github.dpsoft.ap.context.api.Context; +import io.github.dpsoft.ap.context.api.Storage; import org.tinylog.Logger; -public interface Storage { - Context current(); - Storage.Scope store(Context context); - - interface Scope extends AutoCloseable { - Context context(); - void close(); - } - - class ThreadLocalStorage implements Storage { +public class ThreadLocalStorage implements Storage { private final ThreadLocal tls = ThreadLocal.withInitial(Context::empty); @Override @@ -21,22 +14,22 @@ class ThreadLocalStorage implements Storage { public Scope store(Context context) { final var previous = tls.get(); tls.set(context); - Logger.info("store::AsyncProfiler.setContextId(" +context.contextId+")"); + Logger.info("store::AsyncProfiler.setContextId(" + context.contextId + ")"); // AsyncProfilerUtils.load().setContextId(context.contextId); return new Scope() { @Override - public Context context() { return context; } + public Context context() { + return context; + } @Override public void close() { tls.set(previous); - Logger.info("close::AsyncProfiler.setContextId(" +context.contextId+")"); + Logger.info("close::AsyncProfiler.setContextId(" + context.contextId + ")"); //AsyncProfiler.setContextId(context.contextId); } }; } - } } - diff --git a/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/HotColdFlameGraph.java b/agent/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/HotColdFlameGraph.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/HotColdFlameGraph.java rename to agent/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/HotColdFlameGraph.java diff --git a/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java b/agent/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java rename to agent/src/main/java/io/github/dpsoft/ap/converters/experimental/hotcold/jfr2hotcoldflame.java diff --git a/src/main/java/io/github/dpsoft/ap/converters/experimental/package-info.java b/agent/src/main/java/io/github/dpsoft/ap/converters/experimental/package-info.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/converters/experimental/package-info.java rename to agent/src/main/java/io/github/dpsoft/ap/converters/experimental/package-info.java diff --git a/src/main/java/io/github/dpsoft/ap/functions/Functions.java b/agent/src/main/java/io/github/dpsoft/ap/functions/Functions.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/functions/Functions.java rename to agent/src/main/java/io/github/dpsoft/ap/functions/Functions.java diff --git a/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java b/agent/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java rename to agent/src/main/java/io/github/dpsoft/ap/handler/AsyncProfilerHandler.java diff --git a/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java similarity index 89% rename from src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java rename to agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java index a008922..343543f 100644 --- a/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java +++ b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java @@ -1,7 +1,7 @@ package io.github.dpsoft.ap.instrumentation; -import io.github.dpsoft.ap.context.Context; import io.github.dpsoft.ap.context.ContextStorage; +import io.github.dpsoft.ap.context.api.Context; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; import net.bytebuddy.implementation.MethodDelegation; @@ -17,8 +17,8 @@ public class InstrumentationLoader { public static void install(Instrumentation instrumentation) { new AgentBuilder.Default() // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) - .with(RedefinitionStrategy.RETRANSFORMATION) - .type(named("io.github.dpsoft.ap.ClassToOverride")) +// .with(RedefinitionStrategy.RETRANSFORMATION) + .type(named("io.github.dpsoft.ap.context.api.ContextHandler")) .transform((builder, typeDescription, classLoader, module, transformer) -> builder.method(namedOneOf("runWithContext", "currentContext", "storeContext")) .intercept(MethodDelegation.to(new ContextStorageInterceptor()))) diff --git a/src/main/java/io/github/dpsoft/ap/util/Banner.java b/agent/src/main/java/io/github/dpsoft/ap/util/Banner.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/Banner.java rename to agent/src/main/java/io/github/dpsoft/ap/util/Banner.java diff --git a/src/main/java/io/github/dpsoft/ap/util/BuildInfo.java b/agent/src/main/java/io/github/dpsoft/ap/util/BuildInfo.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/BuildInfo.java rename to agent/src/main/java/io/github/dpsoft/ap/util/BuildInfo.java diff --git a/src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java b/agent/src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java rename to agent/src/main/java/io/github/dpsoft/ap/util/ProfilerExecutor.java diff --git a/src/main/java/io/github/dpsoft/ap/util/Runner.java b/agent/src/main/java/io/github/dpsoft/ap/util/Runner.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/Runner.java rename to agent/src/main/java/io/github/dpsoft/ap/util/Runner.java diff --git a/src/main/java/io/github/dpsoft/ap/util/Server.java b/agent/src/main/java/io/github/dpsoft/ap/util/Server.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/Server.java rename to agent/src/main/java/io/github/dpsoft/ap/util/Server.java diff --git a/src/main/java/io/github/dpsoft/ap/util/StrSubstitutor.java b/agent/src/main/java/io/github/dpsoft/ap/util/StrSubstitutor.java similarity index 100% rename from src/main/java/io/github/dpsoft/ap/util/StrSubstitutor.java rename to agent/src/main/java/io/github/dpsoft/ap/util/StrSubstitutor.java diff --git a/src/main/java/one/converter/Arguments.java b/agent/src/main/java/one/converter/Arguments.java similarity index 100% rename from src/main/java/one/converter/Arguments.java rename to agent/src/main/java/one/converter/Arguments.java diff --git a/src/main/java/one/converter/CollapsedStacks.java b/agent/src/main/java/one/converter/CollapsedStacks.java similarity index 100% rename from src/main/java/one/converter/CollapsedStacks.java rename to agent/src/main/java/one/converter/CollapsedStacks.java diff --git a/src/main/resources/banner.txt b/agent/src/main/resources/banner.txt similarity index 100% rename from src/main/resources/banner.txt rename to agent/src/main/resources/banner.txt diff --git a/src/main/resources/build-info.properties b/agent/src/main/resources/build-info.properties similarity index 100% rename from src/main/resources/build-info.properties rename to agent/src/main/resources/build-info.properties diff --git a/src/main/resources/reference.conf b/agent/src/main/resources/reference.conf similarity index 100% rename from src/main/resources/reference.conf rename to agent/src/main/resources/reference.conf diff --git a/src/main/resources/tinylog.properties b/agent/src/main/resources/tinylog.properties similarity index 100% rename from src/main/resources/tinylog.properties rename to agent/src/main/resources/tinylog.properties diff --git a/context-api/pom.xml b/context-api/pom.xml new file mode 100644 index 0000000..1ac8125 --- /dev/null +++ b/context-api/pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + + io.github.dpsoft.ap + context-api + 0.1.1-SNAPSHOT + jar + + + + 11 + 11 + 11 + + \ No newline at end of file diff --git a/src/main/java/io/github/dpsoft/ap/context/Context.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java similarity index 91% rename from src/main/java/io/github/dpsoft/ap/context/Context.java rename to context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java index c080839..735104c 100644 --- a/src/main/java/io/github/dpsoft/ap/context/Context.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java @@ -1,4 +1,4 @@ -package io.github.dpsoft.ap.context; +package io.github.dpsoft.ap.context.api; import java.util.Map; @@ -13,9 +13,9 @@ public static Context of(long contextId, Map tags) { public static Context empty() { return new Context(0, Map.of()); } - + public Context(long contextId, Map tags) { this.contextId = contextId; this.tags = tags; } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dpsoft/ap/ClassToOverride.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java similarity index 63% rename from src/main/java/io/github/dpsoft/ap/ClassToOverride.java rename to context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java index 208552b..c8fc643 100644 --- a/src/main/java/io/github/dpsoft/ap/ClassToOverride.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java @@ -1,11 +1,9 @@ -package io.github.dpsoft.ap; +package io.github.dpsoft.ap.context.api; -import io.github.dpsoft.ap.context.Context; -import io.github.dpsoft.ap.context.Storage; import java.util.concurrent.Callable; -public class ClassToOverride { +public class ContextHandler { public Context currentContext() { return null;} public Storage.Scope storeContext(Context context) { return null; } public T runWithContext(Context context, Callable callable) { return null; } diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java new file mode 100644 index 0000000..4063c58 --- /dev/null +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java @@ -0,0 +1,13 @@ +package io.github.dpsoft.ap.context.api; + +public interface Storage { + Context current(); + + Storage.Scope store(Context context); + + interface Scope extends AutoCloseable { + Context context(); + + void close(); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 77663c7..6c9e543 100644 --- a/pom.xml +++ b/pom.xml @@ -1,192 +1,25 @@ - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 io.github.dpsoft - ap-agent + ap-agent-multi + pom 0.1.1-SNAPSHOT - jar - 2.9-4 - 0.10.3 - 1.4.2 - 2.5.0 - 0.0.2-SNAPSHOT + 11 - - - oss-snapshots - Oss Snapshots - https://s01.oss.sonatype.org/content/repositories/snapshots/ - - - maven-central - Maven central - https://repo.maven.apache.org/maven2/ - - + + agent + context-api + - - - me.bechberger - ap-loader-all - ${me.bechberger.ap-loader.version} - - - io.vavr - vavr - ${io.vavr.version} - - - com.typesafe - config - ${com.typesafe.config.version} - - - org.tinylog - tinylog-api - ${org.tinylog.version} - - - org.tinylog - tinylog-impl - ${org.tinylog.version} - - - me.bechberger - jfrtofp - ${me.bechberger.version} - - - net.bytebuddy - byte-buddy - 1.12.22 - - - - net.bytebuddy - byte-buddy-agent - 1.12.22 - - - - - src/main/resources - true - - **/build-info.properties - - - - src/main/resources - false - - **/build-info.properties - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.4.1 - - - package - - shade - - - true - ${project.build.directory}/dependency-reduced-pom.xml - true - - - org.codehaus.mojo - - - - - - io.github.dpsoft.ap.Agent - - - - - - - *:* - - META-INF/maven/** - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/*.md - META-INF/LICENSE - - - - - - com.typesafe.config - io.github.dpsoft.ap.libs.com.typesafe.config - - - io.vavr - io.github.dpsoft.ap.libs.io.vavr - - - org.tinylog - io.github.dpsoft.ap.libs.org.tinylog - - - one - io.github.dpsoft.ap.libs.one - - - dev.dirs - io.github.dpsoft.ap.libs.dev.dirs - - - me.bechberger - io.github.dpsoft.ap.libs.me.bechberger - - - kotlin - io.github.dpsoft.ap.libs.kotlin - - - kotlinx - io.github.dpsoft.ap.libs.kotlinx - - - org.jetbrains - io.github.dpsoft.ap.libs.org.jetbrains - - - org.jline - io.github.dpsoft.ap.libs.org.jline - - - org.objectweb - io.github.dpsoft.ap.libs.org.objectweb - - - net.bytebuddy - io.github.dpsoft.ap.libs.net.bytebuddy - - - - - - org.apache.maven.plugins maven-source-plugin From 4d845fe6acd026b5837323016bc289cf269c9459 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 17:38:17 -0300 Subject: [PATCH 07/19] cleanup --- .../dpsoft/ap/context/ContextStorage.java | 6 ++- .../dpsoft/ap/context/ThreadLocalStorage.java | 49 ++++++++++--------- .../InstrumentationLoader.java | 22 ++++++--- .../dpsoft/ap/context/api/ContextHandler.java | 25 ++++++++-- .../github/dpsoft/ap/context/api/Storage.java | 22 +++++++++ 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java index e499c28..d9a73d4 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java @@ -7,10 +7,12 @@ import java.util.concurrent.Callable; +/** + * This class is used to store and retrieve the current context. + * Its is the real implementation that is injected by the agent in runtime to replace the Noop one. see {@link io.github.dpsoft.ap.context.api.ContextHandler} + */ public final class ContextStorage { - public static final ContextStorage INSTANCE = new ContextStorage(); - private final Storage storage = new ThreadLocalStorage(); public Context currentContext() { return storage.current();} diff --git a/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java index f4c4172..eb95a97 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java @@ -4,32 +4,33 @@ import io.github.dpsoft.ap.context.api.Storage; import org.tinylog.Logger; +/** + * Default implementation of {@link Storage} that stores the context in a {@link ThreadLocal}. + */ public class ThreadLocalStorage implements Storage { - private final ThreadLocal tls = ThreadLocal.withInitial(Context::empty); + private final ThreadLocal tls = ThreadLocal.withInitial(Context::empty); - @Override - public Context current() { return tls.get(); } + @Override + public Context current() { return tls.get(); } - @Override - public Scope store(Context context) { - final var previous = tls.get(); - tls.set(context); - Logger.info("store::AsyncProfiler.setContextId(" + context.contextId + ")"); + @Override + public Scope store(Context context) { + final var previous = tls.get(); + tls.set(context); + Logger.info("store::AsyncProfiler.setContextId(" + context.contextId + ")"); + //AsyncProfiler.setContextId(context.contextId); + return new Scope() { + @Override + public Context context() { + return context; + } -// AsyncProfilerUtils.load().setContextId(context.contextId); - - return new Scope() { - @Override - public Context context() { - return context; - } - - @Override - public void close() { - tls.set(previous); - Logger.info("close::AsyncProfiler.setContextId(" + context.contextId + ")"); - //AsyncProfiler.setContextId(context.contextId); - } - }; - } + @Override + public void close() { + tls.set(previous); + Logger.info("close::AsyncProfiler.setContextId(" + context.contextId + ")"); + //AsyncProfiler.setContextId(context.contextId); + } + }; + } } diff --git a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java index 343543f..73470bb 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java +++ b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java @@ -3,9 +3,10 @@ import io.github.dpsoft.ap.context.ContextStorage; import io.github.dpsoft.ap.context.api.Context; import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.utility.JavaModule; +import org.tinylog.Logger; import java.lang.instrument.Instrumentation; import java.util.concurrent.Callable; @@ -13,11 +14,13 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; -public class InstrumentationLoader { +import net.bytebuddy.agent.builder.AgentBuilder.Listener; + + +public final class InstrumentationLoader { public static void install(Instrumentation instrumentation) { new AgentBuilder.Default() -// .with(AgentBuilder.Listener.StreamWriting.toSystemError()) -// .with(RedefinitionStrategy.RETRANSFORMATION) + .with(new ByteBuddyErrorListener()) .type(named("io.github.dpsoft.ap.context.api.ContextHandler")) .transform((builder, typeDescription, classLoader, module, transformer) -> builder.method(namedOneOf("runWithContext", "currentContext", "storeContext")) @@ -31,12 +34,17 @@ public Object onRunWithContext(Context context, Callable callable) { return ContextStorage.INSTANCE.runWithContext(context, callable); } @RuntimeType - public Context currentContext() { - return ContextStorage.INSTANCE.currentContext(); - } + public Context currentContext() { return ContextStorage.INSTANCE.currentContext();} @RuntimeType public Object storeContext(Context context) { return ContextStorage.INSTANCE.storeContext(context); } } + + private static class ByteBuddyErrorListener extends Listener.Adapter { + @Override + public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable cause) { + Logger.error(cause, "Error trying to instrument class {}", typeName); + } + } } diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java index c8fc643..c828d68 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java @@ -3,8 +3,27 @@ import java.util.concurrent.Callable; +/** + * This class is used to store and retrieve the current context. + * Its implementation is Noop by default, but it can be replaced by an agent in runtime. + */ public class ContextHandler { - public Context currentContext() { return null;} - public Storage.Scope storeContext(Context context) { return null; } - public T runWithContext(Context context, Callable callable) { return null; } + /** + * Returns the current context. + */ + public Context currentContext() { return Context.empty(); } + /** + * Stores the current context and returns a scope that can be used to restore it. + */ + public Storage.Scope storeContext(Context context) { return Storage.Scope.Empty.INSTANCE;} + + /** + * Runs the given callable with the given context. + * The context is restored after the callable is executed. + */ + public T runWithContext(Context context, Callable callable) { + try (var ignore = storeContext(currentContext())) {} + try { return callable.call(); } + catch (Exception e) { throw new RuntimeException(e);} + } } diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java index 4063c58..04b5f43 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java @@ -9,5 +9,27 @@ interface Scope extends AutoCloseable { Context context(); void close(); + + enum Empty implements Scope { + INSTANCE; + + @Override + public Context context() { return Context.empty(); } + + @Override + public void close() { } + } + } + + enum Empty implements Storage { + INSTANCE; + + @Override + public Context current() { return Context.empty(); } + + @Override + public Scope store(Context context) { + return Scope.Empty.INSTANCE; + } } } \ No newline at end of file From 8c9043ac194fde9e81da37ebb01dbbff0dccb399 Mon Sep 17 00:00:00 2001 From: Diego Date: Thu, 9 Feb 2023 18:13:16 -0300 Subject: [PATCH 08/19] clean up --- .../main/java/io/github/dpsoft/ap/context/api/Context.java | 5 ++++- .../java/io/github/dpsoft/ap/context/api/ContextHandler.java | 2 +- .../main/java/io/github/dpsoft/ap/context/api/Storage.java | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java index 735104c..352b004 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java @@ -3,7 +3,10 @@ import java.util.Map; public final class Context { + public static final Context EMPTY = new Context(0, Map.of()); + public final long contextId; + public final Map tags; public static Context of(long contextId, Map tags) { @@ -11,7 +14,7 @@ public static Context of(long contextId, Map tags) { } public static Context empty() { - return new Context(0, Map.of()); + return EMPTY; } public Context(long contextId, Map tags) { diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java index c828d68..615d61b 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java @@ -11,7 +11,7 @@ public class ContextHandler { /** * Returns the current context. */ - public Context currentContext() { return Context.empty(); } + public Context currentContext() { return Context.EMPTY; } /** * Stores the current context and returns a scope that can be used to restore it. */ diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java index 04b5f43..12ce3e9 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java @@ -14,7 +14,7 @@ enum Empty implements Scope { INSTANCE; @Override - public Context context() { return Context.empty(); } + public Context context() { return Context.EMPTY; } @Override public void close() { } @@ -25,7 +25,7 @@ enum Empty implements Storage { INSTANCE; @Override - public Context current() { return Context.empty(); } + public Context current() { return Context.EMPTY; } @Override public Scope store(Context context) { From 3d65e4657e63aff1753eb284eddbcff9f7845ff7 Mon Sep 17 00:00:00 2001 From: Diego Date: Fri, 10 Feb 2023 12:19:08 -0300 Subject: [PATCH 09/19] first attempt of ContextInstrumenter --- .../main/java/io/github/dpsoft/ap/Agent.java | 26 +------- .../dpsoft/ap/config/AgentConfiguration.java | 16 +++++ .../dpsoft/ap/context/ContextStorage.java | 1 + .../instrumentation/ContextInstrumenter.java | 60 +++++++++++++++++++ .../InstrumentationLoader.java | 50 ---------------- agent/src/main/resources/reference.conf | 6 ++ .../github/dpsoft/ap/context/api/Storage.java | 8 +-- 7 files changed, 90 insertions(+), 77 deletions(-) create mode 100644 agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java delete mode 100644 agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java diff --git a/agent/src/main/java/io/github/dpsoft/ap/Agent.java b/agent/src/main/java/io/github/dpsoft/ap/Agent.java index 6f1d778..440e364 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/agent/src/main/java/io/github/dpsoft/ap/Agent.java @@ -1,7 +1,7 @@ package io.github.dpsoft.ap; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; -import io.github.dpsoft.ap.instrumentation.InstrumentationLoader; +import io.github.dpsoft.ap.instrumentation.ContextInstrumenter; import io.github.dpsoft.ap.util.Banner; import io.github.dpsoft.ap.util.Runner; import io.github.dpsoft.ap.util.Server; @@ -9,10 +9,10 @@ import java.lang.instrument.Instrumentation; public final class Agent { - public static void premain(String args, Instrumentation inst) { + public static void premain(String args, Instrumentation instrumentation) { Runner.runWith((profiler, configuration) -> { - InstrumentationLoader.install(inst); + ContextInstrumenter.install(instrumentation, configuration.instrumenter); Banner.show(configuration); @@ -22,24 +22,4 @@ public static void premain(String args, Instrumentation inst) { }); }); } - - public static void main(String[] args) { -// premain(null, ByteBuddyAgent.install()); -// -// var contextStorage = new ContextHandler(); -// -// var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { -// var currentContext = contextStorage.currentContext(); -// return currentContext; -// }); -// -//// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); -// -// System.out.println(xx.contextId); -// System.out.println(xx.tags); -// -// System.out.println(contextStorage.currentContext().contextId); -// -// while (true) {} - } } diff --git a/agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java b/agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java index 050f09f..e245a1a 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java +++ b/agent/src/main/java/io/github/dpsoft/ap/config/AgentConfiguration.java @@ -12,11 +12,16 @@ public final class AgentConfiguration { public final Server server; public final Handler handler; + + public Instrumenter instrumenter; + public final boolean showBanner; + private AgentConfiguration(Config config) { this.server = new Server(config); this.handler = new Handler(config); + this.instrumenter = new Instrumenter(config); this.showBanner = config.getBoolean("show-banner"); } @@ -48,6 +53,17 @@ public boolean isGoMode() { public String context() { return isGoMode() ? goContext : context;} } + public static class Instrumenter { + public final boolean enabled; + public Instrumenter(Config config) { + this.enabled = config.getBoolean("context-instrumenter.enabled"); + } + + public boolean shouldInstall() { + return enabled; + } + } + public boolean showBanner() { return this.showBanner; } diff --git a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java index d9a73d4..a7d6c4f 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java @@ -16,6 +16,7 @@ public final class ContextStorage { private final Storage storage = new ThreadLocalStorage(); public Context currentContext() { return storage.current();} + public Storage.Scope storeContext(Context context) { return storage.store(context); } public T runWithContext(Context context, Callable callable) { diff --git a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java new file mode 100644 index 0000000..e15d5ef --- /dev/null +++ b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java @@ -0,0 +1,60 @@ +package io.github.dpsoft.ap.instrumentation; + +import io.github.dpsoft.ap.config.AgentConfiguration; +import io.github.dpsoft.ap.context.ContextStorage; +import io.github.dpsoft.ap.context.api.Context; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.utility.JavaModule; +import org.tinylog.Logger; + +import java.lang.instrument.Instrumentation; +import java.util.concurrent.Callable; + +import static net.bytebuddy.matcher.ElementMatchers.*; + +import net.bytebuddy.agent.builder.AgentBuilder.Listener; + + +public final class ContextInstrumenter { + /** + * Installs the ContextInstrumenter, this approach is based on ByteBuddy Interceptors mechanism, maybe in the future we can use + * ByteBuddy's Advice mechanism or MemberSubstitution. + */ + public static void install(Instrumentation instrumentation, AgentConfiguration.Instrumenter config) { + if (!config.shouldInstall()) Logger.info("Context Instrumenter is disabled, nothing to do..."); + new AgentBuilder.Default() + .with(new ByteBuddyListener()) + .type(named("io.github.dpsoft.ap.context.api.ContextHandler")) + .transform((builder, typeDescription, classLoader, module, transformer) -> + builder.method(namedOneOf("runWithContext", "currentContext", "storeContext")) + .intercept(MethodDelegation.to(ContextHandlerInterceptor.class))) + .installOn(instrumentation); + } + + public static class ContextHandlerInterceptor { + @RuntimeType + public static Object onRunWithContext(Context context, Callable callable) { return ContextStorage.INSTANCE.runWithContext(context, callable);} + @RuntimeType + public static Context onCurrentContext() { return ContextStorage.INSTANCE.currentContext();} + @RuntimeType + public static Object onStoreContext(Context context) { + return ContextStorage.INSTANCE.storeContext(context); + } + } + + private static class ByteBuddyListener extends Listener.Adapter { + @Override + public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable cause) { + Logger.error(cause, "Error trying to instrument class {}", typeName); + } + + @Override + public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) { + Logger.info("Instrumented class {}", typeDescription.getName()); + } + } +} diff --git a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java deleted file mode 100644 index 73470bb..0000000 --- a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/InstrumentationLoader.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.dpsoft.ap.instrumentation; - -import io.github.dpsoft.ap.context.ContextStorage; -import io.github.dpsoft.ap.context.api.Context; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.implementation.MethodDelegation; -import net.bytebuddy.implementation.bind.annotation.RuntimeType; -import net.bytebuddy.utility.JavaModule; -import org.tinylog.Logger; - -import java.lang.instrument.Instrumentation; -import java.util.concurrent.Callable; - -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; - -import net.bytebuddy.agent.builder.AgentBuilder.Listener; - - -public final class InstrumentationLoader { - public static void install(Instrumentation instrumentation) { - new AgentBuilder.Default() - .with(new ByteBuddyErrorListener()) - .type(named("io.github.dpsoft.ap.context.api.ContextHandler")) - .transform((builder, typeDescription, classLoader, module, transformer) -> - builder.method(namedOneOf("runWithContext", "currentContext", "storeContext")) - .intercept(MethodDelegation.to(new ContextStorageInterceptor()))) - .installOn(instrumentation); - } - - public static class ContextStorageInterceptor { - @RuntimeType - public Object onRunWithContext(Context context, Callable callable) { - return ContextStorage.INSTANCE.runWithContext(context, callable); - } - @RuntimeType - public Context currentContext() { return ContextStorage.INSTANCE.currentContext();} - @RuntimeType - public Object storeContext(Context context) { - return ContextStorage.INSTANCE.storeContext(context); - } - } - - private static class ByteBuddyErrorListener extends Listener.Adapter { - @Override - public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable cause) { - Logger.error(cause, "Error trying to instrument class {}", typeName); - } - } -} diff --git a/agent/src/main/resources/reference.conf b/agent/src/main/resources/reference.conf index a573ad5..c9a1c4b 100644 --- a/agent/src/main/resources/reference.conf +++ b/agent/src/main/resources/reference.conf @@ -9,9 +9,15 @@ ap-agent { port: 8080 } + # The agent will recive requests on this path handler { go-mode: false go-context: "/debug/pprof/profile" context: "/profiler/profile" } + + # Bytecode instrumentation is disabled by default + context-instrumenter { + enabled = true + } } \ No newline at end of file diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java index 12ce3e9..2f5442e 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Storage.java @@ -1,5 +1,8 @@ package io.github.dpsoft.ap.context.api; +/** + * This interface is used to define the storage of the current context. + */ public interface Storage { Context current(); @@ -7,7 +10,6 @@ public interface Storage { interface Scope extends AutoCloseable { Context context(); - void close(); enum Empty implements Scope { @@ -28,8 +30,6 @@ enum Empty implements Storage { public Context current() { return Context.EMPTY; } @Override - public Scope store(Context context) { - return Scope.Empty.INSTANCE; - } + public Scope store(Context context) { return Scope.Empty.INSTANCE; } } } \ No newline at end of file From 516ede99d4ac14f5fed9037555d5a99d32480e38 Mon Sep 17 00:00:00 2001 From: Diego Date: Fri, 10 Feb 2023 12:27:54 -0300 Subject: [PATCH 10/19] main --- .../main/java/io/github/dpsoft/ap/Agent.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/agent/src/main/java/io/github/dpsoft/ap/Agent.java b/agent/src/main/java/io/github/dpsoft/ap/Agent.java index 440e364..37a63e6 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/agent/src/main/java/io/github/dpsoft/ap/Agent.java @@ -1,5 +1,7 @@ package io.github.dpsoft.ap; +import io.github.dpsoft.ap.context.api.Context; +import io.github.dpsoft.ap.context.api.ContextHandler; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; import io.github.dpsoft.ap.instrumentation.ContextInstrumenter; import io.github.dpsoft.ap.util.Banner; @@ -7,6 +9,7 @@ import io.github.dpsoft.ap.util.Server; import java.lang.instrument.Instrumentation; +import java.util.Map; public final class Agent { public static void premain(String args, Instrumentation instrumentation) { @@ -22,4 +25,24 @@ public static void premain(String args, Instrumentation instrumentation) { }); }); } + +// public static void main(String[] args) { +// premain(null, ByteBuddyAgent.install()); +// +// var contextStorage = new ContextHandler(); +// +// var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { +// var currentContext = contextStorage.currentContext(); +// return currentContext; +// }); +// +//// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); +// +// System.out.println(xx.contextId); +// System.out.println(xx.tags); +// +// System.out.println(contextStorage.currentContext().contextId); +// +// while (true) {} +// } } From 59634212a5f1eae955decbaf9e3316b3b7439268 Mon Sep 17 00:00:00 2001 From: Diego Date: Mon, 13 Feb 2023 18:59:43 -0300 Subject: [PATCH 11/19] wip: context api --- agent/pom.xml | 21 +++++--- .../main/java/io/github/dpsoft/ap/Agent.java | 36 ++++++------- .../dpsoft/ap/context/ContextStorage.java | 19 +++---- .../dpsoft/ap/context/ThreadLocalStorage.java | 5 +- .../instrumentation/ContextInstrumenter.java | 8 +-- context-api/pom.xml | 8 +++ .../github/dpsoft/ap/context/api/Context.java | 53 ++++++++++++++++--- .../dpsoft/ap/context/api/ContextHandler.java | 30 +++++++---- .../github/dpsoft/ap/context/api/Labels.java | 47 ++++++++++++++++ .../github/dpsoft/ap/context/api/Storage.java | 22 +++++++- .../dpsoft/ap/context/api/ContextTest.java | 33 ++++++++++++ .../dpsoft/ap/context/api/LabelsTest.java | 33 ++++++++++++ 12 files changed, 255 insertions(+), 60 deletions(-) create mode 100644 context-api/src/main/java/io/github/dpsoft/ap/context/api/Labels.java create mode 100644 context-api/src/test/java/io/github/dpsoft/ap/context/api/ContextTest.java create mode 100644 context-api/src/test/java/io/github/dpsoft/ap/context/api/LabelsTest.java diff --git a/agent/pom.xml b/agent/pom.xml index 78791af..5d9199c 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -31,6 +31,11 @@ Maven central https://repo.maven.apache.org/maven2/ + + local + Maven local + file:////home/diego/.m2/repository + @@ -70,17 +75,17 @@ 1.12.22 - - - - - + + net.bytebuddy + byte-buddy-agent + 1.12.22 + - io.github.dspoft.ap + io.github.dpsoft.ap context-api - 0.1.1-SNAPSHOT - provided + 0.1.2-SNAPSHOT + diff --git a/agent/src/main/java/io/github/dpsoft/ap/Agent.java b/agent/src/main/java/io/github/dpsoft/ap/Agent.java index 37a63e6..135f0ef 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/Agent.java +++ b/agent/src/main/java/io/github/dpsoft/ap/Agent.java @@ -2,11 +2,13 @@ import io.github.dpsoft.ap.context.api.Context; import io.github.dpsoft.ap.context.api.ContextHandler; +import io.github.dpsoft.ap.context.api.Labels; import io.github.dpsoft.ap.handler.AsyncProfilerHandler; import io.github.dpsoft.ap.instrumentation.ContextInstrumenter; import io.github.dpsoft.ap.util.Banner; import io.github.dpsoft.ap.util.Runner; import io.github.dpsoft.ap.util.Server; +import net.bytebuddy.agent.ByteBuddyAgent; import java.lang.instrument.Instrumentation; import java.util.Map; @@ -26,23 +28,21 @@ public static void premain(String args, Instrumentation instrumentation) { }); } -// public static void main(String[] args) { -// premain(null, ByteBuddyAgent.install()); -// -// var contextStorage = new ContextHandler(); -// -// var xx = contextStorage.runWithContext(new Context(1, Map.of("a", "b")), () -> { -// var currentContext = contextStorage.currentContext(); -// return currentContext; -// }); -// -//// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); -// -// System.out.println(xx.contextId); + public static void main(String[] args) { + premain(null, ByteBuddyAgent.install()); + + var xx = ContextHandler.runWithContext(Context.of(Context.ContextID, 1L, Labels.of("a", "b")), () -> { + var currentContext = ContextHandler.currentContext(); + return currentContext; + }); + +// var xxxx = contextStorage.storeContext(new Context(2, Map.of("c", "d"))); + + System.out.println(xx.get(Context.ContextID)); // System.out.println(xx.tags); -// -// System.out.println(contextStorage.currentContext().contextId); -// -// while (true) {} -// } + + System.out.println(ContextHandler.currentContext().get(Context.ContextID)); + + while (true) {} + } } diff --git a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java index a7d6c4f..7a22042 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ContextStorage.java @@ -2,10 +2,7 @@ import io.github.dpsoft.ap.context.api.Context; import io.github.dpsoft.ap.context.api.Storage; -import io.vavr.control.Try; -import org.tinylog.Logger; - -import java.util.concurrent.Callable; +import java.util.function.Supplier; /** * This class is used to store and retrieve the current context. @@ -19,10 +16,14 @@ public final class ContextStorage { public Storage.Scope storeContext(Context context) { return storage.store(context); } - public T runWithContext(Context context, Callable callable) { - return Try.withResources(() -> storeContext(context)) - .of(scope -> callable.call()) - .onFailure((cause) -> Logger.error(cause, "Error running callable with context")) - .get(); + public T runWithContext(Context context, Supplier thunk) { + try (var ignored = storeContext(context)) { + return thunk.get(); + } + } + public void runWitContext(Context context, Runnable runnable) { + try (var ignored = storeContext(context)) { + runnable.run(); + } } } diff --git a/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java index eb95a97..4496217 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java +++ b/agent/src/main/java/io/github/dpsoft/ap/context/ThreadLocalStorage.java @@ -17,8 +17,9 @@ public class ThreadLocalStorage implements Storage { public Scope store(Context context) { final var previous = tls.get(); tls.set(context); - Logger.info("store::AsyncProfiler.setContextId(" + context.contextId + ")"); + Logger.info("store::AsyncProfiler.setContextId(" + context.get(Context.ContextID) + ")"); //AsyncProfiler.setContextId(context.contextId); + return new Scope() { @Override public Context context() { @@ -28,7 +29,7 @@ public Context context() { @Override public void close() { tls.set(previous); - Logger.info("close::AsyncProfiler.setContextId(" + context.contextId + ")"); + Logger.info("close::AsyncProfiler.setContextId(" + context.get(Context.ContextID) + ")"); //AsyncProfiler.setContextId(context.contextId); } }; diff --git a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java index e15d5ef..61dcff3 100644 --- a/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java +++ b/agent/src/main/java/io/github/dpsoft/ap/instrumentation/ContextInstrumenter.java @@ -12,7 +12,7 @@ import org.tinylog.Logger; import java.lang.instrument.Instrumentation; -import java.util.concurrent.Callable; +import java.util.function.Supplier; import static net.bytebuddy.matcher.ElementMatchers.*; @@ -37,11 +37,11 @@ public static void install(Instrumentation instrumentation, AgentConfiguration.I public static class ContextHandlerInterceptor { @RuntimeType - public static Object onRunWithContext(Context context, Callable callable) { return ContextStorage.INSTANCE.runWithContext(context, callable);} + public static Object runWithContext0(Context context, Supplier supplier) { return ContextStorage.INSTANCE.runWithContext(context, supplier);} @RuntimeType - public static Context onCurrentContext() { return ContextStorage.INSTANCE.currentContext();} + public static Context currentContext0() { return ContextStorage.INSTANCE.currentContext();} @RuntimeType - public static Object onStoreContext(Context context) { + public static Object storeContext0(Context context) { return ContextStorage.INSTANCE.storeContext(context); } } diff --git a/context-api/pom.xml b/context-api/pom.xml index 7413f75..86b5ae1 100644 --- a/context-api/pom.xml +++ b/context-api/pom.xml @@ -6,6 +6,14 @@ 0.1.2-SNAPSHOT jar + + + org.junit.jupiter + junit-jupiter + 5.9.2 + test + + 11 diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java index 352b004..f757b84 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Context.java @@ -1,24 +1,61 @@ package io.github.dpsoft.ap.context.api; +import java.util.HashMap; import java.util.Map; public final class Context { - public static final Context EMPTY = new Context(0, Map.of()); + public static final Context EMPTY = new Context(Map.of(), Labels.EMPTY); - public final long contextId; + public static final Context.Key ContextID = new Context.Key<>("contextId", 0L); - public final Map tags; + private final Map _underlying; - public static Context of(long contextId, Map tags) { - return new Context(contextId, tags); + private final Labels labels; + + public static Context of(Labels labels) { + return new Context(Map.of(), labels); + } + + public static Context of(Context.Key key, T value) { + return new Context(Map.of(key.name, value), Labels.EMPTY); + } + + public static Context of(Context.Key key, T value, Labels labels) { + return new Context(Map.of(key.name, value), labels); } public static Context empty() { return EMPTY; } - public Context(long contextId, Map tags) { - this.contextId = contextId; - this.tags = tags; + private Context(Map underling, Labels labels) { + this.labels = labels; + this._underlying = underling; + } + + public Context withEntry(Context.Key key, T value) { + var x = new HashMap(_underlying.size() + 1); + x.putAll(_underlying); + x.put(key.name, value); + + return new Context(x, labels); + } + + public T get(Context.Key key) { + return (T) _underlying.getOrDefault(key.name, key.emptyValue); + } + + public static Context.Key key(String name, T emptyValue) { + return new Context.Key<>(name, emptyValue); + } + + public static class Key { + public final String name; + public final T emptyValue; + + public Key(String name, T emptyValue) { + this.name = name; + this.emptyValue = emptyValue; + } } } \ No newline at end of file diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java index 615d61b..b13555a 100644 --- a/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/ContextHandler.java @@ -1,29 +1,39 @@ package io.github.dpsoft.ap.context.api; -import java.util.concurrent.Callable; +import java.util.function.Supplier; /** * This class is used to store and retrieve the current context. * Its implementation is Noop by default, but it can be replaced by an agent in runtime. */ -public class ContextHandler { +public class ContextHandler { /** * Returns the current context. */ - public Context currentContext() { return Context.EMPTY; } + public static Context currentContext() { return Context.EMPTY; } /** * Stores the current context and returns a scope that can be used to restore it. */ - public Storage.Scope storeContext(Context context) { return Storage.Scope.Empty.INSTANCE;} + public static Storage.Scope storeContext(Context context) { return Storage.Scope.Empty.INSTANCE;} /** - * Runs the given callable with the given context. - * The context is restored after the callable is executed. + * Runs the given supplier with the given context. + * The context is restored after the supplier returns. */ - public T runWithContext(Context context, Callable callable) { - try (var ignore = storeContext(currentContext())) {} - try { return callable.call(); } - catch (Exception e) { throw new RuntimeException(e);} + public static T runWithContext(Context context, Supplier supplier) { + try (var ignored = storeContext(context)) { + return supplier.get(); + } + } + + /** + * Runs the given runnable with the given context. + * The context is restored after the runnable returns. + */ + public static void runWitContext(Context context, Runnable runnable) { + try (var ignored = storeContext(context)) { + runnable.run(); + } } } diff --git a/context-api/src/main/java/io/github/dpsoft/ap/context/api/Labels.java b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Labels.java new file mode 100644 index 0000000..4a5c34b --- /dev/null +++ b/context-api/src/main/java/io/github/dpsoft/ap/context/api/Labels.java @@ -0,0 +1,47 @@ +package io.github.dpsoft.ap.context.api; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public final class Labels { + public final static Labels EMPTY = new Labels(Map.of()); + private final Map _underlying; + + private Labels(Map underlying) { + this._underlying = underlying; + } + + public static Labels from(Map underlying) { + return new Labels(underlying); + } + + public static Labels of(String key, String value) { + return new Labels(Map.of(key, value)); + } + + public Labels withLabel(String key, String value) { + var mergedMap = new HashMap(_underlying.size() + 1); + mergedMap.putAll(_underlying); + mergedMap.put(key, value); + + return new Labels(mergedMap); + } + + public Set