From bca5c8e628ebacf6ab3d3962f0965d3aad9fc007 Mon Sep 17 00:00:00 2001 From: Kanro Date: Tue, 14 Feb 2023 18:10:21 +0800 Subject: [PATCH] Refactor kotlin coroutine plugin with CoroutineContext (#453) --- CHANGES.md | 2 +- .../DispatchedTaskExceptionInterceptor.java | 60 ------------- .../DispatchedTaskRunInterceptor.java | 73 ---------------- .../coroutine/DispatcherInterceptor.java | 56 ------------ .../NewCoroutineContextInterceptor.java | 51 +++++++++++ .../coroutine/TracingCoroutineContext.java | 87 +++++++++++++++++++ .../kotlin/coroutine/TracingRunnable.java | 82 ----------------- .../apm/plugin/kotlin/coroutine/Utils.java | 65 -------------- .../CoroutineContextInstrumentation.java | 65 ++++++++++++++ .../define/DispatchedTaskInstrumentation.java | 86 ------------------ .../define/DispatcherInstrumentation.java | 69 --------------- .../src/main/resources/skywalking-plugin.def | 3 +- 12 files changed, 205 insertions(+), 494 deletions(-) delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskExceptionInterceptor.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskRunInterceptor.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/NewCoroutineContextInterceptor.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingCoroutineContext.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/Utils.java create mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/CoroutineContextInstrumentation.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatchedTaskInstrumentation.java delete mode 100644 apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java diff --git a/CHANGES.md b/CHANGES.md index cda54595a..e0a9a6bc7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,8 +12,8 @@ Release Notes. * Remove Powermock entirely from the test cases. * Fix H2 instrumentation point * Refactor pipeline in jedis-plugin. -* Enhance kotlin coroutine plugin for stack tracing. * Add plugin to support ClickHouse JDBC driver (0.3.2.*). +* Refactor kotlin coroutine plugin with CoroutineContext. * Fix OracleURLParser ignoring actual port when :SID is absent. #### Documentation diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskExceptionInterceptor.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskExceptionInterceptor.java deleted file mode 100644 index 54395236c..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskExceptionInterceptor.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; - -import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; - -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -public class DispatchedTaskExceptionInterceptor implements InstanceMethodsAroundInterceptor { - - @Override - public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) { - } - - @Override - public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) { - if (!(ret instanceof Throwable)) return ret; - Throwable exception = (Throwable) ret; - - if (ContextManager.isActive() && objInst.getSkyWalkingDynamicField() instanceof AbstractSpan) { - AbstractSpan span = (AbstractSpan) objInst.getSkyWalkingDynamicField(); - String[] elements = Utils.getCoroutineStackTraceElements(objInst); - if (elements.length > 0) { - Map eventMap = new HashMap<>(); - eventMap.put("coroutine.stack", String.join("\n", elements)); - span.log(System.currentTimeMillis(), eventMap); - } - - objInst.setSkyWalkingDynamicField(exception); - span.errorOccurred().log(exception); - } - return ret; - } - - @Override - public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskRunInterceptor.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskRunInterceptor.java deleted file mode 100644 index 83c4d312c..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatchedTaskRunInterceptor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; - -import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; -import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; - -import java.lang.reflect.Method; - -public class DispatchedTaskRunInterceptor implements InstanceMethodsAroundInterceptor { - - @Override - public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) { - if (objInst.getSkyWalkingDynamicField() instanceof ContextSnapshot) { - ContextSnapshot snapshot = (ContextSnapshot) objInst.getSkyWalkingDynamicField(); - - if (ContextManager.isActive() && snapshot.isFromCurrent()) { - // Thread not switched, skip restore snapshot. - return; - } - - // Create local coroutine span - AbstractSpan span = ContextManager.createLocalSpan(TracingRunnable.COROUTINE); - span.setComponent(ComponentsDefine.KT_COROUTINE); - objInst.setSkyWalkingDynamicField(span); - - // Recover with snapshot - ContextManager.continued(snapshot); - } - } - - @Override - public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) { - if (ContextManager.isActive() && objInst.getSkyWalkingDynamicField() instanceof AbstractSpan) { - AbstractSpan span = (AbstractSpan) objInst.getSkyWalkingDynamicField(); - if (span != null) { - ContextManager.stopSpan(span); - } - } - return ret; - } - - @Override - public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { - if (ContextManager.isActive() && objInst.getSkyWalkingDynamicField() instanceof AbstractSpan) { - AbstractSpan span = (AbstractSpan) objInst.getSkyWalkingDynamicField(); - if (span != null) { - ContextManager.stopSpan(span.errorOccurred().log(t)); - } - } - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java deleted file mode 100644 index cc31f47e8..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/DispatcherInterceptor.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; - -import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; - -import java.lang.reflect.Method; - -public class DispatcherInterceptor implements InstanceMethodsAroundInterceptor { - - @Override - public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) { - if (!ContextManager.isActive()) { - return; - } - - Runnable runnable = (Runnable) allArguments[1]; - - if (Utils.isDispatchedTask(runnable)) { - // Using instrumentation for DispatchedContinuation - EnhancedInstance enhancedRunnable = (EnhancedInstance) runnable; - enhancedRunnable.setSkyWalkingDynamicField(ContextManager.capture()); - } else { - // Wrapping runnable with current context snapshot - allArguments[1] = TracingRunnable.wrapOrNot(runnable); - } - } - - @Override - public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) { - return ret; - } - - @Override - public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/NewCoroutineContextInterceptor.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/NewCoroutineContextInterceptor.java new file mode 100644 index 000000000..ab8b41dde --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/NewCoroutineContextInterceptor.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; + +import kotlin.coroutines.CoroutineContext; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2; + +import java.lang.reflect.Method; + +public class NewCoroutineContextInterceptor implements StaticMethodsAroundInterceptorV2 { + + @Override + public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, MethodInvocationContext context) { + + } + + @Override + public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Object ret, MethodInvocationContext context) { + if (ContextManager.isActive()) { + // Create TracingCoroutineContext when have been in tracing context already. + // Kotlin coroutine plugin does not automatically activate tracing. + CoroutineContext coroutineContext = (CoroutineContext) ret; + // Provide context snapshot for current tracing to TracingCoroutineContext. + return coroutineContext.plus(new TracingCoroutineContext(ContextManager.capture())); + } + + return ret; + } + + @Override + public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class[] parameterTypes, Throwable t, MethodInvocationContext context) { + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingCoroutineContext.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingCoroutineContext.java new file mode 100644 index 000000000..84b638a88 --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingCoroutineContext.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; + +import kotlin.coroutines.AbstractCoroutineContextElement; +import kotlin.coroutines.CoroutineContext; +import kotlinx.coroutines.ThreadContextElement; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.jetbrains.annotations.NotNull; + +public class TracingCoroutineContext extends AbstractCoroutineContextElement implements ThreadContextElement { + private static class Key implements CoroutineContext.Key { + public static final Key INSTANCE = new Key(); + + private Key() { + } + } + + private static final String COROUTINE_OPERATION = "Kotlin/Coroutine"; + + private static final ILog LOG = LogManager.getLogger(TracingCoroutineContext.class); + + private final ContextSnapshot snapshot; + + public TracingCoroutineContext(ContextSnapshot snapshot) { + super(Key.INSTANCE); + this.snapshot = snapshot; + } + + @Override + public void restoreThreadContext(@NotNull CoroutineContext coroutineContext, AbstractSpan span) { + if (ContextManager.isActive() && span != null) { + ContextManager.stopSpan(span); + } + } + + @Override + public AbstractSpan updateThreadContext(@NotNull CoroutineContext coroutineContext) { + // Coroutine will be executed in a new thread, we need recover our tracing context in this thread. + + // Snapshot is null means tracing is disabled in current coroutine. + if (snapshot != null) { + + // Tracing is enabled on the target dispatched thread. + if (ContextManager.isActive()) { + + // If the trace context is from the snapshot, it means that the thread has not been switched, + // and there is no need to create a cross-thread span. + // If not, it means that we are scheduled to a dirty thread, we log a warning and give up to + // create cross-thread span. + if (!snapshot.isFromCurrent()) { + LOG.warn("Kotlin coroutine has been dispatched to a dirty thread which with active span: {}.", ContextManager.getGlobalTraceId()); + } + return null; + } + + AbstractSpan span = ContextManager.createLocalSpan(COROUTINE_OPERATION); + span.setComponent(ComponentsDefine.KT_COROUTINE); + // Recover with snapshot + ContextManager.continued(snapshot); + return span; + } + + return null; + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java deleted file mode 100644 index 8fb09e9a1..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/TracingRunnable.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; - -import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; -import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; - -/** - * {@link Runnable} wrapper with trace context snapshot, it will create span with context snapshot around {@link - * Runnable} runs. - *

- * A class implementation will be cheaper cost than lambda with captured variables implementation. - */ -class TracingRunnable implements Runnable { - public static final String COROUTINE = "Kotlin/Coroutine"; - - private ContextSnapshot snapshot; - private Runnable delegate; - - private TracingRunnable(ContextSnapshot snapshot, Runnable delegate) { - this.snapshot = snapshot; - this.delegate = delegate; - } - - /** - * Wrap {@link Runnable} by {@link TracingRunnable} if active trace context existed. - * - * @param delegate {@link Runnable} to wrap. - * @return Wrapped {@link TracingRunnable} or original {@link Runnable} if trace context not existed. - */ - public static Runnable wrapOrNot(Runnable delegate) { - // Just wrap continuation with active trace context - if (ContextManager.isActive() && !(delegate instanceof TracingRunnable)) { - return new TracingRunnable(ContextManager.capture(), delegate); - } else { - return delegate; - } - } - - @Override - public void run() { - if (ContextManager.isActive() && snapshot.isFromCurrent()) { - // Thread not switched, skip restore snapshot. - delegate.run(); - return; - } - - // Create local coroutine span - AbstractSpan span = ContextManager.createLocalSpan(COROUTINE); - span.setComponent(ComponentsDefine.KT_COROUTINE); - - // Recover with snapshot - ContextManager.continued(snapshot); - - try { - delegate.run(); - } catch (Throwable e) { - span.errorOccurred().log(e); - throw e; - } finally { - ContextManager.stopSpan(span); - } - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/Utils.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/Utils.java deleted file mode 100644 index 6698b63ae..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/Utils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine; - -import kotlin.coroutines.jvm.internal.CoroutineStackFrame; -import org.apache.skywalking.apm.plugin.kotlin.coroutine.define.DispatchedTaskInstrumentation; - -import java.util.ArrayList; - -public class Utils { - private static Class DISPATCHED_TASK_CLASS = null; - private static Boolean IS_DISPATCHED_TASK_CLASS_LOADED = false; - - private static void loadDispatchedTaskClass() { - if (IS_DISPATCHED_TASK_CLASS_LOADED) return; - try { - DISPATCHED_TASK_CLASS = Class.forName(DispatchedTaskInstrumentation.ENHANCE_CLASS); - } catch (ClassNotFoundException ignored) { - } finally { - IS_DISPATCHED_TASK_CLASS_LOADED = true; - } - } - - public static boolean isDispatchedTask(Runnable runnable) { - loadDispatchedTaskClass(); - if (DISPATCHED_TASK_CLASS == null) return false; - return DISPATCHED_TASK_CLASS.isAssignableFrom(runnable.getClass()); - } - - public static String[] getCoroutineStackTraceElements(Object runnable) { - if (!(runnable instanceof CoroutineStackFrame)) { - return new String[0]; - } - - ArrayList elements = new ArrayList<>(); - CoroutineStackFrame frame = (CoroutineStackFrame) runnable; - while (frame != null) { - StackTraceElement element = frame.getStackTraceElement(); - frame = frame.getCallerFrame(); - - if (element != null) { - elements.add(element.toString()); - } else { - elements.add("Unknown Source"); - } - } - return elements.toArray(new String[0]); - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/CoroutineContextInstrumentation.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/CoroutineContextInstrumentation.java new file mode 100644 index 000000000..caf59a29e --- /dev/null +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/CoroutineContextInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassStaticMethodsEnhancePluginDefineV2; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class CoroutineContextInstrumentation extends ClassStaticMethodsEnhancePluginDefineV2 { + public static final String ENHANCE_CLASS = "kotlinx.coroutines.CoroutineContextKt"; + public static final String COROUTINE_CONTEXT_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kotlin.coroutine.NewCoroutineContextInterceptor"; + public static final String ENHANCE_METHOD_NEW_COROUTINE_CONTEXT = "newCoroutineContext"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } + + @Override + public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() { + return new StaticMethodsInterceptV2Point[]{ + new StaticMethodsInterceptV2Point() { + @Override + public ElementMatcher getMethodsMatcher() { + // This matcher will match the following method: + // 1. CoroutineScope.newCoroutineContext(CoroutineContext) + // 2. CoroutineContext.newCoroutineContext(CoroutineContext) + return named(ENHANCE_METHOD_NEW_COROUTINE_CONTEXT).and(takesArguments(2)); + } + + @Override + public String getMethodsInterceptorV2() { + return COROUTINE_CONTEXT_INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatchedTaskInstrumentation.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatchedTaskInstrumentation.java deleted file mode 100644 index 62177e2c6..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatchedTaskInstrumentation.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine.define; - -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; -import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; - -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; -import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; - -public class DispatchedTaskInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - public static final String ENHANCE_CLASS = "kotlinx.coroutines.DispatchedTask"; - public static final String RUN_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kotlin.coroutine.DispatchedTaskRunInterceptor"; - public static final String ENHANCE_METHOD_RUN = "run"; - public static final String EXCEPTION_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kotlin.coroutine.DispatchedTaskExceptionInterceptor"; - public static final String ENHANCE_METHOD_GET_EXCEPTIONAL_RESULT = "getExceptionalResult$kotlinx_coroutines_core"; - - @Override - protected ClassMatch enhanceClass() { - return byName(ENHANCE_CLASS); - } - - @Override - public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[0]; - } - - @Override - public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[]{ - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named(ENHANCE_METHOD_RUN).and(takesNoArguments()); - } - - @Override - public String getMethodsInterceptor() { - return RUN_INTERCEPTOR_CLASS; - } - - @Override - public boolean isOverrideArgs() { - return true; - } - }, - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named(ENHANCE_METHOD_GET_EXCEPTIONAL_RESULT); - } - - @Override - public String getMethodsInterceptor() { - return EXCEPTION_INTERCEPTOR_CLASS; - } - - @Override - public boolean isOverrideArgs() { - return false; - } - } - }; - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java deleted file mode 100644 index 0931a8b92..000000000 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/java/org/apache/skywalking/apm/plugin/kotlin/coroutine/define/DispatcherInstrumentation.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.kotlin.coroutine.define; - -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; -import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; -import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; - -import static net.bytebuddy.matcher.ElementMatchers.named; -import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; - -public class DispatcherInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kotlin.coroutine.DispatcherInterceptor"; - public static final String ENHANCE_CLASS = "kotlinx.coroutines.CoroutineDispatcher"; - public static final String ENHANCE_METHOD_DISPATCH = "dispatch"; - - @Override - protected ClassMatch enhanceClass() { - return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS); - } - - @Override - public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[0]; - } - - @Override - public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named(ENHANCE_METHOD_DISPATCH).and(takesArgumentWithType(0, "kotlin.coroutines.CoroutineContext")) - .and(takesArgumentWithType(1, "java.lang.Runnable")); - } - - @Override - public String getMethodsInterceptor() { - return INTERCEPTOR_CLASS; - } - - @Override - public boolean isOverrideArgs() { - return true; - } - } - }; - } -} diff --git a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def index 1cb46e078..212d6297b 100644 --- a/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/kotlin-coroutine-plugin/src/main/resources/skywalking-plugin.def @@ -14,5 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -kotlin-coroutine=org.apache.skywalking.apm.plugin.kotlin.coroutine.define.DispatcherInstrumentation -kotlin-coroutine=org.apache.skywalking.apm.plugin.kotlin.coroutine.define.DispatchedTaskInstrumentation \ No newline at end of file +kotlin-coroutine=org.apache.skywalking.apm.plugin.kotlin.coroutine.define.CoroutineContextInstrumentation \ No newline at end of file