MPI - Martini Profiling Interface

Table of Contents

  1. Introduction
  2. Client Initialization
  3. MPI Event Handling
  4. Event Groups and Filters
  5. Data Requests
  6. Working with MPI Arrays

Introduction

The Martini Profiling Interface (MPI) is an interface for managed-runtime profiling tools. It is defined to suit a wide-range of virtual machines, such as Java and .NET. Its main goals is to allow using the same tool with different managed-runtime environments.

MPI is a declarative, event-based interface, offering a variety of well-defined events and data request operations. Some of the events may be optionally restricted to specific selective criteria, such as specific classes rather than all, reducing the overhead and the amount of data generated.

An MPI client registers to relevant events and event-specific data by implementing Event Observer objects. Once the event occurs, the Observer's HandleEvent callback function is called, with event-specific data passed as an argument. Additional data can be retrieved by calling one of the data request functions.

More that one profiler can use MPI at the same time. Each profiler registers to its own events and receives notification only for the events to which it registered.

Client Initialization

The MPI client library is loaded by the module that implements MPI, according to its configuration (TBD). Once the client library is loaded, its MPICL_Instantiate() entry-point function is called. The client is expected to initialize itself and register for MPI events at this time. Registration to MPI events is possible only at this phase. For certain events, the profiler can decide to initially disable these events (meaning it will not get any notifications for these events) and enable them at a later time.

MPI Event Handling

To handle MPI events, an MPI client must define an Event Observer class for the specific MPI event it needs to handle, and then register an instance of this class with Martini using the IMpi::RegisterEvent function. The client can optionally override the observer's EventDataTypes method and specify the data items to be supplied with the event. After registration, when the requested event occurs, the Event Observer's HandleEvent function is called with the requested data as an argument.

The following example shows how to define and register an event observer for the Method Enter MPI event:

 using namespace Martini::MPI;

 // Define an event observer for the Method Enter event
 class MethodEnterObserver : public IMethodEnterEventObserver
 {
 public:

     // Specify the data items to be supplied with the event
     virtual BitSet EventDataTypes()
     {
         return DR_METHOD_ID | DR_THREAD_ID;
     }
 
     // Event handler
     virtual void HandleEvent(SMethodEnterEventData &data)
     {
         cout << "Method Enter event received. Method id = " 
              << data.methodId << " Thread id = " << data.threadId << endl;
     }
 }

 // Event Observer instance
 MethodEnterObserver g_methodEnterObserver;

 // MPI Client entry point
 extern "C" API_EXPORT 
 TResult MPICL_Instantiate(IMpi *pMpiApi, TId clientId, const char *szOptions)
 {
     TResult res = pMpiApi->RegisterEvent(clientId, g_methodEnterObserver);
     return MRTE_RESULT_OK;
 }

Event Groups and Filters

Some MPI events can be optionally restricted to specific selective criteria, in order to reduce the overhead and the amount of data generated. For example, the MPI client can restrict the Method Enter and Method Leave events to be generated only for specific methods rather than for all methods, thereby reducing the overhead on the profiled application. The MPI Client define these restrictions by implementing Event Filter objects and registering them with MPI.

Since applying filters usually make sense for a group of events rather than for an individual event, MPI Event Filters are applied to Event Groups and not to specific events.

MPI defines the following event groups:

To apply a filter to an MPI event group, the MPI client must define an Event Filter class for the specific event group it wants to restrict, and then register an instance of this class with Martini using the IMpi::SetEventGroupFilter function.

After registration, The Event Filter's ShouldNotify function will be called by Martini to determine the set of elements for which to generate the events belonging to the restricted group.

The following example shows how to instruct Martini to generate Method Enter and Method Leave events only for public methods of the public class Foo:

 using namespace Martini::MPI;

 // Define a call-graph event filter
 class CallGraphFilter : public ICallGraphFilter
 {
     bool ShouldNotify(SCallGraphFilterData &methodInfo)
     {
         if (strcmp("Foo", data.szClassName) == 0 && data.methodAccessType == MAT_PACKAGE_API)
         {
             return true;
         }
         return false;
     }
 };

 // Event Observer instance
 MethodEnterObserver g_methodEnterObserver;
 
 // Event Filter instance
 CallGraphFilter g_callGraphFilter;

 // MPI Client entry point
 extern "C" API_EXPORT 
 TResult MPICL_Instantiate(IMpi *pMpiApi, TId clientId, const char *szOptions)
 {
     // Register for the Method Enter event
     pMpiApi->RegisterEvent(clientId, g_methodEnterObserver);

     // Set the call-graph filter
     pMpiApi->SetEventGroupFilter(clientId, EG_CALL_GRAPH, g_callGraphFilter);

     return MRTE_RESULT_OK;
 }

Data Requests

The Data Request functions are called by the MPI client to receive additional information about an element, such as a class, method, object and so on. Refer to the documentation of the specific data request functions for detailed information about each of these function.

All data request functions has the following definition

 virtual TResult GetElementInfo(TId clientId,
                                TId ElementId,
                                BitSet requestedDataTypes, 
                                SElementInfo *pData);

Where Element represents the type of element for which to retrieve information.

The data request functions are all used in the following way:

  1. The client specifies its client id in the clientId parameter
  2. The client specifies the id of the element to query in the ElementId parameter (moduleId, classId, objectId, etc...).
  3. The client specifies the data items to retrieve in the requestedDataTypes parameter. Data items are specified as a combination of TDataRequestType consts OR'ed together (see Data Request Types).
  4. The client passes a reference to an allocated data structure in the pData parameter. Upon return, MPI will write the requested data to this structure and will set the pData->validData field to indicate which data items were successfully retrieved.

All data request return one of the following values:

Return values:
MRTE_RESULT_OK If data successfully retrieved
MRTE_ERROR_PARTIAL_INFO If not all data items could be retrieved. The client should examine the 'pData->validData' field to check which data items were returned.
MRTE_ERROR_BUFFER_TOO_SHORT For requests that return arrays, indicates that not enough space was allocated to return all the available information. For more information, refer to the Working with MPI Arrays section.
MRTE_ERROR_NULL_PTR If pData is NULL, or one of its requested pointer fields is NULL
MRTE_ERROR_ILLEGAL_ID If clientId is not a valid MPI client id
MRTE_ERROR_NOT_SUPPORTED If one of the specified data items is not supported by the data request.
MRTE_ERROR_PHASE_FAILURE If the requested information cannot be retrieved before the VM has initialized. The client should request the information only after the VM Init event has been sent.
MRTE_ERROR_FAIL If failed to retrieve data
Remarks:
- The memory for the pData argument must be allocated by the client before calling the data request function. Memory must be allocated for pointer fields that correspond to requested data items as well.
The following example shows how to retreive the name and signature of a method:
 IMpi *g_pMpiApi;

 void CNewMethodObserver::HandleEvent(SNewMethodEventData &data)
 {
     char szName[1000];
     char szSig[1000];
     SMethodInfo methodInfo;
     methodInfo.szMethodName = szName;
     methodInfo.szMethodSig = szSig;
 
     TResult res = g_pMpiApi->GetMethodInfo(m_clientId, data.methodId, 
         DR_METHOD_NAME | DR_METHOD_SIGNATURE, &methodInfo);
 }

Working with MPI Arrays

Several MPI events and data request functions return information in arrays. Generally, arrays returned in an event callback are allocated and freed by the MPI implementation, while arrays returned by data request functions must be pre-allocated by the MPI Client.

All MPI arrays are defined as structs similar to the following

 struct SArray
 {
     TArrayElement *entries;
     unsigned int uiSize;
     unsigned int uiActualSize;
 };



Generated on Thu Mar 6 15:07:55 2008 for Martini by doxygen 1.5.5