12

Context:

I have created a small (java) multithread server for a game. Despite my best efforts of following the best practices it turned out that some methods that were intended to be called from just one thread got called from 2 or more threads. After debugging and analysis I have managed to "fix" my design but I was wondering:

The question:

Is there a tool (or if not - is it possible (and how) to be developed) that allows you to mark some methods with annotations like @SingleThread or @ThreadCount(2) or @ThreadNameLike("my_fancy_thread_group*") which counts/monitors/logs access to these methods like:

  • @SingleThread - checks if this method is always accessed by only thread
  • @ThreadCount(2) - is accessed by two threads exactly
  • @ThreadNameLike - is accessed only by threads with name matching the pattern(s)

The idea is to do a TEST run of the program and get at least log record that annotated condition is violated.

I was thinking that probably AspectJ can do the job to some extend with it's pointcuts but then I realized that some approach similar to Dagger / Dagger2 will be better, i.e. when you want to test your server you will have to turn on an annotation processor (let's called it hypothetically "SafetyFirst") which will generate adapter (wrapper?) classes which contain the monitoring code. Then you will run the server, run some load tests and then check the logs for violations (or in an ideal world - get a report file).

I fully realize that such tool:

  • will not provide 100% coverage of all potential cases;
  • will mask/trigger heisenbugs
  • will slow down the examined program

but at very least it can serve as an early warning system that will clearly advertise violations of the intended design.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Ognyan
  • 13,452
  • 5
  • 64
  • 82
  • 1
    Not exactly an answer to the requirements. But log4j can output ThreadIDs/Names ... I could imagine that using a DB-Appender you could analyse the log-table of a test run regarding Threads calling a specific method. – Fildor May 26 '15 at 07:12
  • @Fildor thank you for your comment. I am currently using similar solution but it needs manually adding log statements in all methods that need to monitored. – Ognyan May 26 '15 at 07:33
  • 1
    Tool requests are off topic. – Raedwald May 29 '15 at 08:45
  • @Raedwald as you can see in the bounty description: "is more like to receive ideas and guidelines how it can be developed" – Ognyan May 29 '15 at 09:23
  • 1
    I'd have a look at [BTrace](https://github.com/jbachorik/btrace), it is a framework designed for this and has VisualVM integration, which is nice in terms of usage. I do not have a code sample for your problem. Therefore just a comment. But you will not get Annotations ... – cheffe Jun 03 '15 at 12:01
  • Duplicate of [question](http://stackoverflow.com/questions/8942658/static-analysis-tool-to-detect-multithreading-problems-deadlocks-race-conditio) mentions tool called [ThreadSafe](http://www.contemplateltd.com/threadsafe). Did you try? – Jayan Jun 03 '15 at 15:52
  • @Jayan thank you for the link. Saw it a while ago but not gave it a try because it is not open source AND it is "static analysis tool"... – Ognyan Jun 04 '15 at 06:33

1 Answers1

5

I used a similar test with AspectJ load time weaving for intended printing of all function calls within my package.

Best way of load time weaving is you dont dirty your classes like compile time wevaing. When you remove -javaagent:<path to aspectj lib> and your custom astpect lib classpath entry from your run command. Then all all gone, clear.

I made some changes and implemented a test covering @ThreadCount functionality you asked. You need to download and install AspectJ.

Please see code snippets:

aspect Profile {

    private static Map<String, AtomicInteger> counterMap = new HashMap<String, AtomicInteger>();

    pointcut threadCountPc(test.ThreadCount tc) : execution(* test..*(..)) && @annotation(tc);

    Object around(test.ThreadCount tc) : threadCountPc(tc) {        

        String signature = thisJoinPointStaticPart.getSignature().toString();

        AtomicInteger counter = getCounter(signature);
        int currentValue = counter.incrementAndGet();

        if (currentValue >= tc.value()){
            System.out.println("[Thread Name:" + Thread.currentThread().getName() + 
            "] Method Name:" + signature + ", threadCount:" + currentValue + " exceeds " + tc.value());
        }

        try{
            return proceed(tc);
        }finally{
            counter.decrementAndGet();          
        }
    }

    private static AtomicInteger getCounter(String methodName){
        AtomicInteger value = counterMap.get(methodName);
        if (value == null){
            synchronized (counterMap){
                value = counterMap.get(methodName);
                if (value == null){
                    value = new AtomicInteger(0);
                    counterMap.put(methodName, value);
                }
            }
        }
        return value;
    }
}

Compiling: "C:/aspectj1.8/bin/ajc.bat" profile_ft.aj -cp C:/aspectj1.8/lib/aspectjrt.jar;../test/. -1.6 -outxml -outjar profile_ft.jar

Running: java -javaagent:C:/aspectj1.8/lib/aspectjweaver.jar -cp aspectj/profile_ft.jar;test test.Test1

hsnkhrmn
  • 961
  • 7
  • 20