Table of Contents
Application Response Measurement
Basic Tasks for Instrumenting an Application
The Software Developer's Kit (SDK)
Overview of the ARM API Function Calls
Adding ARM Function Calls to an Application
Additional Data Passed in the ARM Function Calls
Appendix: Measurement Agent Information
Application Response Measurement
2.0 API Guide
For Your Information
This Guide is intended for the application developers who wants to know how to instrument an application for transaction monitoring using the standard Application Response Measurement (ARM) API function calls.
The 2.0 version of the ARM Software Developer's Kit has been developed with the help of the ARM Working Group of the Computer Measurement Group (CMG).
The 2.0 ARM SDK, including this documentation, is available on CD and from the following CMG web site:
http://www.cmg.org/regions/cmgarmw
This web site also contains information on performance measurement agents that use the data generated by the ARM API function calls and the latest information regarding future updates or changes to the API.
A public discussion list for ARM, cmgarm is now available at:
cmgarm@cmg.org
To subscribe, send the following to majordomo@cmg.org
subscribe cmgarm
Application Response Measurement
The applications that are used to run businesses have changed dramatically over the past few years. In the early 1980s, large applications generally executed on large computers, and were accessed from "dumb" terminals. Non-networked applications executing on personal computers were just beginning to be widely used. Since then, these two application models have moved steadily towards each other, fusing together to form distributed (networked) applications.
The most common programming model for distributed applications is the client/server model. In a client/server application, the application is split into two or more parts. One part is the user or "client" part, and this part generally executes on a personal computer or workstation. The "server" parts execute on computers that provide functions for the client part, that is, they serve the client application. The client and server can run on the same system, but generally they are on different systems. The client part of an application may invoke one or more functions on one or more servers, and it may do a significant amount of processing itself – combining, manipulating, or analyzing the data provided by the servers.
An example of a client/server application might be processing a sales order by retrieving inventory information from one database, sales information from another database, and pricing information from a third. The client part of the application determines if there is sufficient inventory to accept the order, calculates the price based on current market conditions, factors in price discounts for this particular customer, and then invokes more server functions to complete processing of the order.
By contrast, host-centric applications contain all the application logic in one computer system, and users connect through "dumb" terminals to use the application. Examples of the protocols used by these applications are 3270, Telnet, and X-Windows. The response time as seen by a user for a transaction can generally be broken down into two components: the time to process the transaction on the host, and the time for the input message and the output response. Processing time at the terminal is usually trivial.
A monitoring product running at the host is able to measure the service levels of host-centric applications. The monitor observes the input request message that starts the transaction, and then observes the outbound response back to the terminal. The difference between the two times is the amount of time to process the transaction on the host. The monitor generally also measures the time for the outbound response to be sent to the terminal and an acknowledgment to be received, using this as an approximation of the transit time. The combination of the host and transit times is an approximation of the service level seen by the user.
Monitoring the performance and the availability of distributed applications has not proven easy to do. Some of the fundamental assumptions that the host-centric methods depend on do not hold true. Some examples:
In spite of these difficulties, the need to monitor distributed applications has never been greater. They are increasingly being used in mission-critical roles. An approach that solves the problems listed above is to let the application itself participate in the process. A developer knows unambiguously when transactions begin and end, both those that are visible to the user, and the component transactions that invoke transactions on remote servers.
With the Application Response Measurement (ARM) API, a developer can easily mark sections of an application to define business transactions. By invoking ARM API function calls at the beginning and end of each transaction, you can enable your application to be monitored by any of the measurement agents that use data generated by the ARM API. Programs executing on client or server systems can be instrumented.
By instrumenting your application to call the ARM API, you enable your application to be managed by any of the measurement agents that implement ARM. The advantage of this approach is that your application customers can choose the measurement agent that best meets their needs without your application needing to change.
System administrators will be able to answer some key questions such as:
What's New in Version 2.0 of the ARM API
Several additions to the ARM API improve the ways your application can be managed.
Note that version 2.0 of the ARM API is backward compatible with version 1.0. Applications instrumented to the ARM 1.0 API can continue to function correctly with agents that implement the additional features of the 2.0 API. ARM 2.0 instrumented applications will function correctly with agents that implement the features of the 1.0 API.
Basic Tasks for Instrumenting an Application
There are three basic tasks involved in instrumenting an application with the ARM API.
- Start with transactions that are visible to users or that represent major business operations. These are the building blocks for service level agreements, for workload monitoring, and for early problem detection.
- Next, focus on transactions that are dependent on external services, such as a database operation, a Remote Procedure Call (RPC), or a remote queue operation. These generally are components of a user/business transaction. Knowing how these types of transactions are performing can be invaluable when analyzing problems, tuning applications, and reconfiguring systems and networks.
The Application Response Measurement API is designed to instrument a unit of work, such as a business transaction, that is performance sensitive. These transactions should be something that needs to be measured, monitored, and for which corrective action can be taken if the performance is determined to be too slow.
This API is not designed to be a programmer profiling tool. The measurement agents using data generated by this API are designed to give application/system managers data to understand how their environment is performing, and whether all services are available.
For information on measurement agents that do transaction monitoring, refer to the web site mentioned earlier under "For Your Information". Links may be found on this site to commercially available measurement agent solutions.
Some questions you may want to ask yourself when instrumenting a transaction are:
The Software Developer's Kit (SDK)
This ARM SDK contains everything you need to prepare your application for transaction monitoring. It comes with a default no-operation (NULL) shared library that contains all the function calls you will need and a header file. The NULL library allows developers to instrument and run their applications without having one of the measurement agents installed.
Additionally, the source used to create the NULL library is part of the SDK. This is provided so a shared library can be created for applications that exist on platforms not currently supported by the measurement technologies. The SDK contains NULL libraries compiled for UNIX systems (HP-UX, IBM AIX, NCR MP-RAS, and Sun Solaris) and PC based systems (OS/2, Windows NT, and Windows95). The kit installs the correct library for the system.
A C language header file is supplied for applications written in either C or C++.
The source code and header file for a logging agent is supplied for use in testing your instrumentation.
Sample programs for C/C++ are provided as examples of how to instrument applications. Examples for other programming languages from the ARM 1.0 SDK are also available on the CD and the web site.
NOTE: The arm.lst file on the CD-ROM contains a detailed listing of all the files on the CD-ROM.
The ARM Shared Library (libarm)
The library specified here is a NULL shared library provided to resolve externals in the code. If you are working with a specific vendor’s performance measurement agent you may want to use the libarm library supplied for that agent instead of the NULL library. The agent-specific library will return errors that may be helpful during development, whereas the NULL library will always return a non-error condition (0).
After installation libarm.* shared libraries reside in the directory where the system libraries are installed. For example:
HP-UX 10.x | /usr/lib/libarm.sl |
IBM AIX | /usr/lib/libarm.a |
Sun Solaris | /usr/lib/libarm.so |
NCR MP-RAS | /usr/lib/libarm.so |
Windows NT | $windir$\SYSTEM32\LIBARM32.DLL |
Windows95 | $windir$\SYSTEM32\LIBARM32.DLL |
OS/2 (32-bit) | $os2dir$\DLL\LIBARM.DLL |
It is recommended that the library be used from the standard location. This is so applications can locate the library in a standard location and be able to take advantage of a measurement agent once it is installed on the system.
The source code for a logging agent, logagent.c, has been included for use in testing your instrumentation. The path is:
<install directory>/lib/logagent/logagent.c. on UNIX systems
<install directory>/ARM_SDK/LIB/logagent/logagent.c on PC systems
Unlike the NULL libraries, it is only in source format so it needs to be compiled (see "Using the Logging Agent" page 13 for more information on this).
A C language header file, arm.h, is supplied for applications written in either C or C++. If you are using a language other than C or C++, the data structures and external references need to be translated to the language you are using.
Note: Not all hardware systems or compilers provide native support for 64-bit integers – nor is there yet a standard type declaration for them. For these reasons the distributed version of the arm.h header file does not assume native support for 64 bit integers. However, the symbol "INT64" can be defined near the front of the file to customize the header for compilers and systems with 64 bit integer support.
This section gives you the information you need to begin instrumenting your application with the ARM API function calls.
To get started, you need to install the ARM SDK files on your system. The installation process installs the appropriate NULL shared library, the header files, the shared library source code, logging agent source, documentation files and sample program files for your system.
The installation utility prompts you for a directory to install the ARM source files.
NOTE: The NULL libraries for ARM 1.0 and ARM 2.0 are interchangeable, so a failure to install will have no impact. You should contact your measurement agent vendor if you need to update your agent’s shared library to ARM 2.0.
If a libarm.* shared library exists in the default directory, the install utility will not install the library. This is so the installation will not overlay an installation of one of the measurement agent’s libraries. Install will not copy the library to the default (/usr/lib) directory if the directory is not writable by the user.
For OS/2, Windows NT, or Windows95 systems
Where <drive letter> is the letter of the drive where you want to install the ARM SDK and <install directory> is the directory path for the location of where you want to install the ARM SDK. The install utility will put the files into a directory called ARM_SDK under the <install directory> specified.
The logging agent is provided for use in testing your instrumentation. It provides more information than the NULL library that only returns zeros but it does not function as a measurement agent.
The logging agent is provided in source format only, so it must be compiled. The logging agent source code file, logagent.c, can be included and compiled with an application implemented in C or it can be compiled into a library object and linked to an application.
Statically link with the logging agent and then run your application. Programmatic calls to the ARM API by the application result in the creation of a text file log (logfile by default) that contains a time-stamped history of the calls and the parameter values associated with those calls. See the "Testing Your Instrumentation" section page 20 for a sample output file and more information on using the logging agent.
Overview of the ARM API Function Calls
The ARM API is made up of a set of function calls that are contained in a shared library. All the performance measurement agents that support the ARM API provide their own implementation of the shared library. When you insert the ARM API function calls in your application, it can be monitored by the agents that implement the shared library. The advantage of this approach is that your application customers can choose any measurement agent that best meets their needs without your application needing to change.
arm_init | During the initialization of your application, call arm_init which names your application and optionally the users, and initializes the ARM environment for your application. A unique identifier is returned that must be passed to arm_getid. |
arm_getid | Use arm_getid to name each transaction class you use in your application. This is often done during the initialization of your application. A transaction class is a description of a unit of work, such as "Check Account Balance". In each program, each transaction class may be executed once or many times. arm_getid returns a unique identifier that must be passed to arm_start. |
arm_start | Each time a transaction class is executed, this is a transaction instance. arm_start signals the start of execution of a transaction instance and returns a unique handle to be passed to arm_update and arm_stop. |
arm_update | This is an optional function call that can be made any number of times after arm_start and before arm_stop. arm_update gives information about the transaction instance, such as a "heartbeat" after a group of records has been processed. |
arm_stop | arm_stop signals the end of the transaction instance. |
arm_end | At termination of the application call arm_end which cleans up the ARM environment for your application. There should be no problem if this call is not made, but memory may be wasted because it is allocated by the agent even though it is no longer needed. |
Adding ARM Function Calls to an Application
The following steps show how to add ARM API function calls to an application. Also shown is a very simple application that has been instrumented with the libarm calls. Each numbered step below (1-4) is highlighted in the source code for the sample application that follows.
When distributing your application, the NULL shared library must be included in your installation package. By doing this you will insure that your application will load and execute correctly, even if no measurement agent is installed. If the libarm.* file already exists on the system where your application is being installed, do not overwrite the library. The library that exists may be the NULL library or it could be one of the measurement agent's libraries.
The API calls use the C calling conventions for UNIX systems, the PASCAL calling conventions for OS/2 and the _std calling conventions for Windows NT and Windows95.
/*****************************************************************************/ /* sample.c */ /*****************************************************************************/
#include <stdio.h>
(1) #include "arm.h"
int32 appl_id = -1; /* Unique indentifer for the application */
int32 tran_id = -1; /* Unique identifier for the transaction */
void init()
{
(2) appl_id = arm_init("ARM sample program", /* application name */
"*", /* use default user */
0,0,0);
if (appl_id < 0)
printf("ARM sample program not registered.\n");
(3) tran_id = arm_getid(appl_id, /* application id from arm_init */
"Sample_transaction", /* transaction name */
"First Transaction in Sample program",
0,0,0);
if (tran_id < 0)
printf("Sample_transaction is not registered.\n");
} /* init */
void transaction()
{
int32 tran_handle;
(4) tran_handle = arm_start(tran_id, /* transaction id from arm_getid */
0,0,0);
/*********************************************/ /* Perform actual transaction processing here*/ /*********************************************/
sleep(1);
(4) arm_stop(tran_handle, /* transaction handle from arm_start */
ARM_GOOD, /* successful completion define = 0 */
0,0,0);
return;
} /* transaction */
main()
{
int continue_processing = 1;
init();
while (continue_processing)
{
transaction();
}
(2) arm_end(appl_id, /* application id from arm_init */
0,0,0);
return(0);
}
Definition of Data Type Terminology
The API calls use the following terminology to define each of the parameters:
The standard API calls use the following terminology to define each of the parameters:
int32 | A signed 32-bit integer. |
char* | A 32-bit pointer to a character string or data structure. Strings must be NULL terminated unless specified otherwise. Strings are expected to be displayed, put in reports, etc., so choose appropriate characters. |
The more advanced functions in the API use the following terminology to define each of the parameters:
int64 | A signed 64-bit integer. |
unsigned32 | An unsigned 32-bit integer |
unsigned64 | An unsigned 64-bit integer |
bit8 | A byte containing 8 single-bit flags. In this document, when a bit8 is represented as eight flags using the notation abcdefgh, a is the most significant bit, and h is the least significant bit. |
unsigned16 | An unsigned 16-bit integer |
unsigned8 | An unsigned 8-bit integer. |
These formats are in the native format of the hardware platform. This accommodates the difference between "Big-Endian" and "Little-Endian" systems, that is, the difference between hardware architectures in which the most significant bit position is on the left versus the right.
The following tasks are recommended for testing your instrumentation after you have included the ARM API calls in your program.
Search the log for error messages (identified by "ERROR" in the text) and informative messages (identified by "INFO" in the text) after your application has run for a considerable period of time in a simulated production environment. Upon successful completion of this test, you should be confident that your ARM API calls are correct. A sample log is provided on the next page.
7:47:39.sss: arm_init: Application <Appl_0> User <User_0> = Appl_id <1>
17:47:39.sss: arm_getid: Application <Appl_0> User <User_0> Transaction <Tran_0> Detail <This is transaction type 0>
17:47:39.sss: arm_getid: Application <Appl_0> User <User_0> Transaction <Tran_0> = Tran_id <1>
17:47:39.sss: arm_getid: Application <Appl_0> User <User_0> Transaction <Tran_0> Metric Field <1> Type <1> Name <This is a Counter32 user metric >
17:47:39.sss: arm_start: Application <Appl_0> User <User_0> Transaction <Tran_0> = Start_handle <1>
17:47:39.sss: arm_start: Application <Appl_0> User <User_0> Transaction <Tran_0> Start_handle <1> Metric < This is a Counter32 user metric > : <0>
17:47:40.sss: arm_update: Application <Appl_0> User <User_0> Transaction <Tran_0> Start_handle <1> Metric < This is a Counter32 user metric > : <2>
17:47:41.sss: arm_stop: Application <Appl_0> User <User_0> Transaction <Tran_0> Start_handle <1> Status <0>
17:47:41.sss: arm_stop: Application <Appl_0> User <User_0> Transaction <Tran_0> Start_handle <1> Metric < This is a Counter32 user metric > : <4>
17:47:41.sss: arm_end: Application <Appl_0> User <User_0> appl_id <1>
Use arm_init to define the application or a unique instance of the application and user. You must call arm_init before any other ARM API calls. It is often called when an application initializes. The return code is an application/user identifier that is input as a parameter on the arm_getid to associate transactions with the application.
Each application needs to be identified by a unique name. It is your responsibility to choose a name that is meaningful, and that won't likely duplicate the names other developers will choose for their applications. Suggestions for names would be the product name and version number or a project name.
There can be any number of application instances executing simultaneously that use the same application name, or the same application and user names. A measurement agent may assign a unique application identifier to each application instance, or it may assign an identifier that is shared across identically named instances.
Syntax:
appl_id=arm_init(appl_name,appl_user_id,flags,data,data_size)
Parameters:
appl_name (char*) | The name used to identify the application. The maximum length is 128 bytes including the NULL string terminator. |
appl_user_id (char*) | The name of the application user. On UNIX and Windows NT you can set this value to "*" to indicate the login user ID of the person running the application. The maximum length is 128 bytes including the NULL string terminator. If you do not provide a value for this parameter, you must specify the NULL value (0). |
flags (int32)=0 | Reserved for future use. It must be set to zero. |
data (char*)=0 | Reserved for future use. A NULL value (0) must be used. |
data_size (int32)=0 | Reserved for future use. It must be set to zero. |
Return Code:
appl_id (int32) | A unique value to reference an application/user identifier. This id must be passed to the arm_getid call. |
Example:
my_appl_id = arm_init ("Parts Inventory Manager 1.1",/* appl name */
"*", /* user id */
0, 0, 0); /* reserved for future use */
Error Handling:
If the value returned in appl_id is less than zero, an error occurred in communicating with the measurement agent. The value returned on an error can be passed to arm_getid which will cause arm_getid to function as a NULL operation. The error should be logged so corrective action can be taken.
The arm_getid function call is used to assign a unique identifier to a transaction class, and optionally to describe the format of additional data passed on arm_start, arm_update, and arm_stop calls. This is often done during the initialization of your application. The identifier returned by arm_init is passed as a parameter in arm_start calls to identify which class of transaction is starting.
A transaction class is a description of a unit of work, such as "Check Account Balance". Any number of transaction classes can be defined within each application. The transaction class name should help a person understand what function the transaction performs. The call to arm_getid need be made only once for each transaction class each time the application is started. A call to arm_getid can be made with the same information as a previous call, in which case the transaction identifier (tran_id) that is returned will be the same as the previous calls. Four types of information are tested to see if the information is the same. If any of these are different, a different tran_id will be returned.
Any number of transaction classes can be defined within each application. In each application, each transaction class may be executed any number of times. Each time a transaction class is executed (via arm_start), it is called a transaction instance. There can be any number of instances of each transaction class executing simultaneously.
Syntax:
tran_id=arm_getid(appl_id,tran_name,tran_detail,flags,data,data_size)
Parameters:
appl_id (int32) | The unique reference to an application/user identifier returned from the arm_init call. If the appl_id is less than zero, this arm_getid call will be treated as a NULL operation, and a negative tran_id returned. |
tran_name (char*) | The unique name of the transaction class. It is defined for each transaction class by the application developer. It must be unique within the application (for each arm_init call). The maximum length is 128 bytes including the NULL string terminator. |
tran_detail (char*) | Transaction detail allows a developer to provide additional information about a transaction class. It is a free-form text area that is set once for each appl_id/tran_name pair. If the contents of the field change on later calls using the same appl_id/tran_name pair, the new contents are ignored. The maximum length is 128 bytes including the NULL string terminator. If no tran_detail is associated with this transaction, you must specify the NULL value (0). |
flags (int32)=0 | Reserved for future use. It must be set to zero. |
data (char*) | A pointer to a buffer that describes the format of additional data that can be passed on arm_start, arm_update, and arm_stop calls. If no additional data is passed on these calls, this parameter must be set to zero (0). See the section "Format of Data Buffer in arm_getid" on page 46 for the detailed buffer format. |
data_size (int32) | The length in bytes of the buffer pointed to by data. If data is set to zero (0), data_size must also be set to zero. |
Return Code:
tran_id (int32) | The unique identifier assigned for this transaction class. This id needs to be passed on arm_start calls. |
Example:
my_tran_id = arm_getid (my_appl_id, /* application name */
"Part Number Query", /* transaction name */
"Call to Server XYZ", /* transaction details */
0, /* reserved for future use */
my_buffer_ptr, /* metrics data/metrics meta-data */
my_buffer_length); /* length of data buffer */
Error Handling:
If the value returned in tran_id is less than zero, an error occurred in communicating with the measurement agent. The most likely cause is passing an invalid value for appl_id. The value returned on an error can be passed to arm_start which will cause arm_start to function as a NULL operation. The error should be logged so corrective action can be taken.
Use arm_start to mark the beginning of execution of a transaction. Each time a transaction executes, it is called a transaction instance. You must call arm_start in your application at the beginning of each transaction instance you want monitored.
Additional information about the transaction can be provided in the optional data buffer. If no additional information is provided, pass a null pointer. This information can be provided on any or all of the arm_start, arm_update, and arm_stop calls, except correlation information which is passed only on arm_start. See the "Advanced Topics" section for details on how to pass this information.
Syntax:
start_handle=arm_start(tran_id,flags,data,data_size)
Parameters:
tran_id (int32) | The unique identifier assigned to the transaction class. This is the id generated by arm_getid. If the tran_id is less than zero, this arm_start call will be treated as a NULL operation, and a negative start_handle returned. |
flags (int32)=0 | Reserved for future use. It must be set to zero. |
data (char*) | A pointer to a buffer with additional data that can optionally be passed. If no additional data is passed, this parameter must be set to zero (0) See the section "Format of Data Buffer in arm_start, arm_update, and arm_stop" on page 50 for the detailed buffer format. |
data_size(int32) | The length in bytes of the buffer pointed to by the data parameter. If data is set to zero (0), data_size must also be set to zero. |
Return Code:
start_handle (int32) | The unique transaction handle assigned to this instance of a transaction. This handle must be passed on arm_stop and any arm_update calls. |
Example:
my_handle = arm_start (my_tran_id, /* transaction handle */
0, /* reserved for future use */
my_buffer_ptr, /* metrics data/correlator */
my_buffer_length); /*length of data buffer */
Error Handling:
If the value returned in start_handle is less than zero, an error occurred in communicating with the measurement agent. The most likely cause is passing an invalid value for tran_id. The value returned on an error can be passed to arm_update and arm_stop calls, which will cause these calls to function as NULL operations. The error should be logged so corrective action can be taken.
Use arm_update for the following purposes. This is an optional call.
arm_update is also useful for updating any of the metric or string variables passed in the buffer pointed to by the data parameter (as defined in arm_getid). This could be used to show not only that the transaction is progressing, but also how far it has progressed. For example, every time another 1000 records are processed, an arm_update call could be made with an updated count in the buffer.
Measurement agents are not required to do anything with the information in this call.
Syntax:
error_status=arm_update(start_handle,flags,data,data_size)
Parameters:
start_handle (int32) | The unique handle from the arm_start call that marked the start of this transaction instance. The start_handle must be passed in each arm_update call. Many transaction instances may be executing at the same time from this and other applications, so this handle is essential to identify which transaction instance is being updated. If start_handle is less than zero, this arm_update call will be treated as a NULL operation, and a negative error_status returned. | |
flags (int32)=0 | Reserved for future use. It must be set to zero. | |
data (char*) | A pointer to a buffer with
additional data that can optionally be passed. If no additional data is
passed, this parameter should be set to zero (0).
There are two possible buffer formats:
See the section "Format of Data Buffer in arm_start, arm_update, and arm_stop" on page 50 for the detailed buffer formats. |
|
data_size (int32) | The length in bytes of the buffer pointed to by data. If data is set to zero (0), data_size should also be set to zero. |
Return Code:
error_status (int32) | Contains a zero if successful and a negative value if an error occurred. |
Example:
status = arm_update (my_handle, /* transaction handle */
0, /* reserved for future use */
my_buffer_ptr, /* data description */
my_buffer_length); /* length of data description */
Error Handling:
If the value returned in error_status is less than zero, an error occurred in communicating with the measurement agent. The most likely cause is passing an invalid value for start_handle. The error should be logged so corrective action can be taken.
Use arm_stop to mark the end of a transaction instance that was started with arm_start. Call arm_stop from your application program just after each transaction instance ends.
In addition to signaling the end of the transaction instance, which allows a measurement agent to calculate the elapsed time since the arm_start, additional information about the transaction can be provided in the optional data buffer. This information can be provided on any or all of the arm_start, arm_update, and arm_stop calls.
Syntax:
error_status=arm_stop(start_handle,tran_status,flags,data,data_size)
Parameters:
start_handle (int32) | The unique handle from the arm_start call that marked the start of this transaction instance. start_handle, must be passed in each arm_stop call. Many transaction instances may be executing at the same time from this and other applications, so this handle is essential for the measurement agent to use to identify which transaction instance is stopping. If start_handle is less than zero, this arm_stop call will be treated as a NULL operation, and a negative error_status returned. |
tran_status (int32) | The completion code of the transaction,
as determined by the application.
0 = Transaction successful (defined as ARM_GOOD in arm.h). Use this value when the operation completed normally and as expected. 1 = Transaction aborted (defined as ARM_ABORT in arm.h). Use this value when there was a fundamental failure in the system. For example, a timeout from a communications protocol stack, or an error when doing a database operation. 2 = Transaction failed (defined as ARM_FAILED in arm.h). Use this value in applications where the transaction worked properly, but no result was generated. For example, when makingtracking an airline reservation, a server indicates no seats arecall comes in and the travel agent sees available on the requested flight. Since no reservation was made, recordthe transaction wasn't successful; but since the reservation system is operating correctly, it isn't an aborted transaction either. In this case, you might want to record the transaction as a failed transaction. |
flags (int32)=0 | Reserved for future use. It must be set to zero. |
data (char*) | A pointer to a buffer with additional
data that can optionally be passed. If no additional data is passed,
this parameter should be set to zero (0). The format is identical to the
arm_start
call, except the Correlator field is not used in the arm_stop
call.
See the section "Format of Data Buffer in arm_start, arm_update, and arm_stop" on page 50 for the detailed buffer format. |
data_size (int32) | The length in bytes of the buffer pointed to by the data parameter. If data is set to zero (0), data_size should also be set to zero. |
Return Code:
error_status (int32) | Contains a zero if successful and a negative value if an error occurred. |
Example:
status = arm_stop (my_handle, /* transaction handle */
ARM_GOOD, /* transaction status */
0, /* reserved for future use */
buffer_ptr, /* data description */
buffer_length); /* length of data description*/
Error Handling:
If the value returned in error_status is less than zero, an error occurred in communicating with the measurement agent. The most likely cause is passing an invalid value for start_handle. The error should be logged so corrective action can be taken.
Use arm_end when you are finished initiating new activity using the ARM API. It is typically called when an application/user instance is terminating. Each arm_end is paired with one arm_init to mark the end of an application.
An arm_end is a signal from the application that it does not intend to issue any more arm_getid calls using this appl_id, or any arm_start calls using any tran_id defined using this appl_id. After arm_end, the measurement agent may ignore any arm_getid or arm_start calls. It is acceptable to call arm_update or arm_stop for any incomplete transaction instances started with arm_start.
Syntax:
error_status=arm_end(appl_id,flags,data,data_size)
Parameters:
appl_id (int32) | A unique reference to an application/user identifier returned from the arm_init call. If appl_id is less than zero, this arm_end call will be treated as a NULL operation, and a negative error_status returned. |
flags (int32)=0 | Reserved for future use. It must be set to zero. |
data (char*)=0 | Reserved for future use. A NULL pointer (0) must be used. |
data_size (int32)=0 | Reserved for future use. It must be set to zero. |
Return Code:
error_status | Contains a zero if successful and a negative value if an error occurred. |
Example:
status = arm_end (my_appl_id, /* transaction handle */
0,0,0); /* reserved for future use */
Error Handling:
If the value returned in error_status is less than zero, an error occurred in communicating with the measurement agent. The most likely cause is passing an invalid value for appl_id. The error should be logged so corrective action can be taken.
The following topics provide information on more advanced implementations using the ARM 2.0 API.
Additional Data Passed in the ARM Function Calls
The following two types of additional data can now be provided via the ARM 2.0 API.
You can indicate that a transaction is a component of another transaction. You can do transaction correlation within one system or across multiple systems. This permits a better understanding of the overall transaction, how much time each part of the transaction is taking, and where problems are occurring.
Application-defined metrics provide additional information about the transaction, such as the number of bytes or records being processed, or about the state of the application at the moment that the transaction is being processed, such as the length of a work queue. This information is useful to better understand response times, and how the application can be tuned to perform better.
Many client/server transactions consist of one transaction visible to the user, and any number of nested component transactions that are invoked by the one visible transaction. These component transactions are the children of the parent transaction (or the child of another child component transaction). It's very useful to know how much each component transaction contributes to the total response time of the visible transaction. Similarly, a failure in one of the component transactions will often lead to a failure in the visible transaction, and this information is also very useful.
There are two facilities that the application developer can use to provide this information to measurement agents that implement the ARM 2.0 API.
If the correlation application collects all the data about these transactions, it can put together the total picture, knowing that T1 is the parent of T2 (via C1), and T2 is the parent of T3 (via C2). The parent/child relationship could be from a client to a server, or within one program.
An application using the ARM API need not be concerned with the format of the correlators. Measurement agents generate correlators.
Changes Needed in the Applications for Transaction Correlation
Each application responsible for a component of the overall transaction (client and server) will require some modifications. Applications have three responsibilities:
To enable a correlation application to analyze the correlators coming from different systems, measurement agents follow conventions when creating correlators. Included within the correlator is information identifying the system, the transaction class (from arm_getid), the transaction instance (from arm_start), and some flags. The format is flexible and extendible so more conventions can be added as the need arises. See the "Appendix: Measurement Agent Information" on page 58 for information on the correlator format.
Correlators are passed in the arm_start calls by utilizing the data buffer. This same data buffer is used to pass application-defined metrics, as described "Format of Data Buffer in arm_start, arm_update, arm_stop" on page 50. Correlators are ignored in arm_update and arm_stop calls.
If a correlator is being requested, the data buffer should be 256 bytes, to allow for a variable size correlator. If a correlator is being passed to the measurement agent, and none is requested, the length may be truncated based on the correlator length.
If you only wanted to do transaction correlation in your application and not provide application-defined metrics, you can zero out the metrics (set the Flags Second Byte to zero and fill with zeros 80 bytes for the metrics descriptions).
NOTE: Other than the length, the correlator format need not be understood by the application developer, as it is opaque.
Application-defined metrics can tell you more about the transaction or about the state of the application at the moment that the transaction is being processed. Three likely uses are envisioned as described below:
In setting up application-defined metrics, arm_getid is used to define the context (or "meta-data") for a buffer of values that can be passed at arm_start, arm_update or arm_stop. Actual values are passed in arm_start, arm_update and arm_stop. The length of the buffer is specified in the data_size parameter.
The additional data provided in the data buffer uses metric and/or string fields. (See later sections for information on the format of the data buffer.) Four general data types can be specified for each field (counter, gauge, numeric id and string). This section provides some suggestions about which data type to use.
Counter
A counter should be used when it makes sense to sum up the values over an interval. Examples are bytes printed and records written. The values can also be averaged, maximums and minimums (per transaction) can be calculated, and other kinds of statistical calculations can be performed.
If a counter is used, its initial value must be set in the arm_start call. The difference between the value in the arm_start and the arm_stop (or the value in the last arm_update call if no metric value is passed in arm_stop), equals the amount attributed to this transaction. Similarly, the difference between successive arm_update calls, or from the arm_start to the first arm_update call, or from the last arm_update to the arm_stop call, equals the value for the time period between the calls.
Here are three examples of how a counter would probably be used:
Gauge
A gauge should be used instead of a counter when it is not meaningful to sum up the values over an interval. An example is the amount of memory used. If you were measuring the amount of memory used over 20 transactions in an interval and the average usage for each of these transactions was 15 MB, it does not make sense to say that 20*15=300 MB of memory used over the interval. It would make sense to say that the average was 15 MB, that the median was 12 MB, and that the standard deviation was 8 MB. These are the kinds of operations that an agent will typically apply to gauges. The values can also be averaged, maximums and minimums per transaction calculated, and other kinds of statistical calculations performed.
Gauges can be provided on arm_start, arm_update, and arm_stop calls. This creates the potential for different interpretations. If several values are provided for a transaction (one on an arm_start, one on arm_update(s), and one on an arm_stop), which one(s) should be used? In order to have consistent interpretation, the following conventions apply. Measurement agents are free to process the data in any way within these guidelines.
Numeric ID
A numeric id is simply a numeric value that is used as an identifier, and not as a measurement value. Examples are message numbers and error codes. It is not meaningful to sum, average, or manipulate these values in any arithmetic way. By using numeric id instead of a gauge or counter, the application indicates this to the measurement agent. An agent could create statistical summaries based on these values, such as generating a frequency histogram by error code, but this is done by counting the numbers, not by summing them or performing any other arithmetic operation.
String
A measurement agent should process a string in the same way as a numeric id. As with numeric ids it is not meaningful to do arithmetic operations on a string value.
Format of Data Buffer in arm_getid
Format | 4 bytes | 101 (int32) (identifies "meta-data" format) |
Flags
The flags indicate which Metric and String Descriptions are included in the buffer. |
4 bytes | First Byte (bit8) = 0
Second Byte (bit8) abcdefg0, where a through g each denote the value of a bit flag: a = 1 if there is a description for Metric #1, otherwise a = 0 b = 1 if there is a description for Metric #2, otherwise b = 0 c = 1 if there is a description for Metric #3, otherwise c = 0 d = 1 if there is a description for Metric #4, otherwise d = 0 e = 1 if there is a description for Metric #5, otherwise e = 0 f = 1 if there is a description for Metric #6, otherwise f = 0 g = 1 if there is a description for String #1, otherwise g = 0 Third Byte (bit8) = 0 Fourth Byte (bit8) = 0 |
Metric #1 Description | 48 bytes | The first 4 bytes (int32) define the
type of data that will be passed in the 8 byte field. See the
description below this table for an explanation of the different data
types.
1 = ARM_Counter32 3 = ARM_CntrDivr32 4 = ARM_Gauge32 5 = ARM_Gauge64 6 = ARM_GaugeDivr32 7 = ARM_NumericID32 8 = ARM_NumericID64 9 = ARM_String8 The last 44 bytes (char*) are the name of the metric. This is a NULL terminated character string. A possible use of this name is to display it along with the current value, either on a user interface or in a report. |
Metric #2 Description | 48 bytes | Same as Metric Description #1. |
Metric #3 Description | 48 bytes | Same as Metric Description #1. |
Metric #4 Description | 48 bytes | Same as Metric Description #1. |
Metric #5 Description | 48 bytes | Same as Metric Description #1. |
Metric #6 Description | 48 bytes | Same as Metric Description #1. |
String #1 Description | 48 bytes | The first 4 bytes (int32) define the
type of data that will be in the field. Only one data type is valid in
this field.
10 = ARM_String32 The last 44 bytes (char*) are the name of the String #1 field. It is a NULL terminated character string. A possible use of this name is to display it along with the current value, either on a user interface or in a report. |
ARM_Counter32 | An unsigned32 value that increases up to the maximum value that the counter can hold, at which point it resets to zero and continues counting up from zero. Except for the reset back to zero, the value can never decrease. The counter is in the first four bytes, and the second four bytes are unused. |
ARM_Counter64 | An unsigned64 counter (see ARM_Counter32, except it’s 64 bits long). |
ARM_CntrDivr32 | A combination of two unsigned32 integers, with ARM_Counter32 in the first four bytes, and an unsigned32 divisor in the second four bytes. The total value is ARM_CntrDivr32. The purpose of this format is to be able to represent decimal values without using floating point formats. |
ARM_Gauge32 | An int32 (signed) value that can increase or decrease. The gauge is in the first four bytes, and the second four bytes are unused. |
ARM_Gauge64 | An int64 (signed) gauge (see ARM_Gauge32, except it’s 64 bits long). |
ARM_GaugeDivr32 | A combination of two integers, one an int32 (signed) and one an unsigned32. ARM_Gauge32 is in the first four bytes, and an unsigned32 divisor in the second four bytes. The total value is ARM_GaugeDivr32. The purpose of this format is to be able to represent decimal values without using floating point formats. |
ARM_NumericID32 | An unsigned32 value that should not be used in arithmetic operations because it is used as an identifier, not as a measurement. For example, a message number or error code. The numeric id is in the first four bytes, and the second four bytes are unused. |
ARM_NumericID64 | An unsigned64 value that should not be used in arithmetic operations because it is used as an identifier, not as a measurement. An example is a message number or error code. |
ARM_String8, | An 8 byte string that is not NULL terminated. If the string is less than eight bytes long, it must be padded with blanks. The character set is ASCII or EBCDIC, depending on whatever is standard for that platform. Unlike the NULL terminated character strings passed in various places in the API, these strings cannot be reliably converted to other code pages, so it is suggested you use only the common characters in the first 128 characters of the Latin code pages. See the "Internationalization" section on page 56 for more information.. |
ARM_String32, | A 32 byte string that is not NULL terminated. If the string is less than 32 bytes long, it must be padded with blanks. The character set is ASCII or EBCDIC, depending on whatever is standard on that platform. Unlike the NULL terminated character strings passed in various places in the API, these strings cannot be reliably converted to other code pages, so it is suggested you use only the common characters in the first 128 characters of the Latin code pages. See the "Internationalization" section on page 56 for more information. |
Format of Data Buffer in arm_start, arm_update, arm_stop
Format 1
Format | 4 bytes | 1 (int32)
(2 is a special format for arm_update, see the table on page 54) |
Flags
The flags indicate which fields are included in the buffer. |
4 bytes | First Byte (bit8) (Only valid for
arm_start.
Ignored on arm_update
and arm_stop.)
abcd0000, where a,b,c,d each denote the value of a bit flag. a,b,d are set by the application. c is set by the measurement agent. a = 1 if the application is passing the correlator from a parent transaction in the Correlator field; otherwise a = 0. b = 1 if the application is requesting that the agent generate a correlator for the transaction (the one indicated by this arm_start command); otherwise b = 0. If a correlator is being requested, the data buffer should be 256 bytes, to allow for a variable size correlator. c = 1 if the agent is returning a correlator in the Correlator field. When set, the value in the Correlator field overlays any previous value. This flag will only be set when three conditions are met, otherwise c=0:
If this bit is not set to 1, there is no correlator, and therefore the application should not forward the contents of the Correlator field. d = 1 if the application is requesting that the agent trace this transaction. This might be done when a dummy test transaction is being executed, or when an error has occurred. Each agent can choose how and if it should honor the request, and administrators who configure the agent may establish the policy. Second Byte (bit8) abcdefg0, where a through g each denote the value of a bit flag: a = 1 if a value is passed in Metric #1, otherwise a = 0 b = 1 if a value is passed in Metric #2, otherwise b = 0 c = 1 if a value is passed in Metric #3, otherwise c = 0 d = 1 if a value is passed in Metric #4, otherwise d = 0 e = 1 if a value is passed in Metric #5, otherwise e = 0 f = 1 if a value is passed in Metric #6, otherwise f = 0 g = 1 if a value is passed in String #1, otherwise g = 0 It is perfectly permissible for an application to pass none or some of the metrics on each call, and to change which metrics are passed from call to call. This holds true for arm_start, arm_update, and arm_stop calls. The one requirement that must be adhered to is that the meaning and position of the field must have been defined with the arm_getid call. (see the section "Format of Data Buffer in arm_getid"). Third Byte (bit8) = 0 Fourth Byte (bit8) = 0 |
Metric #1 | 8 bytes | The metric fields are used by the
application to pass useful information about the transaction or the
state of the application to the measurement agent. The field contains
one or two integers, or a string variable. The use of the field and the
format of the field are determined by the buffer passed on the arm_getid
call (see the section "Format of Data Buffer in arm_getid").
See the sections "Choosing A Data Type" on page 42 and "Data Type Definitions" on page 48 for more information. |
Metric #2 | 8 bytes | Same as Metric #1. |
Metric #3 | 8 bytes | Same as Metric #1. |
Metric #4 | 8 bytes | Same as Metric #1. |
Metric #5 | 8 bytes | Same as Metric #1. |
Metric #6 | 8 bytes | Same as Metric #1. |
String #1 | 32 bytes | A string variable of up to 32 characters. The string is not NULL terminated, and is padded with blanks if it is less than 32 characters. Any information can be included in the string. Examples would be a part number being processed, or an error code. |
Correlator | Length
2 bytes Data 0-166 bytes |
The field has two different uses
depending on whether it is passed on the call from the application to
the measurement agent, or if it is passed in the return from the agent:
If the correlator returned bit is set (Flags First Byte c=1), the application can either pass the entire 168 byte correlator. Or if you want to optimize, the application can choose to read the correlator length field and only pass the number of bytes containing data, starting with the 2 bytes of the correlator length. See "Transaction Correlation" on page 38 for more information on correlating transactions. See the "Appendix: Measurement Agent Information" on page 58 for more information on the content of the correlator. The Correlator length field (unsigned 16) specifies the length of a correlator (including this field) generated by a measurement agent (when bit c is set in the first Flags byte). If this value is zero, it means that the agent is not returning a correlator, and therefore there isn’t any reason to pass this correlator on to other parts of the application (or servers that it calls). This field is considered a part of the correlator and must be included in the forwarded correlator data. The Correlator data field is used to show the parent/child relationship between transactions. (Note: the application instrumenter need not understand the correlator format as it is "opaque"). |
Format 2
In the arm_update calls with a Format field containing the value 2, the buffer may have the following format:
Format | 4 bytes | 2 (int32) |
Data | 1020 bytes (maximum) | Contains the data. The length of the
buffer is determined by the data_size
parameter. The format of the data is not defined,
but it is suggested that the data be formatted as plain-text characters
so it can be understood without requiring a special formatting program.
The agent cannot summarize the data over an interval, it must be treated
as trace data. One suggestion is to format all information as plain-text
characters so it can be read by a person without a special formatting
program.
Note that because the data in an opaque buffer cannot be summarized, and processing by the agent may consist of logging the data to a trace file, many calls at a high frequency could result in a loss of data or a slowing down of the system, most likely due to an excessive amount of file I/O. Therefore it is recommended that the call be used only in special situations. NULL termination is not required. |
Three Ways to Instrument within a Transaction Instance
There are three methodologies for instrumenting within a transaction instance. The first two are useful when the transaction is within one application; the last one is useful when the transaction is distributed across applications or systems.
If these transactions have different steps associated with processing each record, you may want to instrument these steps with component transactions (as described below), or use repeated calls to arm_update to show the overall progress of the transaction. For example, the transaction may process a million records. A call to arm_update could be made for every 1000 records or every minute of processing. This could show the progress of the transaction based on the number of times arm_update was called or with one or more application-defined metrics.
For example, step 1 takes about 20 minutes, step 2 takes about 40 minutes, and step 3 takes about 10 minutes. Each step can have a defined transaction as well as the overall transaction. So you would define 3 component transactions monitoring each step, plus one transaction that monitors the overall transaction.
The ARM API is designed to enable applications to use native code pages and languages, and for measurement agents to be able to support many different languages. Users of agents should contact the providers to see if the agent supports the needed code pages and languages.
The ARM API supports any code page as long as no characters are encoded with binary zero bytes (octets). This is because most strings are passed as NULL terminated strings, and the NULL terminator character is a binary zero byte. If a binary zero byte is encountered before the end of the string, the agent would interpret the zero byte as the NULL terminator and truncate the string. Most code pages meet this requirement.
These are code pages that contain binary zero bytes, but there are alternate ways to encode the characters. A well-known example is the Unicode standard. In its native format using 16 bit characters (UTC-2), there are binary zero bytes. However, the UTF-8 encoding of the same Unicode characters does not contain binary zero bytes, and this format is entirely compatible with the ARM API.
Agents that support native languages will often use the following technique. When the application links to the agent it links to a part of the agent that executes in the same process space as the application. Typically this small part of the agent communicates with the main part of the agent across an inter-process communications (IPC) channel. The small part of the agent that executes in the same process as the application can issue an operating system call to find out what code page and language the process is using. It can then pass this information to the main part of the agent, and the main part of the agent can convert from the native code page as necessary.
There are the following three restrictions on the use of native languages.
Appendix: Measurement Agent Information
This appendix contains information provided for measurement agent implementers as opposed to ARM application instrumenters. For instrumenters it is provided as reference only, the correlator is "opaque" from an application instrumenter’s perspective.
The agents provide the correlators, and within the correlator they provide information to uniquely identify agents. To enable an enterprise management solution (correlation application) to analyze the correlators coming from different systems in a heterogeneous environment, agents need to follow some conventions when creating correlators.
The following section documents a set of semantics for measurement agents to use in formatting the correlator and agent identifiers.
The correlator passed on arm_start calls is sent across systems, so it is always in network byte order. Network byte order is a standard described as follows:
Buffer word/byte/bit Format
byte 0 byte 1 byte2 byte 3
|----------|----------|-----------|-----------|
0 7 8 15 16 22 23 31
msb lsb
Correlators provided by agents and passed on the arm_start commands have the following format.
2 bytes | Length of the Correlator (unsigned16)
If this value is zero, it means that the measurement agent is not returning a correlator, and therefore there isn’t any reason to pass this correlator on to other parts of the application (or servers that it calls). A zero length provides another safeguard for agents. If an application passes a null correlator anyway, when any agent receives this correlator as the parent correlator for another transaction, the agent can see that the data in the correlator is invalid and ignore it, regardless of whether the "parent correlator" bit (Flags First Byte a) is set in the arm_start buffer. |
1 byte | Correlator format (unsigned8)=1
Only one format is defined at this point, but others could be added in the future. |
1 byte | Flags
First Byte (bit8) ab000000, where a and b are bit flags: a = 1 if a trace of this transaction and any nested component transactions is requested by the agent. b = 1 if a trace of this transaction and any nested component transactions is requested by the application. The application requests this by setting the "d" bit (in abcdefgh notation) in the first flag byte in the buffer passed on arm_start. The agent will decide whether to set this bit, based on its capabilities and how it is configured. The "trace this correlator" flag is a way to cause agents to trace and/or monitor a transaction and all component transactions associated with the transaction without having to trace or monitor all transactions on a system, or without requiring a complicated infrastructure to control tracing and monitoring. (Note that this does not preclude other ways to control agents, nor is this intended to be a final and comprehensive solution. It is intended that this will be used in addition to other approaches). When an agent builds a correlator, it is free to turn on these flags. The agent might do this if an application has been experiencing unsatisfactory response times. Any agents that receive this correlator as the parent correlator for a component transaction will also see the flag, and they in turn could turn on the flag in any correlators they generate. This process could repeat, resulting in the passing of the trace flag through all the transactions of interest. All the agents might be configured to trace only the few transactions with this flag on, and this would both capture the information needed to diagnose the transaction problem, and avoid overloading the agents and their systems with attempts to trace all transactions. The reason there are separate flags for traces requested by an agent and an application is to provide additional flexibility in how policies for monitoring and tracing are implemented. It might be common for an installation to trace transactions only when requested by agents (based on how the administrator has configured the agents), because then the administrator would control all tracing. On the other hand, permitting the application to highlight when a transaction is special has advantages. |
2 bytes | Format of the Address field
(unsigned16)
The following formats are defined: 0 = reserved 1 = IPv4 2 = IPV4+port number 3 = IPv6 4 = IPv6+port number 5 = SNA 6 = X.25 7:32767 = reserved This list will be expanded as new requirements arise. The intent is to provide a value for any common addressing format as soon as the need is identified. 32768-65535 = undefined and available for agent implementers to use. There are no semantics associated with the address format. It will be an unusual situation where a new format is needed, but this provides a solution if this is needed. The preferred approach is to get a new format defined that is in the 0-32767 range. There is a risk that two different agent developers will choose the same id, but this risk is small. |
2 bytes | Vendor ID (unsigned16)
The vendor ID is a way to identify who built the agent. Combining this information with the Agent Version field will provide a way for a management application to know what kind of agent generated a correlator. A management application may contain specialized functions or logic that only works with the agents from a particular vendor and/or supporting particular functions or interfaces. By putting these two fields in the correlator, a management application has a way to know whether the agent that generated the correlator has some of these specialized capabilities. For example: The management application wants to contact the agent to know the name of the application, user, and transaction class running this transaction instance. Although the address of the agent is known from the Address field, the protocol that one uses to interface to the agent could be anything. The management application may know how to access several different agents, and could use these values to determine if the correlator came from an agent that it knows how to access. Alternately an agent has a special capability. For example, maybe version 3.3 of a vendor’s agent analyzes data in a particular way, but previous versions do not. The management application could use this field to see what are the agent’s capabilities. In order to minimize the possibility of two vendors using the same vendor ID, the value should be taken from the list of enterprise identifiers from the Internet Assigned Numbers Authority (IANA). This list was created for vendors who have SNMP agents. Although the ARM API specification does not require or endorse SNMP, it's likely that most or all the organizations that will create an ARM agent will have at least one enterprise ID assigned. The list of enterprise IDs can be found at: ftp://ftp.isi.edu/in-notes/iana/assignments/enterprise-numbers For organizations that don't have an enterprise identifier assigned by the IANA, the values between 32768-65535 are free for agent developers to use. There are no semantics associated with these ids. It is expected that most or all agent developers will have a formally assigned vendor id, and it will be an unusual situation where another id is needed, but this provides a solution if this is needed. There is a risk that two different agent developers will choose the same id, but this risk is very small. |
2 bytes | Agent Version (unsigned16)
The Agent Version is used to distinguish between different versions of an agent, and will be most useful when the capabilities and/or interfaces of an agent change from one release to another. It will also be useful to distinguish between different agents from the same vendor. Each vendor is responsible for avoiding having multiple agents with different capabilities using the same Agent Version value. Refer to the explanation in the Vendor ID field above to understand how to use this field. |
2 bytes | Agent Instance (unsigned16)
Each agent assigns transaction ids and start handles. Typically there will be one agent on each system, and this one agent is responsible for making sure that there aren’t any duplicate ids or handles. From one system to another, however, duplicate ids and handles will be common, i.e., an id/handle combination assigned on system X will also be assigned on system Y. One of the main purposes of the Address, Vendor ID, and Agent Version fields is to tell a management application how to contact an agent in order to translate the transaction id and start handle into the names of the application, user, and transaction class, and the instance of the transaction. As long as there is only one set of ids and handles stored at that address, all the required information is there. However, if the address is not the address of an individual agent, but rather is the address of a directory that contains information about multiple agents, there isn’t sufficient information, because the id/handle combinations can be duplicated. The purpose of the Agent Instance field is to provide a way to identify which agent generated a correlator, even if the correlation data from multiple agents is available at the address specified in the Address field. |
4 bytes | Transaction instance (start_handle returned from an arm_start) |
4 bytes | Transaction class ID (tran_id returned from an arm_getid) |
2 bytes | Length of the address field (unsigned16) |
Maximum 146 bytes | Address
This field is the address of the agent. More precisely, it is the address that a management application can contact in order to have the Transaction class ID mapped to the names of an application, user, and transaction class, and to get information about the transaction instance, or aggregated data about the transaction class (or any other data). The maximum length of this field is determined by an overall limit of 168 bytes for the correlator. In the correlator format described here, the maximum address length is 146 bytes. In actual practice, it is expected to be no more than 20 bytes for most implementations. If new correlator formats are added in the future, the maximum size of this field could change. The maximum correlator size of 168 bytes will not change. Correlators are passed on arm_start calls as part of the buffer pointed to by the data pointer. The maximum size of the buffer is 256 bytes, of which 88 bytes are used for other fields, leaving 168 bytes for the correlator. An application should allocate space for the full 256 bytes when making the arm_start call, but can then use the Correlator Length field to determine how long the correlator really is, and only forward that much data to other cooperating applications. Following are the formats that have been defined so far. The data is stored in network standard byte order, in which integers are sent most significant byte first, unless otherwise indicated. This list is not intended to be exhaustive, and will be extended whenever a new agent implementation requires a new format. 0 = reserved 1 = IPv4 Bytes 0:3 4 byte IP address 2 = IPV4+port number Bytes 0:3 4 byte IP address Bytes 4:5 2 byte IP port number 3 = IPv6 Bytes 0:15 16 byte IP address 4 = IPv6+port number Bytes 0:15 16 byte IP address Bytes 16:17 2 byte IP port number 5 = SNA Bytes 0:7 EBCDIC-encoded network ID Bytes 8:15 EBCDIC-encoded network accessible unit (control point or LU) 6 = X.25 Bytes 0:15 The X.25 network address (also referred to as an X.121 address). This is up to 16 ASCII character digits ranging from 0-9. The length is known from the "Length of the address field". An agent running over an X.25 link with the IP configured may choose to use this format or the IP format. This format must be used when IP is not configured above an X.25 link. 7:32767 = reserved 32768-65535 = undefined and available for agent
implementers to |
Sample code is provided as part of the ARM SDK. These sample programs are available on the ARM API CD-ROM and the ARM Web Site.