AUG Aprobe API Reference

From OC Systems Wiki!
Revision as of 23:51, 20 March 2018 by Swn (talk | contribs) (Bit Manipulation)
Jump to: navigation, search

Next Previous Top Contents Index

Aprobe User Guide

Contents

Aprobe API Reference


Overview

This Appendix provides a description of the Aprobe Application Programming Interface (API). The API consists of the types, constants, macros and functions defined in the file $APROBE/include/aprobe.h.

All names start with "ap_". Refer to the Index for alphabetical access to these names. This Appendix is organized along functional lines, as is the file aprobe.h itself, although the ordering is different.

Note: in general you should reference the version of $APROBE/include/aprobe.h in your installation as the final references.

Private Types and Operations

This interface defines a number of types meant to be private. This means that the implementation of the type -- whether it's a pointer, an integer, or a structure -- should not be referenced directly. Instead, the macros and functions provided to query and update values of a private type must always be used. In this way, your probes will be compatible with Aprobe for other platforms, and with future versions of Aprobe.

The private types are:

ap_ModuleIdT

a handle for a linked module, such as an executable, shared library, or DLL. Additional types and operations for modules are described in "Aprobe Module Operations".

ap_SymbolIdT

a handle for a symbol named in a module. See "Aprobe Symbol Operations".

ap_FunctionIdT

a handle for a function (callable routine) in a module. See "Function ID Operations".

ap_OffsetT

the offset of an instruction within its enclosing function, for use with "on_line" and "on_offset" actions. See "Source Line Operations".

ap_LineIdT

information about a source line in the probed program. See "Source Line Operations".

ap_ThreadIdT

a handle for each thread of execution in the probed process, used to access information aprobe has about that thread. See "Process and Thread Support".

ap_LogIdT

a handle associating data that is recorded with methods for reading, writing and formatting that data. See "Logging Support".

ap_LogMethodIdT

a handle identifying a set of data processing functions collectively known as a log method. See "Logging Support".

ap_UalId

a handle identifying each UAL loaded at run-time or format time. See "UAL Support".

The declarations of these types are repeated in the referenced sections in the form:

typedef private ap_ModuleIdT;

Additionally, the definition of most macros which operate on objects of these types is replaced by the word private, indicating that your code should not depend on this definition in any way.

Also note that some functions defined below may actually be implemented as macros. This is generally denoted by a prototype with no parameter names.

Basic Types

Numeric Types

Aprobe defines some numeric types for portability purposes:

typedef unsigned char         ap_Byte;
typedef long                  ap_Int32;
typedef unsigned long         ap_Uint32;
typedef long long             ap_Int64;
typedef unsigned long long    ap_Uint64;

Boolean Type

Aprobe defines a Boolean. Some platforms may not declare TRUE and FALSE so they are defined here only if necessary. The boolean type is often used to return success or failure from an Aprobe function. TRUE will always equate to success.

typedef unsigned char ap_BooleanT;
#define FALSE(0)
#define TRUE (1)

String Pointers

Aprobe uses the following types to refer to character strings:

typedef char            *ap_DataPointerT;
typedef const char            *ap_NameT;
typedef ap_NameT             ap_FileNameT; 

Null values of these are defined as follows:

#define ap_NoDataPointer  ((char *) 0)
#define ap_NoName             ""
#define ap_IsNoName(S) ((S) == NULL || *(S) == 0)

Address and Size Types

This is an address in Aprobe. It refers to a location in memory during execution:

typedef unsigned char *ap_AddressT;

The type ap_SizeT represents the size in bytes of an entity pointed to by a symbol.

typedef long ap_SizeT;

Action Routine Prototype

All action routines--on_entry, on_exit and on_line--have the same parameter profile. These parameters are in turn passed to other functions defined in this API.

typedef void (*ap_ProbeActionT)
   (ap_ExecutionContextPtrT  ap_ExecutionContextPtr,
    ap_FunctionIdT           ap_FunctionId,
    ap_OffsetT               ap_Offset,
    ap_ThreadContextPtrT     ap_ThreadContextPtr,
    ap_ProbeActionRecordPtrT ap_ProbeActionRecordPtr,
    ap_ProbeActionReasonT    ap_ProbeActionReason);

A probe action is normally called when a function is entered, an offset is hit or a function returns. However, there are a number of cases when an exit action will be called for other reasons. The specific reason for the call is given by a parameter ap_ProbeActionReason of type ap_ProbeActionReasonT:

typedef enum {
  ap_UnknownReason,
  ap_EntryAction ,
  ap_OffsetAction,
  ap_LineAction ,
  ap_ExitAction,
  ap_ThreadExiting,
  ap_CppExceptionPropagated,
  ap_AdaExceptionPropagated } ap_ProbeActionReasonT;

These are self-explanatory. ap_UnknownReason should never be seen, but may occur if control is transferred by a user-coded "longjmp()".

The only other parameters that may be of use are ap_FunctionId, to get information about the function being probed (see "Function ID Operations") and ap_Offset, with which source line information may be obtained (see "Source Line Operations").

Aprobe Execution State

The API includes functions to determine whether the executable is being analyzed using aprobe or apformat, or using the Aprobe library in some other way.

typedef enum {
  ap_AprobeRunTime,
  ap_AprobeFormatTime,
   ap_AprobeTime } ap_AprobeStateT;

ap_AprobeStateT ap_CurrentAprobeState (void);

Is the target executable running or are we formatting?

void ap_InitializeAprobe (void);

Call this function to initialize Aprobe library if you plan to use it for something other than UAL-driven tools like aprobe and apformat. This would set ap_AprobeState to ap_AprobeTime.

ap_BooleanT ap_ImmediateFormat (void);

Return TRUE if data is sent directly to the format routine instead of being logged. This is always true at format time.

Enabling and Disabling Probes

Aprobe provides mechanisms to enable/disable specific probes, all probes in a thread, or all probes in the program.

Disabling a Specific Probe

When you declare a probe like this:

probe "main"
{
  on_entry ...
} MainProbe;

you end up with a probe instance (called MainProbe in this example).

The probe instance is really a pointer to an internal structure:

typedef struct _ProbeInstance_  *ap_ProbeInstancePtrT;

You can use this probe instance pointer to enable and disable the probe by passing it to the following functions:

void ap_EnableProbe(ap_ProbeInstancePtrT);
void ap_DisableProbe(ap_ProbeInstancePtrT);

Example: To make some probes active only while processing a particular message. You might write:

if (MyMessage == 100)
{
  ap_EnableProbe (MyRadarProbe);
}

One can enable/disable the current probe from within an action using the following macro:

#define ap_ThisProbe private

Example (see also "Avoiding recursive probe activation"):

probe "S1" 
{
  on_entry // to S1
  {
    ap_DisableProbe(ap_ThisProbe);
  }
} // end probe

Sometimes you may want to temporarily enable probes. For instance, suppose on_entry to routine A you make a call to another routine in your application (say B) which calls routine C. You have a probe on C which you want to happen. You could bracket the call as follows:

on_entry
{
    // Turn on probes before the call
    ap_DecrementDisableProbesCount (ap_ThreadContextPtr);
    // Make the call
    $B (1, 2, 3);
    // Turn probes back off
    ap_IncrementDisableProbesCount (ap_ThreadContextPtr);
}
void ap_IncrementDisableProbesCount (ap_ThreadContextPtrT);
void ap_DecrementDisableProbesCount (ap_ThreadContextPtrT);

These two routines can be used to turn off / on probes for the thread. Normally when a probe is hit, Aprobe disables further probes in the thread for the duration of the action. This is to prevent recursive loops (for instance imagine if a probe on printf() called printf()and we did nothing about it).

ap_BooleanT ap_ProbesAreDisabled(ap_ThreadContextPtrT);

returns TRUE if probes are disabled for the given thread context.

Disabling All Probes

The RootCause product provides flexibility in controlling enabling and disabling of traces. There are three levels of control: The top two levels are program-wide and per-thread. These both rely on explicit checking in the user-written probe code itself. The predefined probes such as trace.ual check these.

void ap_RootCauseTraceEnable(void);
void ap_RootCauseTraceDisable(void);

Use these two functions to enable or disable tracing program-wide. These increment and decrement a global count.

ap_BooleanT ap_RootCauseTraceIsEnabled;

Use this macro in your probe to check the global trace-enabled count:

void ap_RootCauseTraceEnableForThread(ap_ThreadContextPtrT);
void ap_RootCauseTraceDisableForThread(ap_ThreadContextPtrT);

Enable or disable tracing in the specified thread:

ap_BooleanT RootCauseTraceIsEnabledForThread;

Use this macro in your probe to check the per-thread trace-enabled count. This is independent of the global count above.

The lowest level is the patch level, at which all probes at a give address are considered enabled or disabled regardless of what those probes do.

ap_BooleanT ap_EnableProbePatch(ap_FunctionIdT);
ap_BooleanT ap_DisableProbePatch(ap_FunctionIdT);

Enable/disable of patches created by ap_InstrumentFunction (which includes all that are generated by APC).

ap_BooleanT ap_RootCauseTraceIsEnabledForFunction(F)

This macro returns TRUE if this function's probe patch is enabled and ap_RootCauseTraceIsEnabled.

ap_BooleanT ap_RootCauseTraceIsEnabledForProbeAction

This macro can only be called within a probe action (e.g., on_entry) in place of ap_RootCauseTraceIsEnabledForFunction but is much quicker.

void ap_Java_EnableProbePatch(int MethodId);
void ap_Java_DisableProbePatch(int MethodId);

Enable/disable patch for the specified Java method. If disabled, all probes in all threads are disabled.

ap_BooleanT ap_RootCauseTraceIsEnabledForMethod(M)

Just like ap_RootCauseTraceIsEnabledForFunction but for Java. This macro returns true if probes for this method are enabled both at the patch level and globally.

Overview of Modules, Symbols, and Functions

Aprobe can identify all symbols within your executable or in any shared libraries which your executable accesses. Each symbol has a symbol ID which is composed of a module ID which indicates which module the symbol is in and an index which is unique within that module. Your code should not rely on the module IDs being the same from one run to the next since the IDs are assigned in the order provided by the operating system.

For a given module, provided that the module has not been in any way modified, the symbol index within that module will be consistent from one run to another. However, it is not recommended that your probes rely on this to (for example) create tables based on symbol index since the indices may not be contiguous. Usually you will be interested in tables based on function IDs, defined below.

Each symbol has a name (what the symbol is called) and an address (the memory location that the symbol points to). The memory could be the start of a function, a piece of data or a read-only constant. For each symbol we also have a length (such as the number of bytes of data or the length of the function's code. Additionally, some symbols are local in scope to a file (for instance, static variables/functions in C). One may have to explicitly specify this file name to uniquely identify such symbols.

Your probes may access data from many kinds of symbols but probes can only be set on function symbols. Each function within a module is given a unique function ID - your probe knows the current function ID and the current module so you can easily build lookup tables when you need to quickly store data about a lot of subprograms.

Aprobe Module Operations

A "module" is a loadable object file. Your application is a module; shared libraries (.so) are modules; and Aprobe's UALs are modules.

As mentioned above, each module has a unique module ID:

typedef private ap_ModuleIdT;
extern const ap_ModuleIdT ap_NoModuleId;;
ap_BooleanT ap_IsNoModuleId(ModuleIdT ModuleId);
ap_BooleanT ap_ModuleIdsEqual(ModuleIdT,ModuleIdT);

Each module has a module kind:

typedef enum {
  ap_NoModuleKind,        /* not a module */
  ap_UalModule,           /* an Aprobe UAL */
  ap_TargetModule,        /* a user application module */
  ap_AprobeRuntimeModule  /* special Aprobe runtime UAL */
} ap_ModuleKindT;

ap_ModuleKindT ap_ModuleKind (ap_ModuleIdT ModuleId);

A number of functions are provided to get information about a module:

ap_NameT ap_ModuleName (ap_ModuleIdT ModuleId);
ap_NameT ap_FullModuleName (ap_ModuleIdT ModuleId);

Get the module (file)name from the module ID. ap_ModuleName always returns the basename, which is to be used when specifying a module to aprobe. ap_FullModuleName returns the full path to the module (if known) . The returned string is allocated by Aprobe when the executable begins and can be relied upon to be valid for the program duration. It must not be freed by your probes.

ap_ModuleIdT ap_ModuleNameToId (ap_NameT ModuleName);

Get the module ID for the given module name.

ap_ModuleIdT ap_AddressToModule (ap_AddressT Address);

Get the module that this address is within.

ap_ModuleIdT ap_ApplicationModuleId (void);

Get the module ID for the executable module.

ap_NameT ap_ApplicationModuleName (void);

Get the name for the executable module.

ap_ModuleIdT ap_SymbolToModule(ap_SymbolIdT SymbolId);

Get the module ID from the symbol ID.

ap_ModuleIdT ap_FunctionToModule (ap_FunctionId FunctionId);

Get the module ID from the function ID.

ap_Uint32 ap_NumberOfModules (void);

Get the number of modules *currently* loaded. This can change over the course of time so do not store this value or otherwise make your code reliant upon it being constant.

ap_ModuleIdT ap_ModuleIndexToId (ap_Uint32);

Convert the module's ordinal number to its ModuleId.

ap_Uint32 ap_ModuleIdToModuleIndex (ModuleIdT)

Get the module index from a module ID.

ap_AddressT ap_ModuleDataOrigin(ap_ModuleIdT)

Get the data origin address for the given module.

ap_AddressT ap_ModuleTextOrigin(ap_ModuleIdT)

Get the text (instruction) origin address for the given module.

ap_Uint32 ap_GetModuleChecksum(ap_ModuleIdT)

Generate a checksum for the module. The implementation of this value is such that it differs if there are executable code or data differences, but is the same for a module before and after it has been stripped.

Module Iterator

Your code can iterate through all modules. You must provide an iterator routine which corresponds to the type ap_ModuleIteratorProcT to which will be passed the module ID of each module that is currently loaded. It will also be passed a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughModules:

typedef ap_BooleanT (*ap_ModuleIteratorProcT) (
  ap_ModuleIdT  ModuleId,
  void          *Data);

void ap_IterateThroughModules (
  ap_ModuleIteratorProcT Iterator,
  void                   *Data);

Aprobe Symbol Operations

Each symbol is given a symbol ID:

typedef private ap_SymbolIdT;

const ap_SymbolIdT ap_NoSymbolId;

ap_BooleanT  ap_IsNoSymbolId(SymbolIdT);

ap_BooleanT  ap_SymbolIdsEqual(ap_SymbolIdT, ap_SymbolIdT);
typedef enum {
 ap_NoSymbolKind,
 ap_FunctionSymbol,
 ap_FunctionPointerSymbol,
 ap_DataSymbol,
 ap_ConstantSymbol } ap_SymbolKindT;
ap_SymbolKindT ap_SymbolKind (ap_SymbolIdT SymbolId);

Return the kind of symbol this is. ap_NoSymbolId or an invalid symbol ID results in ap_NoSymbolKind.

ap_Uint32 ap_NumberOfSymbols (ap_ModuleIdT ModuleId);

Get the number of symbols in a module.

ap_NameT ap_SymbolName (ap_SymbolIdT SymbolId);

Get the symbol name from the symbol ID. The returned string is allocated by Aprobe when the executable begins and can be relied upon to be valid for the program duration. It must not be freed by your probes.

ap_NameT  ap_SymbolFileName (ap_SymbolIdT SymbolId);

Get the name (if any) of the source file the symbol is defined in. This is available for all symbols for which a file is known; symbols local to a file, such as static C functions, must have a file name. If no name is known, returns ap_NoName. The returned string is allocated by Aprobe when the executable begins and can be relied upon to be valid for the program duration. It must not be freed by your probes.

ap_BooleanT ap_IsGlobalSymbol(ap_SymbolIdT SymbolId);

Return TRUE if the symbol has a global scope as opposed to a (static) file scope.

ap_SymbolIdT ap_SymbolNameToId (
  ap_ModuleIdT   ModuleId,
  ap_NameT       SymbolName,
  ap_NameT       FileName
  ap_SymbolKindT SymbolKind);

Get the symbol ID from the given module and symbol name. Use SymbolKind to designate the kind of symbol it is, use NoSymbolKind if unknown. Pass FileName => ap_ExternSymbol if the symbol must be external. Passing FileName => ap_NoName will produce the best match: an external symbol if there is one or a local file symbol if there is exactly one. An error message will be issued if the search results in multiple local symbol matches.

ap_NameT ap_ExternSymbol;

ap_ExternSymbol is a special value to be passed to ap_SymbolNameToId instead of a FileName when the "extern" instance of the given SymbolName is wanted.

ap_AddressT ap_SymbolAddress (ap_SymbolIdT SymbolId);

Get the address to which the symbol refers. This will be the start of the function when the symbol refers to a code location or the beginning of a variable, etc.

ap_AddressT ap_GlobalSymbolAddress(     
     ap_NameT SymbolName,
    ap_NameT ModuleName);

A composite function for simplifying global data accesses. It gives an error message if a symbol isn't defined. If ModuleName is "" or 0, uses the application module. Symbols requiring an explicit filename cannot be accessed with this function.

#define ap_ExeGlobalSymbolAddress(S)   ap_GlobalSymbolAddress(S,0)
#define ap_DllGlobalSymbolAddress  ap_GlobalSymbolAddress
ap_AddressT ap_FunctionPointer(   
   ap_ModuleIdT ModuleId,   
   ap_NameT     FunctionName,   
   ap_NameT     FileName);

Return the "descriptor" for the given function, so it can be called by pointer. If in C you would write &Foo, then in APC you would write ap_FunctionPointer(ap_ApplicationModuleId(), "Foo()", ""). This differs from using ap_SymbolAddress( ap_SymbolNameToId(..., FunctionPointerKind)) in that it creates a descriptor if necessary.

ap_SizeT  ap_SymbolSize (ap_SymbolIdT SymbolId);

Get the length of the item pointed to by the symbol.

ap_SymbolIdT  ap_AddressToSymbol (ap_AddressT Address);

This is the reverse of ap_SymbolAddress: Given an address, find the symbol.

ap_SymbolIdT ap_SymbolIndexToId (
  ap_Uint32       SymbolIndex,
  ap_ModuleIdT ModuleId);

Create a symbol ID from an index and a module ID.

ap_SymbolIdT ap_SymbolToIndex(SymbolIdT);

Given a symbol ID, get its sequential index within its module.

char *ap_FormatSymbol(char *Buffer, SymbolIdT SymbolId);

Place into Buffer the "demangled" image of the specified symbol, preceded by its source file if it's a local symbol.

ap_SizeT ap_FormatSymbolSize(ap_SymbolIdT SymbolId);

returns a minimum buffer size to pass to ap_FormatSymbol();

void ap_PrintSymbol(SymbolIdT SymbolId);

Print to standard output the image returned by ap_FormatSymbol(), followed by a newline.

Symbol Iterator

Your code can iterate through all symbols within a module. You must provide an iterator routine which corresponds to the type ap_SymbolIteratorProcT to which will be passed the symbol ID of each symbol in a given module, and a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughSymbols:

typedef ap_BooleanT (*ap_SymbolIteratorProcT) (
   ap_SymbolIdT  SymbolId,
   void          *Data);

void ap_IterateThroughSymbols (
   ap_SymbolIteratorProcT Iterator,
   ap_ModuleIdT         ModuleId
   void                   *Data);

Dynamically Inserting Symbols

If a module does not have the symbol information needed to apply probes, but you know that symbol information from other means, you may dynamically create a new a new symbol ID within the module, and subsequently use that information to apply a probe as if that symbol defined a known function:

ap_SymbolIdT ap_RecordDynamicFunctionSymbol (
  ap_ModuleIdT        ModuleId,
  ap_NameT        FunctionName,
  ap_NameT        FileName,
  ap_OffsetT        OffsetInModule,
  ap_SizeT        Size,
  ap_Uint32       ModuleChecksum);

If "FileName" is ap_ExternSymbol, the function symbol is global, otherwise it is local. If ModuleChecksum is non-zero then this is compared with the module checksum returned by ap_GetModuleChecksum() and the symbol is only added if the checksums match. ap_NoSymbolId is returned if the symbol cannot be created. This can only be called before the completion of the probe program on_entry action.

Function ID Operations

The parameter FunctionId is implicit to each probe action (e.g., on_entry, see "Action Routine Prototype"). This is the function ID for the probed function. Like a symbol ID it cannot exist in isolation from a module. For each module, the function IDs are contiguous and their function index values -- the value returned by ap_FunctionIdToIndex(FunctionId) -- will range from 0 to (ap_NumberOfFunctions() - 1).

typedef private ap_FunctionIdT;
const ap_FunctionIdT ap_NoFunctionId;
ap_BooleanT ap_FunctionIdsEqual(FunctionIdT, FunctionIdT);
ap_BooleanT ap_IsNoFunctionId(FunctionIdT);
ap_FunctionIdT ap_SymbolToFunction (
    ap_SymbolIdT SymbolId);

Given a symbol ID, get the function ID. ap_NoFunctionId is returned if symbol is not a valid function.

ap_SymbolIdT ap_FunctionToSymbol(
    ap_FunctionIdT FunctionId);

The reverse: Given a function ID, get the symbol ID.

ap_Uint32 ap_NumberOfFunctions (ap_ModuleIdT ModuleId);

Get the number of functions in a module.

ap_FunctionIdT ap_FunctionIndexToId (
    ap_Uint32    FunctionIndex,
    ap_ModuleIdT ModuleId);

Create a function ID from an index and a module ID.

ap_Uint32 ap_FunctionIdToIndex(FunctionIdT);

Get the index from a function ID.

Function Iterator

Your code can iterate through all functions within a module. You must provide an iterator routine which corresponds to the type ap_FunctionIteratorProcT to which will be passed the function ID of each function in a given module. It will also be passed a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughFunctions():

typedef ap_BooleanT (*ap_FunctionIteratorProcT) (
   ap_FunctionIdT  FunctionId,
   void            *Data);

void ap_IterateThroughFunctions (
   ap_FunctionIteratorProcT Iterator,
   ap_ModuleIdT             ModuleId,
   void                     *Data);

Source Line Operations

Aprobe can use debug information within the executable and its component object files to identify to which source lines particular addresses relate. For each 'line' that Aprobe sees within a given function, a unique line ID is created. Note that a given source line can have multiple line IDs for different offsets in the code since certain control constructs (case statements, etc.) may have multiple points in the generated code for the same line number. It is important to remember that a given line ID has a line number and an offset, not a unique line number.

Functions are provided to convert offsets and addresses to line IDs, get the line ID(s) for a given line number, iterate through line IDs and so on.

Line numbers are represented by strings. In the future, this will allow embedded code such as macro expansions, C template instances, or Ada generic instantiations, to be represented more accurately. Currently a line number will always be a simple string image of a single integer (e.g. "100"). This would represent source line number 100 in the enclosing file.

The definition of a line ID itself is private:


typedef private ap_LineIdT;
 
 const ap_LineIdT ap_NoLineId;
 
 ap_BooleanT ap_IsNoLineId (ap_LineIdT);

ap_BooleanT ap_LineIdsEqual (ap_LineIdT, ap_LineIdT);
 
 #define ap_CurrentLineId private

A macro to get the current Line ID, within an on_line or on_offset action of a probe only.


ap_NameT ap_LineIdToSourceFile (ap_LineIdT);

Get the source file name from a Line ID.


int ap_LineIdToSize (ap_LineIdT);

Get the size (number of bytes spanned by the line) from a Line ID.


ap_LineIdT ap_OffsetToLineId (
  ap_FunctionIdT FunctionId,
  ap_OffsetT     Offset);

Get the line ID for a given function and offset. ap_NoLineId is returned if there is no line information found. If there are multiple line IDs for the same offset, the first one is returned.


ap_OffsetT ap_LineIdToOffset (ap_LineIdT);

Get the offset from a Line ID.


ap_LineIdT ap_LineIndexToId (
  ap_FunctionIdT FunctionId,
  int            Index);

Each line Id has an index which is unique for that function. Get the line ID corresponding to the index. ap_NoLineId is returned if there is no line for this index.


int ap_LineIdToIndex(LineIdT)

Get the index for a line ID.


ap_LineIdT ap_AddressToLineId (ap_AddressT Address);

Get the line ID for a given address. ap_NoLineId is returned if the address does not correspond to a valid function or if the offset in that function has no line information. If there are multiple line IDs for the that address, the first one is returned.


ap_LineIdT ap_LineNumberToLineId (
  ap_FunctionIdT  FunctionId,
  char           *LineNumber,
  ap_BooleanT     IsRelative);

Get the line ID for a given function and line number. (The Is_Relative flag currently has no effect.) Note that there may be several LineIds which map to a given line number. This function will return the first one in the list. If no matching line is found in the function, ap_NoLineId will be returned.


ap_NameT ap_LineIdToNumber(LindIdT);

The line number for a line ID.


ap_LineIdT ap_NextLineIdForSameLine (ap_LineIdT LineId);

Given a line ID, get the next line ID which has the same line number. If there are no more line IDs, ap_NoLineId is returned.


ap_LineIdT ap_PreviousLineIdForSameLine (   ap_LineIdT LineId);

Given a line ID, get the previous line ID which has the same line number. If there are no more line IDs, ap_NoLineId is returned.


ap_LineIdT ap_NextLineIdForSameOffset (   ap_LineIdT LineId);

Given a line ID, get the next line ID which has the same offset. If there are no more line IDs, ap_NoLineId is returned.


ap_LineIdT ap_PreviousLineIdForSameOffset (     ap_LineIdT LineId);

Given a line ID, get the previous line ID which has the same offset. If there are no more line IDs, ap_NoLineId is returned.


int ap_NumberOfLines (ap_FunctionIdT FunctionId);

Get the number of lines in a function.

Line Iterator

Your code can iterate through all lines within a function. You must provide an iterator routine which corresponds to the type ap_LineIdIteratorProcT to which will be passed the line ID of each line in the given function. It will also be passed a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughLines():


typedef ap_BooleanT (*ap_LineIdIteratorProcT) (
  ap_LineIdT  LineId,
  void       *Data);

void ap_IterateThroughLines (
  ap_LineIdIteratorProcT  Iterator,
  ap_FunctionIdT          FunctionId,
  void                   *Data);

Process and Thread Support

The types ap_ProcessIdT and ap_ThreadIdT represent platform-independent views of a process and thread ID.

typedef long ap_ProcessIdT;

#define ap_NoProcessId ((ap_ProcessIdT) -1)

typedef private            ap_ThreadIdT;

const ap_ThreadIdT            ap_NoThreadId;

ap_BooleanT ap_IsNoThreadId(ap_ThreadIdT);

ap_BooleanT ap_ThreadIdsEqual(ap_ThreadIdT, ap_ThreadIdT);
ap_ProcessIdT ap_ProcessId (ap_ThreadIdT ThreadId);

Given the thread ID, return its process ID as defined by the OS. This will work at format time as well as while the executable is running.

ap_ThreadIdT ap_ThreadId (void);

Get the current thread ID. The macros below should be used instead of ap_ThreadId when possible for efficiency.

#define  ap_ThreadContextToThreadId(T) (T->ThreadId)

Get the current thread ID from a thread context pointer.

#define  ap_CurrentThreadId (ap_ThreadContextPtr->ThreadId)

Get the current thread ID from within a probe action.

ap_ThreadIdT ap_ParentThreadId (ap_ThreadIdT);

Given a thread ID, get the thread ID of the thread that created this thread.

ap_AddressT ap_ThreadEntryPoint (ap_ThreadIdT ThreadId);

Given a thread ID, get the address of a function, which represents the thread's entry point.

ap_BooleanT ap_IsMainThread (ap_ThreadIdT ThreadId);

Given a thread ID, return TRUE iff this is the main thread of the application.

ap_BooleanT ap_IsChildProcess (ap_ThreadIdT ThreadId);

Given a thread ID, return TRUE iff the given thread belongs to a child process of that owning the initial thread.

ap_BooleanT ap_GetThreadStackBounds (
  ap_ThreadIdT ThreadId,
  ap_AddressT  *StackLowPtr,
  ap_AddressT   *StackHighPtr);

Get the stack bounds for the given thread. TRUE is returned if they could be determined, FALSE if they couldn't.

ap_AddressT ap_ThreadStackBaseAddress (
  ap_ThreadIdT ThreadId);

Get the base address for the given thread. NULL is returned if the thread could not be found.

ap_SizeT ap_ThreadStackSize (ap_ThreadIdT ThreadId);

Get the stack size. NULL is returned if the thread could not be found.

There may be multiple threads which use the same entry point. Aprobe allocates an index for each one.

ap_Uint32 ap_ThreadIndex (ap_ThreadIdT ThreadId);

#define ap_NoThreadIndex ((ap_Uint32) -1)


Thread Iterator

Your code can iterate through all threads. You must provide an iterator routine which corresponds to the type ap_ThreadIteratorProcT to which will be passed the thread ID of each thread. It will also be passed a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughThreads():

typedef ap_BooleanT (*ap_ThreadIteratorProcT) (
  ap_LineIdT  LineId,
  void       *Data);

void ap_IterateThroughThreads (
  ap_ThreadIteratorProcT  Iterator,
  ap_ThreadIdT          ThreadId,
  void                   *Data);


Child Process Callback

Your code register for a callback to occur whenever a child process is created. You provide a callback routine which corresponds to the type ap_NewProcessCallbackPtrT to which will be passed the ap_ThreadContextPtrT:

typedef void (*ap_NewProcessCallbackPtrT) (ap_ThreadContextPtrT);

void ap_AddNewProcessCallback (
  ap_NewProcessCallbackPtrT Callback);

Instrumentation Support

Instrumentation is the applying of the code patches necessary to cause probe actions to be invoked at run-time. It is done once at program start (probe program on_entry).

typedef ap_Uint32 ap_ProbeVectorIndexT;
#define ap_NoProbeVectorIndex ((ap_ProbeVectorIndexT) -1)

The index into the vector of probes.

const ap_ProbeVectorIndexT ap_AllProbeVectorIndex;

A special value associated with "probe all":

ap_ProbeVectorIndexT ap_InstrumentFunction(
  ap_FunctionIdT FunctionId)

Instrument a function. Ordinarily you will use the probe extensions in an APC file to instrument a function and attach a probe to it. However, for special uses (such as reading the list of symbols from a file) you can use the following routine to instrument a function. There are a couple of restrictions which must be noted: a) Currently this routine will only be effective when called from the on_entry action of a 'probe program'. b) You must instrument the routine before you try and attach a probe to that routine.

ap_BooleanT ap_InstrumentOffset (
  ap_OffsetT              Offset,
  ap_ProbeVectorIndexT              ProbeVectorIndex,
  ap_ProbeActionReasonT             ActionReason);

Instrument the instruction at offset bytes into the function. Return TRUE if successful, FALSE, otherwise. Note that you must pass in the ProbeVectorIndex returned from ap_InstrumentFunction rather than the function ID itself.

ap_BooleanT ap_IsOffsetInstrumented (
  ap_FunctionIdT          Function,
  ap_OffsetT          Offset);

Return TRUE if the given offset is instrumented:

void ap_DoNotInstrumentFunction (ap_FunctionIdT FunctionId);

Will make sure the given SymbolId will never get instrumented.

void ap_DoNotInstrumentByName (
  ap_NameT ModuleName,
  ap_NameT FunctionName,
  ap_NameT FileName);

This is similar to ap_DoNotInstrumentFunction but takes names instead of a function ID. The implementation differs however in that: a) You can specify a wildcard (only '*' is allowed) for the module name in which case the function is prevented from being instrumented in all modules. b) This will not cause a module to be loaded. Instead, when the module is read for some other reason, the functions will be marked as non-instrumentable.

ap_BooleanT ap_IsInstrumented (ap_FunctionIdT FunctionId);

Is the function instrumented?

ap_BooleanT ap_InstrumentLine (ap_LineIdT LineId);

Instrument the given line. TRUE is returned for success.

ap_BooleanT ap_IsLineInstrumented (ap_LineIdT LineId);

Return true if the given line ID is instrumented

ap_LineIdT ap_RegisterAndInstrumentLine (
  ap_ProbeVectorIndexT              ProbeVectorIndex,
  char              *LineNumber,
  ap_OffsetT              Offset,
  int             OffsetRange,
  ap_NameT              Filename);

Instrument and register a line ID. This is the same as calling ap_RegisterLineId and passing the result to ap_InstrumentLine. The return is as per ap_RegisterLineId. Again, this must not be called after the main thread's on_entry actions have completed.

void ap_InstrumentAllLines (
  ap_ProbeVectorIndexT ProbeVectorIndex);

Instrument all lines in a function. Errors are generated for any lines which could not be instrumented. Again you must pass in the probe vector index rather than the function ID.

Logging Support

Logging data to an APD file is a central feature of Aprobe. Most of its complexity is abstracted away from the user by the [[AUG_Tools_Reference#a[c|apc]] compiler, but many powerful features are available when the need arises.

In this section only a few of the types and operations associated with logging are described. See the source for aprobe.h for the full list, and the source for a predefined probe such as trace.apc for usage examples. It is also recommended you contact OC Systems support for assistance.

First, there is the concept of a log method, which defines how the raw data is written and read, or "logged" and "delogged". This could be to a memory-resident circular buffer, to a disk file, or directly to other storage device.

Aprobe provides a very flexible solution which will allow multiple log methods to be invoked for a given log statement and have each of those methods get the data at format time. The data could then be passed to one or more format routines - or even not formatted if no format was registered. This allows selective formatting to be made on the raw data. For every kind of logging facility that is provided, whether by default in the runtime or by additional user-supplied code, a set of routines--to initialize the logger, to log data, and to close the logger--must be provided. This is called a log method (and has an associated log method ID).

Then there is the concept of a log ID, which defines both what log method is to be used to on that data, and how the raw data is to be formatted for human consumption. There can be multiple log methods and multiple formats associated with a given log ID, and formats can be added or changed at apformat time without rerunning the application.

A log ID must be provided with every log request. The log ID can be system generated--as is the case for automatic formatting done by the APC compiler--or maintained by the user. When a log request is made, the system looks for any log methods that have been registered for that ID. If there aren't any, a default set of log methods is invoked. Each log ID can also be associated with a format routine.

At format time, the format routine will be called when data for that log ID is read from the incoming device. Like the log method, there can be multiple delog methods to retrieve data for formatting. Each delog method will be called in turn; no attempt to combine the output from different delog methods is made.

typedef private ap_LogIdT;

A log ID is just an integer, and can be specified as such in APC. By convention, IDs from 1 to INT_MAX are available for users to allocate, and IDs from INT_MIN to -2 are system-generated or specified.

typedef private ap_LogMethodIdT;

A log method ID is returned when a log method is registered, so it can be associated with log IDs.

Default Log Methods

There are two log methods defined by Aprobe:

ap_LogMethodIdT ap_PersistentLogMethod;

Used for logging data that is essential for the correct processing of formatting actions, and other data which should not be lost.

ap_LogMethodIdT ap_ApdLogMethod;

The log method based on the APD files.

const ap_LogMethodIdT ap_DefaultLogMethod;

At any time there is a default log method that is used unless another is explicitly specified. Initially this is ap_ApdLogMethod.

Circular Buffer Log Method

In addition one can create a log method which records data in a circular (wraparound, memory-mapped) buffer of a given size. This is used by the trace.apc predefined probe.

Two parameters are passed to ap_CreateCircularBufferLogMethod:
MaxBufferSize -
size of the buffer for storing the logged data. For maximum performance the size must be equal to a power of 2. Should a different size be provided the size will be reduced to the nearest power of 2 number and a warning will be issued.
FileName - name of a file to which the snapshots should be written upon program termination or a call to ap_DoCircularBufferSnapshot(). Provide ap_NoName if you would like the data to be written to APD file instead.

ap_LogMethodIdT ap_CreateCircularBufferLogMethod(
  ap_SizeT MaxBufferSize,
  ap_NameT FileName);

Create new log method for a circular buffer of the given size.

ap_BooleanT ap_DoCircularBufferSnapshot(
  ap_LogMethodIdT         LogMethodId,
  ap_NameT          SnapshotFileName);

Write out the data collected in the circular buffer to the APD file or a snapshot file if one was provided upon circular buffer creation or as a parameter to this call.

void ap_FormatDataFromFile(ap_NameT FileName);

Format the named APD file. This may be one written from ap_DoCircularBufferSnapshot or one directly written by ap_ApdLogMethod.

Custom Log Methods

You do not have to write a log method unless you have a specific need which is not covered by the Aprobe built-in log methods defined above.

Log methods have an initialization routine, a routine to log data and a routine to close down logging, and several others, as provided to ap_InitializeLogMethod shown below:

ap_LogMethodIdT ap_InitializeLogMethod (
  ap_LogMethodInitT             InitProc,
  ap_LogMethodLogT              LogProc,
  ap_LogMethodCloseT            CloseProc,
  ap_LogMethodLockT             LockProc,
  ap_LogMethodReleaseT          ReleaseProc,
  ap_LogMethodSnapshotT         SnapshotProc,
  ap_LogMethodMarkerT           MarkerProc,
  void                         *InitData);

Register and initialize a log method. The InitData is passed to the method's InitProc. The prototypes for each of the Proc arguments follow.

typedef ap_BooleanT ap_LogMethodInitT (
  void  *InitData,
  void **MethodData);

The initialization routine is passed a generic pointer which contains whatever information the log method requires at initialization. The method data returned is passed to the log and close methods; it can be whatever the method needs it to be.

void *ap_GetLogMethodData(ap_LogMethodIdT);

Given the LogMethodId of a log method return the InitData passed to ap_InitializeLogMethod.

typedef struct
{
   ap_Uint32  Length;
   void       *DataPtr;
} ap_LogItemT, *ap_LogItemPtrT;

Each item passed to a log method is a length and a pointer:

typedef ap_BooleanT ap_LogMethodLogT (
  ap_Uint32         NumItems,
  ap_LogItemPtrT          ItemList,
  void          *MethodData,
  int         TotalLength);

The ap_LogMethodLogT routine is passed a list of length NumItems of data items. Each item is a data pointer and a length. The total length of the data (that is the sum of the lengths in the list plus the number of items * size of a length field) is given so that the log method can quickly 'reserve' room in the output device and then copy the items one at a time.

ap_LogMethodLogT *ap_GetLogMethodProc(ap_LogMethodIdT);

Given the LogMethodId of a log method, return the LogProc passed to ap_InitializeLogMethod.

typedef ap_BooleanT ap_LogMethodCloseT (void *MethodData);

The close routine is called when a method is unregistered.

Log methods can (optionally) support locking a section of memory and returning this to the caller to write to (see ap_LockLogArea below). When the caller is done they will release the data. The following types are used to specify the lock and release routines.

typedef ap_AddressT (*ap_LogMethodLockT) (
  ap_LogIdT     LogId,
  void      *MethodData,
  void      **LockDataPtr,
  ap_SizeT      Size);

Get a lock.

typedef void (*ap_LogMethodReleaseT) (
  void  *MethodData,
  void **LockDataPtr);

Release the lock.

Log methods can (optionally) support a snapshot mechanism (see ap_LogSnapshot below). This is primarily used by the APD ring but other methods could support it too. The log method is provided the snapshot string to log but no log ID; each method should have it's own mechanism for logging and formatting the snapshot data depending on what additional information they need.

typedef ap_SnapshotIdT (*ap_LogMethodSnapshotT) (
  void      *MethodData,
  ap_NameT      SnapshotKeyword,
  ap_NameT      SnapshotString);

Record a snapshot.

Log methods can (optionally) support a marker mechanism. This is very similar to snapshots in that a chain of markers is maintained through the file but the data logged and the formatting is exactly the same as for data logged by >ap_LogData. The log method should record the marker with the marker keyword and any other data separately from the user's data so that they are formatted independently, but the data must be logged in contiguous memory. Like the snapshot mechanism this is primarily intended for quick access to data in the APD ring.

typedef ap_BooleanT (*ap_LogMethodMarkerT) (
  ap_NameT          MarkerKeyword,
  ap_Uint32         NumItems,
  ap_LogItemPtrT          ItemList,
  void          *MethodData,
  int         TotalLength);

Record a marker.

Log IDs

ap_LogIdT ap_GenerateLogId (
  ap_NameT LogIdName,
  ap_NameT UalName);

Generate a log ID from a name. You can rely on the same ID being returned for the same name during the execution of your probes (however, you may not get the same ID the next time to execute your program). The mapping is stored away and is available at format time so that you will get the same log ID as was used to log the data.

ap_LogIdT ap_IntegerToLogId (long);

Convert an integer to a Log ID. This is done automatically by the APC compiler.

ap_BooleanT ap_RegisterLogIdWithLogMethod(
  ap_LogIdT         LogId,
  ap_LogMethodIdT         LogMethodId,
  ap_BooleanT         KeepDefaultMethods);

Register a log ID with a log method. If KeepDefaultMethods is TRUE, and this is the first time that the log ID has been registered, the default log methods are added to the list. TRUE is returned for success.

void ap_RestoreLogMethodsToDefault(ap_LogIdT LogId);

Make a given log id use default log methods.

ap_BooleanT ap_RegisterDefaultLogMethodId (
  ap_LogMethodIdT         LogMethodId,
  ap_BooleanT         Replace);

Register a log method with the default set of log methods. If an item is logged which has not been associated with any specific methods, this default set is used. Set Replace parameter to TRUE if you would like the new LogMethod to be used instead of any other default log methods that may have been defined earlier. Set it to FALSE if you would like your log method to be used in addition to the previously defined ones.

ap_BooleanT ap_RegisterLogIdAsMarker (
  ap_LogIdT     LogId,
  ap_NameT      MarkerKeyword);

Register the log Id as a marker with the given marker keyword. Passing in NULL will result in unregistering the marker if one existed previously. Passing in ap_NoName causes an anonymous marker to be logged. The log method may decide to log less information about this marker and a marker format routine should ignore the marker. It's role is to ensure that the data logged with the marker is formatted when in marker-only mode.

ap_LogIdEntryPtrT ap_GetLogIdEntryPtr (ap_LogIdT LogId);

Get the log ID data pointer for the log ID. The idea here is to get this pointer once to save looking it up on each log statement. However, probes that make use of this should be careful to get the pointer after they have registered the log ID so they actually get something useful back!

ap_LogData

ap_LogData is the basic mechanism for logging data. You will probably not have to use this interface directly since the Aprobe APC compiler will generate a call to this if you use the log keyword in your probes. However, for "power" users, the interface is made available.

void ap_LogData (ap_LogIdT LogId, ap_Uint32 NumItems, ...);

The ap_LogData routine takes a variable number of parameters:
LogId - the identifier for the data to be logged.
NumItems - The number of items being logged.
Then follows a variable number of arguments. Each one is a pointer to some memory where the data to be logged is located and the length of the data.

As a rough guide, to log data item I, the two arguments would be &I and sizeof (I). Note that this pair counts as a single item in the NumItems parameter. The full call would be:

ap_LogData (MyLogId, 1, &I, sizeof (I));

The same as the above but they take ap_LogIdEntryPtrT instead of the log ID.

void ap_LogDataWithPtr (
  ap_LogIdEntryPtrT           LogIdEntry,
  ap_Uint32           NumItems,
  ...);

In order to associate logged data with events in a program's execution, facilities to log the time and thread context of the data are provided. It is actually these ap_RootCause versions which are generated for an APC log directive.

void ap_RootCauseLogData (
  ap_ThreadContextPtrT            ThreadContextPtr,
  ap_LogIdT           LogId,
  ap_Uint32           NumItems,
  ...);

Exactly the same as #ap_LogData above, but also records the current time and thread ID.

void ap_RootCauseLogDataWithPtr (
  ap_ThreadContextPtrT            ThreadContextPtr,
  ap_LogIdEntryPtrT           LogIdEntry,
  ap_Uint32           NumItems,
  ...);

Exactly the same as ap_RootCauseLogData above, but takes an ap_LogIdEntryPtr instead of numeric LogID.

Instead of using ap_LogData or ap_RootCauseLogData, occasionally you will want to reserve a block of "memory" in the log file that you can write to. The following calls allow you to lock and release such an area. Be aware that not every log method will support this; additionally, while you have the buffer locked you must not make any other logs to the same method and should note that other threads writing to the log method may be blocked if a file switch is necessary.

A log ID must be provided for locking the area but the normal actions of obtaining the list of log methods and parameter profiles is bypassed. It is the caller's responsibility to ensure that the data is written in the correct format.

If ap_LockLogArea succeeds it will return an address the caller can write to and also set a pointer to some internal data that must be passed back to unlock it. The following is an example of this in practice:

{
  void        *LockData;
  ap_AddressT  LogAddress;
  LogAddress =
    ap_LockLogArea (ap_ApdLogMethod, &LockData, 1024);
  if (LogAddress)
  { // Write to the data area
    ap_ReleaseLockedLogArea (LockData);
  }
}
ap_AddressT ap_LockLogArea (
  ap_LogIdT         LogId,
  ap_LogMethodIdT         LogMethodId,
  void          **LockDataPtr,
  ap_SizeT          Size);

Allocate and lock Size bytes pointed to by the return value

void ap_ReleaseLockedLogArea (
  ap_LogMethodIdT         LogMethodId,
  void          **LockData);

Release the locked area after writing is done.

Snapshot Support

ap_LogSnapshot logs the given arguments as would printf(), identifying them as a snapshot with the returned snapshot ID.

For the APD ring, this call ensures that the APD file containing this snapshot is not deleted as part of normal APD file purging, nor are the N-1 earlier files in the ring, where N<n is the number of ring files specified by the aprobe -n option).

Note: This is not to be confused with ap_DoCircularBufferSnapshot, which does not provide a SnapshotProc. This is intended for the APD ring mechanism and discussion is limited to what the APD ring does. However, another log method can provide a snapshot capability which could have similar behavior.

This returns a positive ap_SnapshotIdT value if the snapshot was done successfully, or ap_NoSnapshot id if it fails (which might happen if logging were disabled).

Note that this does not stop logging, or force logging to resume in a new file. This implies that multiple snapshots may be logged to the same file.

Note: the data logged includes the ap_GetCurrentTime() value, so you should not include time in the logged name.

The parameters for ap_LogSnapshot are:
LogMethod - the method to log to, e.g. ap_ApdLogMethod
Keyword - a keyword used to sort the snapshots. You can use one of the predefined ones or any other you wish.
Format - a printf style format string followed by a variable parameter list with the arguments to the format string.

#define ap_NoSnapshotId ((ap_SnapshotIdT) 0)
#define ap_SnapshotErrorKeyword                        "ERROR"
#define ap_SnapshotExceptionKeyword                        "EXCEPTION"
#define ap_SnapshotInformationKeyword                        "INFORMATION"
#define ap_SnapshotWarningKeyword                        "WARNING"
ap_SnapshotIdT ap_LogSnapshot (
  ap_LogMethodIdT         LogMethod,
  ap_NameT          Keyword,
  ap_NameT          Format,
  ...);

Log a snapshot as described above. For example:

  ap_LogSnapshot (
    ap_ApdLogMethod,
    ap_SnapshotWarningKeyword,
    "Message timeout (%d): %s",
    Message->Kind,
    Message->Name);

APD File Information

Information about where and how data is being written is available at runtime through the following functions.

extern ap_NameT ap_GetApdFilename (void);

this is the same as the pathname of the .apd file without the extensions. The returned string should not be deallocated but will remain persistent through both runtime and format time. If multiple APD rings are being used (see below) this will be the path to the APD ring directory. When using RootCause, one can deduce the workspace directory with ap_Dirname(ap_GetApdFilename())

ap_Uint32 ap_GetNumberOfApdFiles(void);

Get the number of APD files per ring (aprobe -n option). This is only valid at runtime, and 0 will be returned at format time.

ap_Uint32 ap_GetNumberOfApdRings(void);

Get the number of APD rings preserved (aprobe -k option). This is only valid at runtime, and 0 will be returned at format time.

ap_Uint32 ap_GetNumberOfSnapshotFiles(void);

Get the number of snapshot files preserved per run (aprobe -t option). This is only valid at runtime, and 0 will be returned at format time.

ap_Uint32 ap_GetSizeOfApdFiles(void);

Get the maximum size in bytes of each APD file (aprobe -s option). This is only valid at runtime, and 0 will be returned at format time

Apd File Change Notification

If the default ApdLogMethod is being used (see Multiple APD Files), a callback can be issued per-process to say that the file to which data is being logged has changed. This callback will only be issued when an attempt to log data is made, and only delivered to the thread trying to log the data. At that point, the log will be done and then your callback will be called. This allows your callback routine to log data if you wish. The callback will be inherited across a fork. It is recommended that you reset it with a probe on fork if necessary. The callback routine is a parameterless function called every time the APD file is changed after registering it with ap_RegisterApdRingChangeCallback:

typedef void ap_ApdRingChangeCallbackT (void);

void ap_RegisterApdRingChangeCallback(
  ap_ApdRingChangeCallbackT Callback);

UAL Support

Probes are always linked into a special shared library called a User Action Library, or UAL file. A number of types and functions are provided for the management and identification of UALs.

Each UAL is given a unique identifier:

typedef private ap_UalIdT;
extern const ap_UalIdT ap_NoUalId;
ap_BooleanT ap_IsNoUalId(UalIdT UalId);
ap_BooleanT ap_UalIdsEqual(UalIdT, UalIdT);
ap_UalIdT ap_ThisUalId;

This is the current UAL ID at the point of reference.

ap_Uint32 ap_NumberOfUals(void);

This is the number of UALs loaded in the program so far.

ap_UalIdT ap_UalNameToUalId (ap_NameT UalName);

Get the UAL ID from a UAL name.

ap_UalIdT ap_GenerateUalId(ap_NameT UalName);

Register UalName with the runtime and get back its UalId.

ap_NameT ap_UalName (ap_UalIdT UalId);

Get the name from a UAL Id. The name is allocated by Aprobe on startupand must not be freed by your code.

UAL Parameters

Each UAL declares a copy of the following variables, which are exactly analogous to the arguments to main().

ap_Uint32 ap_UalArgc;

The number of arguments passed to the UAL on the aprobe or apformat command line.

ap_NameT *ap_UalArgv;

The argument list for the current UAL (ap_UalArgv[0] is the UAL name).

UAL Data Sharing

Aprobe supports sharing other UALs based upon an agreed-upon key. You call ap_UalDataKeyInit specifying a name and that returns a key. You can then use that key to set or get a pointer that can be used for any purpose. The key can be defined so that it is global or thread-specific.

typedef struct private ap_UalDataKeyT;

The UAL data key type.

extern const ap_UalDataKeyT ap_NoUalDataKey;
ap_BooleanT ap_IsNoUalDataKey(ap_UalDataKeyT);

Define and test for a null UalDataKey.

ap_UalDataKeyT ap_UalDataKeyInit (
   ap_NameT    KeyName,
   ap_BooleanT ThreadLocal);

Create or get the data key with the given name. If the key already exists a handle is provided to the existing key so long as the ThreadLocal flag matches the existing key. ap_NoUalDataKey will be returned if there was an error (such as specifiying the wrong thread flag for the given key name. Thread specific keys can only be created prior to the first probe thread on_entry (e.g. in probe program on_entry or early initialization routines.

void *ap_UalDataGet (ap_UalDataKeyT UalDataKey);

Get the existing key data. The data will be set to NULL when the key is first initialized. NULL will also be returned for thread specific keys if the current thread could not be determined or had not yet initialized (this is primarily during early initialization).

void ap_UalDataSet(ap_UalDataKeyT UalDataKey, void *Data);

Set the existing key data.

UAL Iterator

Your code can iterate through all UALs. You must provide an iterator routine which corresponds to the type ap_UalIteratorProcT to which will be passed the module ID of each module that is currently loaded. It will also be passed a typeless pointer which can point to any data you wish to pass to the iterator routine. The iterator routine must return either TRUE if you wish to keep iterating or FALSE if you wish to stop. To start iterating, pass your routine and the Data value to ap_IterateThroughUals:

typedef ap_BooleanT (*ap_UalIteratorProcT) (
  ap_UalIdT  UalId,
  void       *Data);

void ap_IterateThroughUals (
  ap_UalIteratorProcT Iterator,
  void                *Data);

Java Support

As described in "Writing Java Probes", Aprobe supports Java probes written in Java itself. These are supported by several classes defined in package com.ocsystems.aprobe and compiled into $APROBE/lib/aprobe.jar. The association between these Java probes and the classes and methods they are applied to is generally specified using an XML-format "deployment descriptor file", or "XMJ File", with suffix .xmj. It is this .XMJ file that is named on the aprobe (or "apjava") command line instead of a UAL. However, for lower-level control, or to integrate Java support into your existing UALs, you can use the interfaces described below.

 #define ap_JavaNoId (-1)

An "invalid" ID value for Java probe objects.

int ap_Java_RegisterProbeBean (
  ap_NameT BeanClassName,
  ap_NameT BeanName);

Register a probe bean. This will attempt to load the given class and register it with the runtime. Note that the class must be in the classpath to be found. The bean ID is returned (or ap_JavaNoId on failure).

void ap_Java_SetProbeBeanParamValue(
  int      BeanId,
  ap_NameT ParamName,
  ap_NameT ParamValue);

Register a parameter with the probe bean. This is the mechanism to pass options to the bean. Each parameter is a name and a value pair (both strings).

int ap_Java_RegisterProbeTargetList(
  ap_NameT ProbeTargetListName);

Register a probe target list. This is a name given to a collection of targets that can be attached to the list. The targets can then be registered with a probe bean so that bean will pick up any methods added. The ID returned is the list ID or ap_JavaNoId> if it is already registered.

int ap_Java_GetProbeTargetListId(
  ap_NameT ProbeTargetListName);

Get a probe target list ID if it exists (or ap_JavaNoId if it doesn't).

typedef enum
{
   ap_ParamNone,
   ap_ParamReadOnlyMode,
   ap_ParamReadWriteMode, /* not yet implemented */   
   ap_ParamUnspecified } ap_Java_ParamAccessModeT;

When instrumenting a Java method specify whether or not parameters are required.

typedef enum
{
   ap_LinesNotInstrumented,
   ap_LinesInstrumented,
   ap_LinesUnspecified } ap_Java_LinesAccessModeT;

This is the same idea for lines. Lines can be on or off or unspecified. The default is off.

int ap_Java_RegisterProbeTarget(
  int                       ProbeTargetListId,
  ap_Java_LinesAccessModeT  MustInstrumentLines,
  ap_Java_ParamAccessModeT    ParamAccessMode);

Register a probe target with a specific list. You can provide a default for the instrumentation of any such methods (lines and/or parameters) or you can set these individually for each entry. Bear in mind the "Precedence of Access Modes" described below. ap_JavaNoId may be passed as ProbeTargetListId if the list is not yet available. You may add the list later with a call to ap_Java_AddTargetListToProbeTarget().

Precedence of Access Modes

Because multiple APC or XMJ files can specify different parameter and line access modes at different levels, we have a potential conflict. For instance, we might specify that we want parameters at the class level but none at a method in that class. If there is a conflict, the "higher" parameter mode overrides the lower, and a specific parameter mode overrides ap_ParamUnspecified. For example, ap_ParamReadOnlyMode overrides ap_ParamNone, but ap_ParamNone overrides ap_ParamUnspecified.

This precedence similarly applies to ap_LinesAccessModeT, where ap_LinesInstrumented overrides ap_LinesNotInstrumented>, but either (at any level) overrides ap_LinesUnspecified.

As a result, you are not guaranteed the instrumentation you specify in a call to ap_JavaRegisterProbeTarget. In particular if you specify ap_ParamNone and another probe target has the same method and specifies ap_ParamReadOnlyMode, the instrumentation will be for parameters. Similarly, line instrumentation will always be done if any target requests it for a method if that method has line information in the class file.

void ap_Java_AddTargetListToProbeTarget(
  int         ProbeTargetListId,
  int         ProbeTargetId,
  ap_BooleanT IsRemoveList);

Adds a probe target list to a previously created (by a call to ap_Java_RegisterProbeTarget) probe target. If the parameter IsRemoveList is set to TRUE, the ProbeTargetListId will signify the list of methods to be excluded from the given probe target (ProbeTargetId).

void ap_Java_AddProbeBeanTarget (
  ap_NameT BeanTargetName,
  int      ProbeBeanId,
  int      ProbeTargetId);

Register a specific target with the probe bean. We provide the BeanTargetName so the bean can later get the list of targets.

int ap_Java_AddProbeTargetListEntry(
  int                      ProbeTargetListId,
  ap_NameT                 ProbeTargetListEntry,
  ap_Java_LinesAccessModeT InstrumentLines,
  ap_Java_ParamAccessModeT   ParamAccessMode);

Add a method to a probe target list. The name can be a fully qualified method (including class and signature), or is can be a wildcard (either '*', '*::*', '*::method' or 'class::*'. You can specify whether you want lines and/or parameters - this will override any settings that the list ID already has noting the rules given in "Precedence of Access Modes" above.

int MethodId

The Aprobe Java support assigns a unique integer to each method as it is loaded. In your Java probe it is retrieved by each probe's
getMethodId() method. The only way to get a handle on this in C is to define a ProbeTargetlist and register for a callback on each match, as shown below.

typedef void (*TargetListMethodMatchCallbackT)(
  int                      MethodId,
  int                      ProbeTargetListId,
  ap_BooleanT                IsRemoveEntry);
void ap_Java_RegisterProbeTargetListMethodMatchCallback(
  int                          ProbeTargetListId,
  TargetListMethodMatchCallbackT Callback);

Register a subprogram to be called on a Java method matching a given probe target list.

int ap_Java_AddRemoveProbeTargetListEntry(
  int   ProbeTargetListId,
  ap_NameT ProbeTargetListEntry);

Same as ap_Java_AddProbeTargetListEntry, but the entry signifies those methods that must be excluded from any probe targets that use this probe target list.

List Entry Parameters

List entry parameters are a mechanism to pass data relating to a specific entry to the bean itself. For instance, you may have a particular probe that needs specific information about what to log for this method (perhaps a particular method parameter, for example). Pass this information using a name and a value (both strings).

As target list entries match newly registered methods, their parameters will be recorded for the corresponding pairs of method and probe target ids. The same parameter name may end up being recorded multiple times for the same method- target pair.

void ap_Java_SetProbeListEntryParamValue(
  int      ListEntryRefId,
  ap_NameT ParamName,
  ap_NameT ParamValue);

Associate a parameter name/value with the specified list.

ap_NameT ap_Java_GetPrintableMethodName(int MethodId);

Get a printable name for this method ID. The name is allocated with ap_StrDup and must be freed with ap_StrFree after use.

ap_NameT ap_Java_GetMethodFileName (int MethodId);

Get the filename for the given method id. Note that this name is kept by the runtime and must not be freed.

int ap_Java_GetMethodLineNumber (int MethodId);

Return the first line number for the given method.

ap_NameT ap_Java_GetMethodParameterName(
  int MethodId,
  int ParameterNumber);

For method designated by MethodId, return the name of parameter at the ordinal position specified by ParameterNumber. Returns ap_NoName if there is no such parameter.

ap_NameT ap_Java_GetMethodParameterType(
  int MethodId,
  int ParameterNumber);

For method designated by MethodId, return the type descriptor (e.g. "I" for int) of the parameter at the ordinal position given by ParameterNumber.

Logging String Values

A macro is defined to simplify logging strings from APC:

#define </nowiki>ap_StringValue(Str) Str[0 ..(ap_Strlen(Str) 1)]

Expands to the "slice" representing the string Str in a log directive, for example:

log(ap_StringValue($argv[0]));

Note that ap_StringValue is not needed when logging a string literal, because the APC preprocessor can determine the size directly. For example:

log("Exiting main\n");

The above macro uses ap_Strlen(), rather than the standard C strlen function:

int ap_Strlen(ap_NameT)

Return the length of the string, or zero if the string is a null pointer.

In order to reduce the risk of logging "bad" strings which are garbage data and so potentially very long, a parallel set of operations is provided which bounds the amount of data logged and formatted. ap_BoundedStrlen, ap_BoundedStringValue are same as ap_Strlen, ap_StringValue above, except length of string is limited to ap_BoundedStringLimit.

int ap_BoundedStringLimit;

The maximum amount of data to be logged via ap_BoundedStringValue. The initial value is 1024.

Note that this value must be explicitly set at format time if used in a user-written format routine.

int ap_BoundedStrlen(ap_NameT String);

Return the length of the string or ap_StrlenBound, which ever is less, or 0 if String is a null pointer.

#define ap_BoundedStringValue(Str) \
  ((ap_NameT) (Str))
  [0 ..(ap_BoundedStrlen((ap_NameT) (Str))-1)]

A macro to simplify logging strings with bounded maximum length, e.g., log(ap_BoundedStringValue(argv[1]));

Note: ap_BoundedStringValue does not log a nul-terminator, and is intended for use with compiler-generated format routines. If used with a user-written log, the length must logged as well.

Traceback Support

The traceback functions and macros provide an easy mechanism for obtaining a stack trace, a list showing the current routine, the routine which called that, it's caller and so on up the stack.

The simplest mechanism to log a traceback is to use the ap_LogTraceback macro. This takes one parameter which is the maximum trace level you wish to log. There is a similar macro, ap_PrintTraceback, which prints the current stack trace to standard out. These macros can only be called from inside an action routine since they rely on getting the current location from the action routine's parameters.

If you wish to have more control over the process, you can use the ap_GetTraceback() function. Pass in an array of ap_AddressT and the number of items in that array. The ap_GetTraceback function will fill the array which the instruction addresses found in the stack, up to the maximum depth you specified. The number of addresses stored is returned. You can then log / print this buffer anyway you wish.

A couple of routines are provided to take such a buffer and log or print it. These are: ap_PrintTracebackFunc and ap_LogTracebackFunc.


int ap_GetTraceback (
    ap_LocationT Location,
    ap_AddressT *TraceBuffer,
    int          MaximumDepth);

Get the current traceback up to the maximum depth. Return the depth that was stored in the buffer.


char *ap_FormatAddress (
    char *Buffer,
    ap_AddressT Address);

Format a single address in symbolic form to a buffer provided by the caller, which is then returned. See also ap_FormatSymbol().


ap_SizeT ap_FormatAddressSize(ap_AddressT Address);

Return a minimum buffer size for ap_FormatAddress() to use.


void ap_PrintAddress (ap_AddressT Address);

Print out a single address in symbolic form, followed by a newline. See also ap_PrintSymbol().


void ap_PrintTracebackFunc (
  ap_AddressT *TraceBuffer,
    int          Depth);

Convert the addresses in the buffer into symbols and print them out.


void ap_LogTracebackFunc (
ap_AddressT *TraceBuffer,
  int          Depth);

Log the buffer to the current APD file.

 #define ap_TracebackAction(depth, action) private

Generate a stack trace and call the appropriate routine. action is one of ap_PrintTracebackFunc or ap_LogTracebackFunc. (See next two macro definitions.)

 #define ap_LogTraceback(depth) \
  ap_TracebackAction((depth), ap_LogTracebackFunc)

Log the current stack trace up to the specified depth.

 #define ap_PrintTraceback(depth) \
  ap_TracebackAction((depth), ap_PrintTracebackFunc)

Print the current stack trace up to the specified depth.

Stub Support

The ap_StubRoutine macro causes control to pass to the exit action of the current probe (if any) without executing any further instructions in the probed function. This can only be called from an on_entry action. See also "Stub Support".

#define ap_StubRoutine private

On Solaris, a structure returned by value is written to space on the stack allocated by the caller. However, if the caller is discarding the returned value by calling the function as a procedure, no space is allocated. In this case, a probe which may normally attempt to change the return value should not do so, as it will likely corrupt memory. In order to allow users to handle this problem, the following macro is provided:

#define ap_StructValueReturnExpected private

This would be used as a boolean expression in an on_exit part as follows:

probe "UpdateCoordinates()"
{
  on_entry
    ap_StubRoutine;
  on_exit
    if (ap_StructReturnValueExpected)
      $return.x = $return.y = $return.z = 0;
}

Exception Support

The following types and routines allow exceptions to be tracked by probes. When an exception occurs, a handler routine can be called to log or print the exception. There are also functions to raise exceptions.

typedef enum
{
  ap_NoException,
  ap_CppException,
  ap_WinNTVCppException,
  ap_GnatAdaException,   
  ap_WinNTSEHException,   
  ap_AixCppException,
  ap_Pa4AdaException } ap_ExceptionKindT;

typedef struct
{
  ap_ExceptionKindT  Kind;
   ap_NameT           '''ExceptionName''';
   void              *'''ExceptionData''';
 } '''ap_ExceptionT''';
 
 ap_ExceptionT ap_CurrentException (
  ap_ThreadContextPtrT  ThreadContext,
  ap_ProbeActionReasonT   Reason);

When an exit routine is called, it is possible that an exception is being propagated. This is indicated by the ap_ProbeActionReason parameter being set to ap_CppExceptionPropagated. The type ap_ExceptionKindT defines the kinds of exceptions are currently implemented. The type ap_ExceptionT defines the information concerning an exception: it's Kind, Name, and implementation-specific data associated with the exception. Note that the ExceptionData field is specific to the kind of exception propagated and is defined by the compiler in use rather than by Aprobe. The function ap_CurrentException() returns the information for the exception currently being propagated. The Kind will be ap_NoException if no exception is being propagated.

NameT ap_GetExceptionMessage(ap_ExceptionT *Exception);

Get the exception message (if any) for the given exception. The returned string must be freed by the caller. ap_NoName will be returned if there is no message for the exception. Note that it is expected that you call this within the exception handler procedure or immediately after calling ap_CurrentException(). Any other usage has undefined behavior.

Printing and Logging Exceptions

When an exception is raised, a handler can be called to automatically log and/or print the exception. The following is the definition of the handler (note that Location is the location for the routine which raised the exception):

typedef void ap_ExceptionHandlerProcT (
  ap_ExceptionT *Exception,
  ap_LocationT   Location);

There are several pre-defined routines of this type:

ap_ExceptionHandlerProcT ap_LogExceptionsProc;

Log exceptions when they occur.

ap_ExceptionHandlerProcT ap_PrintExceptionsProc;

Print exceptions when they occur.

ap_ExceptionHandlerProcT ap_LogAndPrintExceptionsProc;

Both log and print exception when they occur.

In order for a handler callback routine to be invoked, it must be registered:

void ap_RegisterExceptionHandler (   ap_ThreadContextPtrT    ThreadContext,   ap_ExceptionHandlerProcT Handler);

Register the routine to be called when an exception occurs.

To make the above easier: just put one of these macro names, followed by a semicolon, prior to the closing '}' of a "probe thread".

#define ap_LogExceptionsInThread \
  on_entry  \
    ap_RegisterExceptionHandler( \
      ap_ThreadContextPtr, ap_LogExceptionsProc)

#define ap_PrintExceptionsInThread \
  on_entry  \
    ap_RegisterExceptionHandler( \
      ap_ThreadContextPtr, ap_PrintExceptionsProc)

#define ap_LogAndPrintExceptionsInThread \
  on_entry  \
    ap_RegisterExceptionHandler( \
      ap_ThreadContextPtr, ap_LogAndPrintExceptionsProc)

#define ap_PrintAndLogExceptionsInThread   ap_LogAndPrintExceptionsInThread

Suppressing Exceptions

If you are using the IBM xlC compiler on AIX, you can prevent an exception from being propagated past the point of a probe by calling ap_SuppressException within a probe action. This is actually a macro which passes parameters from the action onto ap_SuppressExceptionFunction():

#define ap_SuppressException   \     
      ap_SuppressExceptionFunction(   \     
      ap_ThreadContextPtr,    \     
      ap_FunctionId,    \     
      ap_ProbeActionReason)

Do not allow an exception to propagate past the current probe. It can be used only on_exit, when ap_ProbeActionReason == ap_CppExceptionPropagated. For example:

probe "fred"
{
    on_exit
      if (ap_ProbeActionReason ==
        ap_CppExceptionPropagated)
          ap_SuppressException;
}

Raising Ada Exceptions

Aprobe supports exceptions for the GNAT and PowerAda compilers. The differences between them require use of a separate set of operations, described below.

Raising GNAT Exceptions

ap_BooleanT ap_RaiseGnatException(ap_NameT)

The simple form: Just raise an exception in the application module with no associated reason. This is only valid within an action (e.g., on_line() within a probe).

The following types and functions form the implementation of the ap_RaiseGnatException macro.

typedef void (*ap_RaiseExceptionProcT) (void);

When raising exceptions, we pass the address of a routine to be called at the end of processing all the actions. The data pertaining to the raised exception is available in the thread context pointer:

void ap_SetExceptionDetails(
  ap_ThreadContextPtrT    ThreadContext,
  ap_ExecutionContextPtrT ExecutionContext,
  ap_ProbeActionReasonT   ProbeActionReason,
  ap_FunctionIdT          FunctionId,
  ap_Offset               Offset,
  ap_RaiseExceptionProcT  RaiseExceptionProc,
  void                      *ExceptionData);

ap_SetExceptionDetails() is the low-level raise exception routine. It does not directly callthe routine which will raise an exception. Instead it stores the provided data in the thread context pointer and stores the function to be called in the relevant return address within the execution context pointer. The function ID and offset are needed for offset / line patches.

ap_BooleanT ap_RaiseGnatExceptionBySymbol (   
    ap_ThreadContextPtrT    ThreadContext,   
    ap_ExecutionContextPtrT ExecutionContext,   
    ap_ProbeActionReasonT   ProbeActionReason,   
    ap_FunctionIdT          FunctionId,   
    ap_OffsetT              Offset,   
    ap_SymbolIdT            SymbolId,   
    char                      *Reason);

Raise a Gnat exception from a symbol. If the Gnat exception routine is not found, the function returns false. If Reason is null a default string is provided, otherwise the reason is copied into an Ada string and passed to the raise routine. The Reason string must not be freed

ap_BooleanT ap_RaiseGnatExceptionByName (   
    ap_ThreadContextPtrT    ThreadContext,   
    ap_ExecutionContextPtrT ExecutionContext,  
    ap_ProbeActionReasonT   ProbeActionReason,   
    ap_FunctionIdT          FunctionId,   
    ap_OffsetT              Offset,   
    ap_ModuleIdT            ModuleId,  
    ap_NameT                ExceptionName,   
    ap_NameT                Filename,   
    char                      *Reason);

Similar to the above, but providing a name for the exception symbool. FALSE is returned if the symbol is not found for the Name. As before the reason string must not be freed before the exception has been propagated.

Raising PowerAda Exceptions

ap_BooleanT ap_RaisePowerAdaException(ap_NameT);

The simple form: Just raise an exception in the application module with no associated reason. This is only valid within an action (e.g., on_line() within a probe).

ap_BooleanT ap_RaisePowerAdaExceptionByName (   
    ap_ThreadContextPtrT    ThreadContext,  
    ap_ExecutionContextPtrT ExecutionContextPtr,  
    ap_ProbeActionReasonT   ProbeActionReason,  
    ap_FunctionIdT          FunctionId,   
    ap_OffsetT              Offset,   
    ap_NameT                ExceptionName,   
    ap_NameT                  Reason);

Raise a PowerAda exception in the given context, with the given name and reason string. ap_RaisePowerAdaException above is a macro which calls this in the context of the current probe.

ap_BooleanT ap_MakeRaiseExceptionReason(   
    ap_NameT File,   
    int       LineNumber);

Build a standard PowerAda exception reason string given the File and line number. This is used by the RaisePowerAdaException macro.

Time Support

Aprobe provides an interface to the underlying system clock, ap_TimeT. This is the target-independent version of time. It is represented as seconds and nanoseconds. (Note that actual time values may not be accurate to the nanosecond.) See "Time Support" for some examples.

typedef struct{
  ap_Int32 secs;
  ap_Int32 nanosecs;
} ap_TimeT;

The Current Time

ap_TimeT *ap_GetTime (ap_TimeT *Time);

Records the current clock value in the user-supplied structure and returns the pointer.

ap_TimeT ap_GetCurrentTime(void);

Equivalent to ap_GetTime(), but returns the structure by value and does not require a buffer.

ap_TimeT ap_ElapsedTime(void);

Returns the elapsed time since the program start.

Time Arithmetic

ap_TimeT ap_AddTime (ap_TimeT T1, ap_TimeT T2);

Add two times: Result = T1 T2

ap_TimeT ap_SubTime (ap_TimeT T1, ap_TimeT T2);

Subtract one time from another: Result = T1 - T2

ap_TimeT ap_MinTime (ap_TimeT T1, ap_TimeT T2);

Return the lesser of the two times.

ap_TimeT ap_MaxTime (ap_TimeT T1, ap_TimeT T2);

Return the greater of the two times.

float ap_TimeToFloat (ap_TimeT Time);
double ap_TimeToDouble (ap_TimeT Time);

Convert the time to a floating point value. The resulting floating point value is of the form secs.nanosecs.

ap_Int64 ap_TimeToInt64(ap_TimeT Time);

Convert the time to a 64-bit integer number of nanoseconds.

int ap_TimeToSeconds(ap_TimeT);

a macro to convert the time to an integer number of seconds.

Time Image

The function ap_GetTimeImage() constructs a string that represents the time value provided. A number of options are provided by two parameters which define the style and precision:

typedef enum {
  ap_NoTime,  
  ap_Minutes,   
  ap_Seconds,   
  ap_Millisec,   
  ap_Microsec,   
  ap_Nanosec }   ap_TimePrecisionT;

Defines the precision of the time value. See examples associated with ap_TimeStyleT below.

typedef enum {
  ap_NoDate,
  ap_Day,
  ap_Date,
  ap_DayDate,
  ap_DateYear,
  ap_DayDateYear,
  ap_Year,   
  ap_NoDateDelta,
  ap_YearMonthDay }
  ap_DateStyleT;

Defines the amount of information included in the output string.

#define ap_MaxTimeImageLength 35

This is the maximum length of the string returned by ap_GetTimeImage() and ap_FormatTime(). It is the length of "Sun Sep 16 01:03:52.123456789 1973\0". This is similar to that returned by ctime() in <time.h> but with up to 9 optional additional digits of precision, which is down to the nanosecond.

char *ap_GetTimeImage (
    char              *Buffer,
    ap_TimeT          *Time,
    ap_TimePrecisionT Precision,
    ap_DateStyleT     DateStyle );

Converts the time to an image string. The longest result image has form: "Sun Sep 16 01:03:52.123456789 1973\0" The width of each field is constant; the buffer supplied must be at least ap_MaxTimeImageLength bytes in length.

Examples of ap_GetTimeImage() output for different Precision:

For Precision = ap_Microsec and
Style =

ap_NoDate : "20:20:06.535118" 
ap_Day : "Fri 20:20:06.535118" 
ap_Date : "Feb 5 20:20:06.535118" 
ap_Year : "20:20:06.535118 1999" 
ap_DayDate : "Fri Feb 5 20:20:06.535118" 
ap_DateYear : "Feb 5 20:20:06.535118 1999" 
ap_DayDateYear: "Fri Feb 5 20:20:06.535118 1999"
ap_YearMonthDay: "1999-02-05 20:20:06.535118"

For Precision = ap_Minutes and
Style =

ap_NoDate : "20:20"
ap_Day : "Fri 20:20"
ap_Date : "Feb 5 20:20"
ap_Year : "20:20 1999"
ap_DayDate : "Fri Feb 5 20:20"
ap_DateYear : "Feb 5 20:20 1999"
ap_DayDateYear : "Fri Feb 5 20:20 1999"
ap_YearMonthDay: "1999-02-05 20:20"

If Precision = ap_NoTime and
Style =

ap_NoDate : "" 
ap_Day : "Fri" 
ap_Date : "Feb 5" 
ap_Year : "1999" 
ap_DayDate : "Fri Feb 5" 
ap_DateYear : "Feb 5 1999"
ap_DayDateYear: "Fri Feb 5 1999"
ap_YearMonthDay: "1999-02-05"

Note that ap_NoDateDelta is used to print the difference between two times, rather than the clock time. It is the same format as shown for ap_NoDate above, but is not adjusted for the local time zone.

char *ap_FormatTime(
    char *Buffer, 
    ap_TimeT *Time);

return a string representing the time. The full date is returned if the magnitude of the Time value indicates it's a date; otherwise just HH:MM:SS:nanoseconds is returned.

Periodic Function Invocation

As part of your probe, you can register a function to be called every so many seconds. This is especially useful when updating a display of some sort, as demonstrated in the [../examples/learn/visualize_data/README $APROBE/examples/learn/visualize_data] example. README

typedef void (*ap_PeriodicActionT) (void *);

The function to be called. This simply passes in the UserData parameter with which ap_DoPeriodically() was called.

ap_BooleanT ap_DoPeriodically(
    ap_PeriodicActionT              UserAction
    int             IntervalInSeconds,
    void              *UserData);

Specify a function UserAction to be called with UserData every IntervalInSeconds seconds. Returns TRUE if the action was succesfully registered, FALSE otherwise.

Note: ap_DoPeriodically() may be called only from the on_entry action of probe program.

Support For Thread-Safe Operations

In a multi-threaded environment, global data structures must be updated atomically, or else the update operation must lock out all other readers and writers. The following two atomic operations are used by Aprobe.


ap_BooleanT ap_CompareAndSwap (
    void *WordAddr,
    int  *OldValAddr,
    int   NewVal);

A target independent interface to a compare and swap routine. If the value pointed to by WordAddr matches the value pointed to by the OldValAddr field, NewVal is stored at WordAddr and TRUE is returned; otherwise the current value is stored in OldValAddr and FALSE is returned. This is guaranteed to be an atomic operation.


int ap_FetchAndAdd (void *WordAddr, int Value);

This function atomically increments the value at a given address and returns the original value.

Aprobe also provides several thread-safe but lock-free data structures which are well-tested as part of Aprobe itself. See "Data Structures".

Reading Raw Memory

The function ap_ReadMemory() reads bytes from the target executable (or one of it's shared libraries) into a buffer (the buffer must be created by the caller and be of a sufficient size to hold the requested length). FALSE is returned if the address / length was not valid for the loaded modules.

In general this function can be used at format time. However, the following points should be noted:

  • The data you read may be different at format time than at run (aprobe) time. In particular, only text (code) or constants are likely to be the same. And even then, if Aprobe had patched an instruction sequence, at runtime you would get the modified instructions but at format time you would get the original code.
  • The location in memory of the executable and shared libraries is different at format time from runtime. Therefore, if you have an address 0x00010100 at runtime (assume it is within the executable's text section) what does 0x00010100 mean at format time? Aprobe will use the information it logged to translate the address so that it points to the same relative location. Therefore, at format time you should only call this with addresses that were valid at runtime.


ap_BooleanT ap_ReadMemory (
  ap_AddressT  TargetAddress,
  ap_SizeT     Length,
  void         *Buffer);

Bit Manipulation

Functions are provided to insert, extract, and move a sequence of bits. References to these are generated by the apc compiler when target application data cannot be exactly represented by a C type. Users may reference these functions directly in cases where APC does not provide sufficient power (or for any other reason).

void ap_BitSet(
  void *Target,
  int  BitOffset,
  int  Value,
  int  ElementSize,   int  NumberOfElements);

Set NumberOfElements successive elements of ElementSize bits each to the value Value, starting at BitOffset bits from the address Target.

void ap_BitMove(
  void *Target,
  int  TargetBitOffset,
  void *Source,
  int  SourceBitOffset,
  int  BitMoveSize);

Move BitMoveSize number of bits from Source SourceBitOffset to Target TargetBitOffset

ap_Int32 ap_SignedBitExtract(
  void *Source,
  int  SourceBitOffset,
  int  BitSize);

return the signed value comprised of the BitSize bits starting at SourceBitOffset bits from the address Source.

ap_Uint32 ap_UnsignedBitExtract(
  void *Source,
  int  SourceBitOffset,
  int  BitSize);

return the unsigned value comprised of the BitSize bits starting at SourceBitOffset bits from the address Source.

void ap_BitValueInsert(
  int  Value,
  void *Target,
  int  TargetBitOffset,
  int  BitSize);

replace BitSize bits starting at TargetBitOffset from the address Target with the lower BitSize bits of the Value.

Signal Support

If you want to register a signal handler for your probes but want to allow it to co-exist with the application's signal handler(s), call ap_RegisterSignalHandler to register your probe's signal handler.

In order to control the order in which handlers are called, you can specify whether you want your handler called before the user action, instead of the user action, or after the user action. Note that if you request your handler to be executed after the user's action, then it might not get executed, depending on what the user's action does.

typedef enum{
  ap_CallBeforeUserAction,
  ap_CallInsteadOfUserAction,
  ap_CallAfterUserAction,
  ap_CallIfNoUserAction }  ap_PositionOfHandlerT;

A signal registered with ap_CallIfNoUserAction is executed if no other handler is registered for that signal. If a signal is registered with ap_CallInsteadOfUserAction, that handler will be executed and not any registered for the same signal with ap_CallIfNoUserAction. Multiple handlers for one signal may be registered with ap_CallIfNoUserAction. These are executed (in no defined order) after signals registered with ap_CallBeforeUserAction and before those registered with ap_CallAfterUserAction, that is, as if it was registered from the target program.

Note we require the three-argument form for completeness across platforms:

typedef void (*ap_SigHandlerT) (int, siginfo_t *, void *);
extern ap_BooleanT ap_RegisterSignalHandler (
  int                   Signal,
  ap_PositionOfHandlerT When,
  ap_SigHandlerT          Handler);

Note that probes are not automatically disabled before a user signal handler is called. It is recommended that you check the status of the current thread and disable probes as necessary. In particular, if probes are already disabled, logging data to a non re-entrant log method is not recommended as the signal could have been taken with the log method locked. In particular the default ring and persisent log methods are not re-entrant and care must be taken to avoid deadlock. A code sequence like the following might be appropriate.

static void MyHandler (
  int Signal,
  siginfo_t *SigInfo,
  void *ucp)
{
  ap_ThreadContextPtrT  ap_ThreadContextPtr;
  ap_ThreadContextPtr = ap_GetThreadContextPtr ();
  if (ap_ProbesAreDisabled (ap_ThreadContextPtr))
  {
    printf ("Probes are disabled in MyHandler\n");
    return;
  }
  // Disable probes
  ap_IncrementDisableProbesCount (ap_ThreadContextPtr);

  // YOUR HANDLER ACTIONS GO HERE

  // Enable probes
  ap_DecrementDisableProbesCount (ap_ThreadContextPtr);
}

Tracing Support

Aprobe provides macros which automatically log and format entry and exit information for a function. These are used by the trace.ual predefined probe and by the probes generated by apcgen.

 #define ap_LogSubprogramEntry

This macro must appear in the on_entry action of a function probe. It logs the time, function location, and caller location, and maintains the call nesting level, all of which are formatted in a standard way. The on_exit part of the same function probe must be provided and must contain ap_LogSubprogramExit;.

 #define ap_LogSubprogramExit

This macro must appear in the on_exit action of a function probe, and must match a corresponding ap_LogSubprogramEntry.

 #define ap_LogLine

This macro may be used in an on_line action of a function probe to record the line number. It should be used in conjunction with the above macros to maintain correct nesting.

Memory Allocation Functions

These routines can be called by your code to perform memory allocation / deallocation instead of using malloc / free.

void *ap_Malloc (ap_SizeT);

void  ap_Free (void *);

void *ap_Realloc (void *, ap_SizeT);

void *ap_Calloc (
  ap_Uint32 NumberOfElements,
  ap_Uint32 ElementSize);

These functions provide a "lock-free heap" intended primarily for use within the Aprobe runtime. The memory is pre-allocated in a large chunk at startup (and later if necessary) using malloc(), and calls to ap_Malloc(), etc., return blocks from this in an efficient, thread-safe manner.

Warning: it is critical that these functions not be "mixed" with the system malloc() and free(), or data corruption will certainly occur.

The ap_StrDup() function finds a string with the same name, or makes a copy if necessary in the memory allocated by an ap_Malloc() call. The result returned by ap_StrDup() should only be deallocated with ap_StrFree() as it may be shared by other callers of ap_StrDup().

ap_NameT      ap_StrDup (ap_NameT);

void      ap_StrFree (ap_NameT);

Operations which return strings allocated using these functions are described under "String Utilities" below.

General Utilities

The Aprobe runtime includes a fairly substantial programming support library that is used to implement both the Aprobe features themselves and the predefined probes that OC Systems has written. Described below are just a few of the many that are defined in aprobe.h. You are encouraged to look there before implementing any data structure, file or string manipulation function.

String Utilities

ap_NameT ap_CopyStringFragment (
  ap_NameT String,
  ap_Uint32    Length);

copies part of a string using ap_StrDup(), and so must be freed using ap_StrFree(). This is useful for manipulating strings which are not null-terminated, such as those in Ada data structures.

ap_NameT ap_CatenateStrings (ap_NameT FirstString, ...);

catenates a null-terminated list of strings together. The last argument must always be zero!. The new string must be freed using ap_StrFree().

ap_NameT ap_GenerateStringLiteralValue(ap_NameT Value);

This is a very useful function which puts the appropriate backslashes, etc in a string so it can be put in quotes. User must call ap_StrFree() for result.

ap_SizeT ap_DetermineVprintfLen(ap_NameT Format, va_list Ap);

Returns the length (not including the nul-terminator) of the string that would be printed by vprintf(2) with the given args. This is useful when implementing a messaging or logging operation with a "...)" prototype. For example:

void MyError(ap_SeverityT Sev, ap_NameT Message, ...)
{
if (!ap_IsNoName(Message))
{
int MsgLen;
char *Msg;
va_list Ap;

va_start(Ap, Message);
MsgLen = ap_DetermineVprintfLen(Message, Ap);
Msg = (char *)alloca(MsgLen 1);
vsprintf(Msg, Message, Ap);
va_end(Ap);
ap_Error(Sev, "%s: %s", MyProbeName, Msg);
}
}
int ap_TokenizeLine (char *LineBuffer, int BufferSize);

Break the buffer into white-space-delimited tokens. The number of tokens is returned, and the buffer is broken up into null-terminated strings.

char *ap_GetNextToken (ap_NameT Tokens);

Given a set of contiguous null-terminated strings, get the next one. The caller must know when the last token is reached. This can be used in combination with ap_TokenizeLine as follows:

    // make a local, writeable copy of the Line
    int LineSize = strlen(Line) 1;
    char *Tokens =
      (char *)(strcpy(alloca(LineSize), Line);

    // tokenize the line, getting the number of tokens
    int NTokens = ap_TokenizeLine(Tokens, LineSize);

    // process each token
    for (i = 0; i < NTokens; i  )
    {
      ProcessToken(Tokens);
      Tokens = ap_GetNextToken(Tokens);
    }

ap_BooleanT ap_DoFileAndSymbolWildcardsMatch (
  ap_NameT WildcardFileName,
  ap_NameT FileName,
  ap_NameT WildcardSymbolName,
  ap_NameT SymbolName).

Given two wildcard strings, one for the filename and one for the SymbolName, return TRUE if they match a given the given exact file and symbol name strings.

File Path Utilities

Routines are provided to return the directory and basename portions of a filename.

ap_NameT ap_Basename (ap_NameT Path);

Return the portion of the given path that corresponds to the filename. A new string is not allocated so the returned string should not be freed by the caller.

ap_NameT ap_Dirname (ap_NameT Path);

Return the portion of the path that corresponds to the directory. If the string is null or there is no directory in the path, the string "." is returned. All returned strings are allocated by ap_Dirname() using the ap_StrDup() function and must be freed by the caller using ap_StrFree().

ap_BooleanT ap_FileExists(ap_NameT Name);

Return TRUE if the named file exists.

ap_NameT ap_NormalizeFileName(ap_NameT Name);

Normalize the filename so there are no /../ or /./ in it. The string returned is allocated and must be freed by the caller using ap_StrFree().

ap_NameT ap_ExpandFileName(ap_NameT Name);

Expand the filename so that it is fully qualified and apply ap_NormalizeFilename(). The string returned is allocated and must be freed by the caller using ap_StrFree().

Data Structures

Most of Aprobe's internal data structures are implemented using the following data structures. You can make use of these in your probes as well. Only the type names are given here; see aprobe.h for the full specifications:

typedef struct ap_RbTreeT *ap_RbTreePtrT;

a red-black tree, including iterators and user-replaceable storage management.

typedef struct ap_HashTableT  *ap_HashTablePtrT

a fast, thread-safe, lock-free hash table, including an interator and a predefined hash function for strings.

typedef struct ap_VectorT *ap_VectorPtrT;

a fast, thread-safe, lock-free variable-length vector implementation.


Next Previous Top Contents Index

Copyright 2006-2017 OC Systems, Inc.