Previous inter-Thread tracing approaches
- To build up an invocation sequence, each invoked method must know the method that was invoked before it
- Innerthread communication can simply make use of the ThreadLocal pattern and build a stack of the concrete methods being called
- Interthread and inter JVM communication has to connect the stacks of the involved threads. This can be done by an invocation identifier that is passed from the caller to the callee.
- Invocation identifier must also include the method identification that invoked this call
Thread Variables
In Java its possible to store thread specific variables. The class ThreadLocal provides this functionality. This class is internally implemented with a map. The keys for this map are generated by an AtomicInteger starting on 0.
InheritableThreadLocal InheritableThreadLocal are thread variables that can be tnherit inherit to other threads. The whole map with values are inherit to the child thread.
This allows it to give each new thread information about its parent thread, so that the new threads can access it.
This could be used for the invocation sequence sensor.
In thread pools each thread is reused, so most of the time it would be enough to set all thread variables on null every time a thread starts and exits execution.
To achieve this it would be necessary to use the finalize() method. But there is no guarantie guarantee that and when the garbage collector will run this method, so this aproach approach cant be used for the tracer.
Threads can be reused, but Runnables are unique.
Runnable Variables approach
First see Inter-Thread in IS advanced.
To represent the behavior of the application an tree structure of Runnables and their method invocations can be build.
In this tree structure, all trace and context information would be stored.
For this purpose a RunnableMap and InvocationIdentyfiers could be used.
For each Runnable are RunnableLocal is created, analog to ThreadLocal.
RunnableMap
All nodes in this tree are Runnables. Each node can also have child nodes, this would represent the case of an asynchronous method invocation. To make the invocation sequence traceable, each child Runnable(callee) contains information about its parent Runnable(caller).
To make this possible, the constructor of the Runnable class should be instrumented.
Each Thread object needs a Runnable object, when the thread is executed.
RunnableLocal
Parent access- and invocation information can be stored in the Runnable specific variable RunnableLocal. It can be inherit, and replaces the InheritableThreadLocal.
Invocation Identifier
The class Invocation Identifier contains and represents the invocation information contained in each node(Runnable).
Is has to be possible to reconstruct the tree based on the set of InvocationIdentifiers and the information they contain. Therefor each InvocationIdentifier has to be unique and have an ID to for recognition. And also it has to contain information about it parent.
To ensure, that every InvocationIdentifier is unique, it contains the following:
- Invocation ID (Created with Singleton)
- JVM ID (For the assignment)
- Runnable ID
- Node ID
- CreationTimeStamp
- Parent Invocation Identifier
Implementation of RunnableStack for the Runnable tracing
- The constructors of Runnable are instrumented so that each Runnable gets an ID.
- The RunnableMap, generates the ID and provides the mapping to its Runnable (registerRunnable()).
- The RunnableLocal uses the ID as key and stores the RunnableReflection..
- The RunnableReflection uses a ThreadLocal to identify the currently run Runnable. This happens in the run() method.
- (note)
- R1 R2 R3- When R3 is created in constructor of R2. And R1 calls R2. Then is R1 the parent of R3 not R2.
- All with an currentRunnable variable that is handled properly.
- In the same way are the current InvocationIdentifier inherited. (This reduces overhead)
- Boolean indicating if it is the root node.