Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot instrument FutureTask #380

Open
hamlet-lee opened this issue Apr 28, 2015 · 6 comments
Open

cannot instrument FutureTask #380

hamlet-lee opened this issue Apr 28, 2015 · 6 comments
Labels

Comments

@hamlet-lee
Copy link

I want to follow cross-threads executions,such as:

ExecutorService executorService = Executors.newCachedThreadPool();

Future<String> future = executorService.submit(new Callable<String>(){

        @Override
        public String call() throws Exception {
            try {
                return Request.Post("http:https://www.apache.org/").execute().returnContent().asString();
            } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    });

    try {
        future.get();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ExecutionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

and, I tried to write an extension to support this:

private void addAbstractExecutorServiceNewTaskForInterceptor(ProfilerPluginSetupContext context) {
    final ClassFileTransformerBuilder classEditorBuilder = context.getClassFileTransformerBuilder("java.util.concurrent.AbstractExecutorService");
    MethodTransformerBuilder methodEditorBuilder = classEditorBuilder.editMethod("execute", "java.util.concurrent.Callable");
    methodEditorBuilder.property(MethodTransformerProperty.IGNORE_IF_NOT_EXIST);
    methodEditorBuilder.injectInterceptor("com.navercorp.pinpoint.plugin.jdk.exec.interceptor.AbstractExecutorServiceNewTaskForInterceptor");
    context.addClassFileTransformer(classEditorBuilder.build());
}

but unfortunately, the interceptor is not called.

when I change "java.util.concurrent.AbstractExecutorService" to some application level class name, the interceptor is called.

So, we cannot track async task by ExecutorService?

@lioolli
Copy link
Contributor

lioolli commented Apr 29, 2015

You cannot intercept AbstractExecutorService.execute() because AbstractExecutorService doesn't have execute() implementation. You have to instrument concrete subclasses of it.

@hamlet-lee
Copy link
Author

ah, I posted wrong code. Infact, I tried to intercept AbstractExecutorService.newTaskFor(Callable)

private void addAbstractExecutorServiceNewTaskForInterceptor(ProfilerPluginSetupContext context) {
    final ClassFileTransformerBuilder classEditorBuilder = context.getClassFileTransformerBuilder("java.util.concurrent.AbstractExecutorService");
    MethodTransformerBuilder methodEditorBuilder = classEditorBuilder.editMethod("newTaskFor", java.util.concurrent.Callable.class.getCanonicalName());
    //methodEditorBuilder.property(MethodTransformerProperty.IGNORE_IF_NOT_EXIST);
    methodEditorBuilder.injectInterceptor("com.navercorp.pinpoint.plugin.jdk.exec.interceptor.AbstractExecutorServiceNewTaskForInterceptor");
    context.addClassFileTransformer(classEditorBuilder.build());
}

but it seems that the interceptor is not called.

let me attach the interceptor code below

package com.navercorp.pinpoint.plugin.jdk.exec.interceptor;

import com.navercorp.pinpoint.bootstrap.MetadataAccessor;
import com.navercorp.pinpoint.bootstrap.context.AsyncTraceId;
import com.navercorp.pinpoint.bootstrap.context.Trace;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.interceptor.SimpleAroundInterceptor;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.annotation.Cached;
import com.navercorp.pinpoint.bootstrap.plugin.annotation.Name;
import com.navercorp.pinpoint.plugin.jdk.exec.JdkExecConstants;

public class AbstractExecutorServiceNewTaskForInterceptor implements SimpleAroundInterceptor, JdkExecConstants {
    private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
    private final boolean isDebug = logger.isDebugEnabled();

    private TraceContext traceContext;
    //private MetadataAccessor asyncTraceIdAccessor;

    public AbstractExecutorServiceNewTaskForInterceptor(TraceContext traceContext, @Cached MethodDescriptor methodDescriptor/*, @Name(METADATA_ASYNC_TRACE_ID) MetadataAccessor asyncTraceIdAccessor*/) {
        logger.debug("AbsExeSvc ctor");
        this.traceContext = traceContext;
        //this.asyncTraceIdAccessor = asyncTraceIdAccessor;
    }

    @Override
    public void before(Object target, Object[] args) {
        logger.debug("AbsExeSvc before");
    }

    @Override
    public void after(Object target, Object[] args, Object result, Throwable throwable) {
        logger.debug("AbsExeSvc after");
        if (isDebug) {
            logger.beforeInterceptor(target, args);
        }

//        final Trace trace = traceContext.currentTraceObject();
//        if (trace == null) {
//            return;
//        }
//
//        trace.recordServiceType(SERVICE_TYPE);
//        
//        try {
//            final AsyncTraceId asyncTraceId = trace.getAsyncTraceId();
//            trace.recordNextAsyncId(asyncTraceId.getAsyncId());
//            asyncTraceIdAccessor.set(target, asyncTraceId);
//            logger.debug("Set asyncTraceId metadata {}", asyncTraceId);
//        } catch (Throwable t) {
//            logger.warn("Failed to after process. {}", t.getMessage(), t);
//        }
    }
}

but

  1. AbsExeSvc ctor, AbsExeSvc before and AbsExeSvc after are not printed in the log
  2. I made beakpoints at the ctor, before and after line. all of them are not breaked

@lioolli
Copy link
Contributor

lioolli commented Apr 29, 2015

Maybe because AbstractExecutorService is loaded while initializing Pinpoint agent.
Pinpoint agent creates an ExecutorService by Executors.newCachedThreadPool(), which implies AbstractExecutorService is loaded.
As long as I know, JVM doesn't call ClassFileTransformers for classes loaded while agent initialization.

@hamlet-lee
Copy link
Author

FutureTask is an important thing to track. Could we work around it? Maybe:

  1. postpone AbstractExecutorService when agent initializing?
  2. use some class loader trick so we can use "a" AbstractExecutorService but not "the" AbstractExecutorService in agent code

@1103036128
Copy link

ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Callable<Object>> searchTasks = new ArrayList<Callable<Object>>(); 
Callable<Object> shopCall =  new ShopCall(slug,type,jsonObject);
List<Future<Object>> futures = executorService.invokeAll(searchTasks,20, TimeUnit.SECONDS);
jsonObject.put("shop", futures.get(0).get());

@1103036128
Copy link

i need your help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants