Difference between revisions of "DocumentationAeminiumRuntimeProfiler"

From Aeminium
Jump to: navigation, search
(Running an Application)
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
== IMPORTANT NOTE ==
 
This page is still under construction, so the text might eventually be reviewed.
 
 
 
== Preparing the ground ==
 
== Preparing the ground ==
  
Line 11: Line 8:
 
To run an application along with the JProfiler, you must add a few flags before executing the 'java' command, namely the '-agentpath' and '-Djprofiler.probeProvider', as described in the example bellow for Linux 32-bit:
 
To run an application along with the JProfiler, you must add a few flags before executing the 'java' command, namely the '-agentpath' and '-Djprofiler.probeProvider', as described in the example bellow for Linux 32-bit:
  
  java -agentpath:/home/Tools/jprofiler7/bin/linux-x64/libjprofilerti.so=port=8849 -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar''
+
  java -agentpath:/home/username/Tools/jprofiler7/bin/linux-x64/libjprofilerti.so=port=8849 -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar''
  
 
This will allow you monitoring the Runtime activity on online mode, requiring you to start a new session at JProfiler, listening in the port 8849. The meaning of the flags are the following:
 
This will allow you monitoring the Runtime activity on online mode, requiring you to start a new session at JProfiler, listening in the port 8849. The meaning of the flags are the following:
Line 24: Line 21:
 
As for offline mode, use the following command:
 
As for offline mode, use the following command:
  
  java -agentpath:/home/jprofiler7/bin/linux-x64/libjprofilerti.so=offline,id=3652,<br>config=/home/Tools/jprofiler7_scripts/config.xml -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar''
+
  java -agentpath:/home/jprofiler7/bin/linux-x64/libjprofilerti.so=offline,id=3652,<br>config=/home/username/Tools/jprofiler7_scripts/config.xml -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar''
  
 
To know more about the 'config.xml' file, check the [http://resources.ej-technologies.com/jprofiler/help/doc/help.pdf JProfiler documentation].
 
To know more about the 'config.xml' file, check the [http://resources.ej-technologies.com/jprofiler/help/doc/help.pdf JProfiler documentation].
Line 36: Line 33:
 
=== AeminiumProfiler ===
 
=== AeminiumProfiler ===
  
This class was created so we could statically refer the scheduler and the graph. This is necessary because the JProfiler classes must follow a strict constructor and are initialized by the JProfiler itself. Therefore, we couldn't pass, for the example, the Runtime as an argument to the probe providers.
+
This class was created so we could statically refer the scheduler and the graph. This extra step is necessary because the JProfiler classes must use a fixed constructor and are initialized by the JProfiler itself. Therefore, we couldn't pass, for instance, the Runtime as an argument to the probe providers.
To accomplish this goal, the class AeminiumProfiler is initialized by the Runtime and whenever JProfiler needs to collect data from the scheduler or the graph, it simply has to pick the AeminiumProfiler's references.
+
To accomplish this goal, the class AeminiumProfiler is initialized by the Runtime at start time and whenever JProfiler needs to collect data from the scheduler or the graph, it simply has to pick the AeminiumProfiler's references. In order to avoid ''null'' values (if the JProfiler starts sampling before the Runtime is fully ready), it's wise to always double check the contents of the references.
  
 
=== DataCollection ===
 
=== DataCollection ===
  
The DataCollection works like a structure that will hold all the variables to profile. At each measuring, the JProfiler passes an instance of this class to the scheduler and then to the graph, being these components responsible for filling with values the fields that are related to them.
+
The DataCollection works like a structure that will hold all the variables to profile. At each measurement, the JProfiler passes an instance of this class to the scheduler and then to the graph, being these two components responsible for filling the fields that are related to them.
Therefore, it contains the following variables:
+
Therefore, DataCollection contains the following variables:
  
 
===== Scheduler variables =====
 
===== Scheduler variables =====
Line 52: Line 49:
  
 
We may have more than one non-blocking queue, and hence the array for this variable.
 
We may have more than one non-blocking queue, and hence the array for this variable.
In most cases, we will have more than a single working thread. Also, there is also three different types of tasks, namely Atomic, Non-blocking and Blocking. Due to this two conditions, we need a matrix to store all the information about how many tasks were handled by each thread.
+
In most cases, we will also have more than a single working thread. Besides it, there are three different types of tasks too, namely Atomic, Non-blocking and Blocking tasks. Due to this two conditions, we need a matrix to store all the information about how many tasks were handled by each thread.
  
 
===== Graph variables =====
 
===== Graph variables =====
Line 69: Line 66:
 
===<span style="color:#8B0000"> JProfiler Classes</span>===
 
===<span style="color:#8B0000"> JProfiler Classes</span>===
  
The three other classes that also belong to the profiler package are the ones which are required by JProfiler to collect telemetries information, as well as handle method interception.
+
The three classes left for analysis are the ones which are required by JProfiler to collect telemetry information, as well as handle method interception.
  
 
=== AeminiumProbeProvider ===
 
=== AeminiumProbeProvider ===
  
AeminiumProbeProvider tells JProfiler which are the probe classes present in the project which should be used for profiling. It simply contains a method, getProbes(), which return an array with the necessary probe classes. In the Æminium Runtime, those will be CountersProbe and TaskDetailsProbe, discussed right away.
+
AeminiumProbeProvider is a simple class that let's JProfiler know which are the probe classes present in the project that are going to be used for profiling. It merely contains a method, getProbes(), which return an array with the necessary probe classes. In the Æminium Runtime, those will be CountersProbe and TaskDetailsProbe, discussed right away.
  
 
=== CountersProbe (Telemetry Data) ===
 
=== CountersProbe (Telemetry Data) ===
  
The CounterProbe is responsible for monitoring the global variables related to the scheduler and graph. By global variables, we mean, for example, the number of tasks that were completed so far or how many tasks are marked as running at a given time.
+
The CounterProbe is responsible for monitoring the global task activity for the scheduler and graph. By global task activity, we mean, for example, the number of completed tasks or how many tasks marked as running at a given moment.
  
 
The variables to collect are defined in the method getMetaData(). The first group of variables correspond to the sum of the values of all the active threads. Then, you have a second group, which is defined inside the following 'for' cycle:  
 
The variables to collect are defined in the method getMetaData(). The first group of variables correspond to the sum of the values of all the active threads. Then, you have a second group, which is defined inside the following 'for' cycle:  
Line 89: Line 86:
 
  7 }
 
  7 }
  
The Java variable 'maxParallelism' correspond to the number of active threads in the Æminium Runtime and so, we will have profiling variables separated by each thread.
+
This second group separates by the values by each working thread, whose number is defined by the value in 'maxParallelism'.
  
For collecting data, the method fillTelemetryData() is executed. As stated before, a instance of the class DataCollection is created. Then, after checking if the scheduler reference is not null, we simply call the method collectData(). At this point, the DataCollection object will have all the information coming from the scheduler, so it's only a matter of filling up the 'customTelemetries' array with the correct values. The second step, is exactly the same, but now, we will doing the profiling for the graph variables.  
+
For collecting data, the method fillTelemetryData() is executed, going through the following steps:
At the end, the array will be completely full and it's up to the JProfiler to display the graphs.
+
# Firstly, as stated before, a instance of the class DataCollection is created.
 +
# Then, after checking if the scheduler reference is not null, we simply call the method collectData().
 +
# At this point, the DataCollection object will have all the information coming from the scheduler, so it's only a matter of filling up the 'customTelemetries' array with the correct values.
 +
# The next step is exactly the same, but now, we will doing the profiling for the graph variables.  
 +
# At the end, the array will be completely full and it's up to the JProfiler to display the graphs.
  
 
=== TaskDetailsProbe (Method Interception)===
 
=== TaskDetailsProbe (Method Interception)===
  
The use the JProfiler method interception for controlling the time the task spend at each state. As each task change it's state at the beginning of a method (for example, the task turns into the state RUNNING when the method invoke() is called), we only intercept method entrances, because at this point, we know that the previous state is over and new one is starting.
+
Our Profiler uses JProfiler method interception for controlling the time every task spend at each state. We take advantage of the fact that tasks change theirs state at the beginning of a method (for example, the task turns into the state RUNNING when the method invoke() is called), and so, we only intercept method entrances.
  
Firstly, we need to define all the method to intercept at the method getInterceptionMethods(). As stated in the JProfiler API Javadocs, for the class [ftp://202.127.19.60/home/ddg2/chenting/jprofiler5/api/javadoc/com/jprofiler/api/agent/interceptor/InterceptionMethod.html InterceptionMethod], it is used the JVM's representation of type signatures for both argument type and return type.
+
Firstly, we need to define all the method to intercept at the getInterceptionMethods(). As stated in the JProfiler API Javadocs, for the class [ftp://202.127.19.60/home/ddg2/chenting/jprofiler5/api/javadoc/com/jprofiler/api/agent/interceptor/InterceptionMethod.html InterceptionMethod], it is used the JVM's representation of type signatures for both argument type and return type.
  
 
Once you have defined all the methods to be intercepted, it's time to handle them at the interceptionEnter(). As said before, we can do all the work by only intercepting the entries, and therefore, we leave the interceptionExit() untouched.
 
Once you have defined all the methods to be intercepted, it's time to handle them at the interceptionEnter(). As said before, we can do all the work by only intercepting the entries, and therefore, we leave the interceptionExit() untouched.
  
At this point, we take a systematic and quite simple procedure. At the entry of a method, we pick the payload from the hashtable reserved tot he old state, passing it afterwards to the InterceptorContext, which will calculate the difference between the creation time and the current time. This will define the time this task has spent at a given state.
+
Let's now take a look at the interception mechanism. To achieve the time measuring, we use a systematic and quite simple procedure, which is aided by auxiliary hashtables, one for each state a task can assume. The hashtables will hold payloads, which save the time of their creation.
When this is done, a new payload info must be created and stored in the corresponding hashtable, so the next method interception can use it as described in the first step. This second step marks the starting time of a new state.
+
 
 +
At the entry of a method, we have two states to consider. The one a thread has at the moment, the old state, and another, which is the state this task will change to. We get the payload from the old state's hashtable, passing it to the InterceptorContext, which will calculate the difference between the creation time and the current time. This will define the time this task has spent at a that state.
 +
When this is done, a new payload info must be created and stored in the corresponding hashtable, so the next method interception can use it as described in the previous paragraph. This second step marks the starting time of the new state.
  
Naturally, this procedure is different for for the methods addTask() and taskCompleted(), as they correspond to the beginning of the first useful state (WAITING_FOR_DEPENDENCIES) and the end of the last (WAITING_FOR_CHILDREN). For them, we can only insert a new payload info or remove it from the hashtable.
+
Naturally, this procedure is different for the methods addTask() and taskCompleted(), as they correspond to the beginning of the first useful state (WAITING_FOR_DEPENDENCIES) and the end of the last (WAITING_FOR_CHILDREN). For them, we can only insert a new payload info or remove it from the hashtable.

Latest revision as of 10:50, 21 February 2012

Preparing the ground

Downloads

The Æminium Profiler uses the JProfiler framework to collect and manage all the information concerning the performance of the Æminium Runtime, so you must download it before starting any profiling tests. Besides reading the documentation present in this page, also consider consulting the JProfiler documentation whenever you have a doubt which isn't covered here.

Running an Application

To run an application along with the JProfiler, you must add a few flags before executing the 'java' command, namely the '-agentpath' and '-Djprofiler.probeProvider', as described in the example bellow for Linux 32-bit:

java -agentpath:/home/username/Tools/jprofiler7/bin/linux-x64/libjprofilerti.so=port=8849 -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar

This will allow you monitoring the Runtime activity on online mode, requiring you to start a new session at JProfiler, listening in the port 8849. The meaning of the flags are the following:

  • The '-agentpath' flags tells the JVM the location of the 'jprofilerti library', which is usually located the folder where JProfiler was installed.
  • In Windows 32-bit, you can try '-agentpath:"C:\Program Files\jprofiler7\bin\windows\jprofilerti.dll=port=8849"'. For more information, look in the section 'B.3.10 Starting Remote Sessions' of the JProfiler documentation.
  • The '-Djprofiler.probeProvider' will tell the JVM the class path to the probe provider, a class whose contents are discussed below.


As for offline mode, use the following command:

java -agentpath:/home/jprofiler7/bin/linux-x64/libjprofilerti.so=offline,id=3652,
config=/home/username/Tools/jprofiler7_scripts/config.xml -Djprofiler.probeProvider=aeminium.runtime.profiler.AeminiumProbeProvider -jar Fibonnaci.jar

To know more about the 'config.xml' file, check the JProfiler documentation.

The profiler package

The profiler package contains two types of classes. The first type concerns the classes which were only created to make the process of collecting the profiling information easier. Then, in the second type, we can include all the classes that are strictly required by the JProfiler to perform its activity. The first two classes present in this text belong tot he first type, while the other three fall in the second category.

AeminiumProfiler

This class was created so we could statically refer the scheduler and the graph. This extra step is necessary because the JProfiler classes must use a fixed constructor and are initialized by the JProfiler itself. Therefore, we couldn't pass, for instance, the Runtime as an argument to the probe providers. To accomplish this goal, the class AeminiumProfiler is initialized by the Runtime at start time and whenever JProfiler needs to collect data from the scheduler or the graph, it simply has to pick the AeminiumProfiler's references. In order to avoid null values (if the JProfiler starts sampling before the Runtime is fully ready), it's wise to always double check the contents of the references.

DataCollection

The DataCollection works like a structure that will hold all the variables to profile. At each measurement, the JProfiler passes an instance of this class to the scheduler and then to the graph, being these two components responsible for filling the fields that are related to them. Therefore, DataCollection contains the following variables:

Scheduler variables
  • public int noOccupiedQueues;
  • public int [] taskInNonBlockingQueue;
  • public int taskInBlockingQueue;
  • public int [][] tasksHandled;


We may have more than one non-blocking queue, and hence the array for this variable. In most cases, we will also have more than a single working thread. Besides it, there are three different types of tasks too, namely Atomic, Non-blocking and Blocking tasks. Due to this two conditions, we need a matrix to store all the information about how many tasks were handled by each thread.

Graph variables
  • public int noTasksCompleted[];
  • public int noUnscheduledTasks;
  • public int noWaitingForChildrenTasks;
  • public int noWaitingForDependenciesTasks;
  • public int noTasksWaitingInQueue;
  • public int noRunningTasks;
  • public int noCompletedTasks;


The number of tasks completed must be array because, as above, we have three different types of tasks.


JProfiler Classes

The three classes left for analysis are the ones which are required by JProfiler to collect telemetry information, as well as handle method interception.

AeminiumProbeProvider

AeminiumProbeProvider is a simple class that let's JProfiler know which are the probe classes present in the project that are going to be used for profiling. It merely contains a method, getProbes(), which return an array with the necessary probe classes. In the Æminium Runtime, those will be CountersProbe and TaskDetailsProbe, discussed right away.

CountersProbe (Telemetry Data)

The CounterProbe is responsible for monitoring the global task activity for the scheduler and graph. By global task activity, we mean, for example, the number of completed tasks or how many tasks marked as running at a given moment.

The variables to collect are defined in the method getMetaData(). The first group of variables correspond to the sum of the values of all the active threads. Then, you have a second group, which is defined inside the following 'for' cycle:

1 for (int i = 0; i < maxParallelism; i++)
2 {
3     metaData.addCustomTelemetry("Tasks in Non-blocking Queue (" + (i + 1) + ")", Unit.PLAIN, 1f);
4     metaData.addCustomTelemetry("No of Atomic Tasks Handled (" + (i + 1) + ")", Unit.PLAIN, 1f);
5     metaData.addCustomTelemetry("No of Blocking Tasks Handled (" + (i + 1) + ")", Unit.PLAIN, 1f);
6     metaData.addCustomTelemetry("No of Non-blocking Tasks Handled (" + (i + 1) + ")", Unit.PLAIN, 1f);
7 }

This second group separates by the values by each working thread, whose number is defined by the value in 'maxParallelism'.

For collecting data, the method fillTelemetryData() is executed, going through the following steps:

  1. Firstly, as stated before, a instance of the class DataCollection is created.
  2. Then, after checking if the scheduler reference is not null, we simply call the method collectData().
  3. At this point, the DataCollection object will have all the information coming from the scheduler, so it's only a matter of filling up the 'customTelemetries' array with the correct values.
  4. The next step is exactly the same, but now, we will doing the profiling for the graph variables.
  5. At the end, the array will be completely full and it's up to the JProfiler to display the graphs.

TaskDetailsProbe (Method Interception)

Our Profiler uses JProfiler method interception for controlling the time every task spend at each state. We take advantage of the fact that tasks change theirs state at the beginning of a method (for example, the task turns into the state RUNNING when the method invoke() is called), and so, we only intercept method entrances.

Firstly, we need to define all the method to intercept at the getInterceptionMethods(). As stated in the JProfiler API Javadocs, for the class InterceptionMethod, it is used the JVM's representation of type signatures for both argument type and return type.

Once you have defined all the methods to be intercepted, it's time to handle them at the interceptionEnter(). As said before, we can do all the work by only intercepting the entries, and therefore, we leave the interceptionExit() untouched.

Let's now take a look at the interception mechanism. To achieve the time measuring, we use a systematic and quite simple procedure, which is aided by auxiliary hashtables, one for each state a task can assume. The hashtables will hold payloads, which save the time of their creation.

At the entry of a method, we have two states to consider. The one a thread has at the moment, the old state, and another, which is the state this task will change to. We get the payload from the old state's hashtable, passing it to the InterceptorContext, which will calculate the difference between the creation time and the current time. This will define the time this task has spent at a that state. When this is done, a new payload info must be created and stored in the corresponding hashtable, so the next method interception can use it as described in the previous paragraph. This second step marks the starting time of the new state.

Naturally, this procedure is different for the methods addTask() and taskCompleted(), as they correspond to the beginning of the first useful state (WAITING_FOR_DEPENDENCIES) and the end of the last (WAITING_FOR_CHILDREN). For them, we can only insert a new payload info or remove it from the hashtable.