File: //opt/perf/examples/arm/armsample3.c
/*****************************************************************************/
/* armsample3 Application Response Measurement sample FEB01 */
/* */
/* This program provides an example of the use of two features of ARM 2.0: */
/* User-Defined Metrics and Correlation. See the Tracking Your Transactions */
/* and ARM 2.0 API manuals. Printable copies exist under this product's */
/* paperdocs directory (for example, /opt/perf/paperdocs/arm/C/). */
/* */
/* For an example of a program which uses User-Defined Metrics but not ARM */
/* correlators, see the armsample4.c program. */
/* */
/* This example simulates a client/server application where both server and */
/* client perform a number of transactions. Normally application client */
/* and server components would exist in separate programs, but they're here */
/* together for simplicity. The client procedure starts a transaction, and */
/* requests an ARM correlator from its arm_start call. This correlator */
/* is saved by the client and passed to the server so that the server can */
/* use it when it calls arm_start. The performance tools running on the */
/* server can then use this correlator information to distinguish the */
/* different clients making use of the server. */
/* */
/* Also shown in this program is the mechanism for passing user-defined */
/* metric values into the ARM API. This allows you to not only see the */
/* response times and service-level information in the performance tools, */
/* but also data which may be important to the application itself. For */
/* example, a transaction may be processing different size requests, and */
/* the size of the request could be a user-defined metric. When the */
/* response times are high, this user-defined metric could be used to see */
/* if long response times correspond to bigger size transaction instances. */
/* */
/* See target platform and compiler for guidelines on 64-bit support. */
/*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <arm.h>
/* Define macros so sleep works on NT: */
#ifdef _WIN32
#define sleep(n) Sleep(1000*(n))
#else
#include <netinet/in.h>
#endif
/* Global transaction IDs shared by initialization and other procedures: */
arm_tran_id_t client_tran_id;
arm_tran_id_t server_tran_id;
int pausetime = 5; /* sleep seconds */
int pausecount = 0; /* cumulative seconds slept */
/*****************************************************************************/
/* init_server_application */
/* */
/* This procedure would be called once by the server program to initialize */
/* variables and the ARM API. */
/*****************************************************************************/
void init_server_application(void)
{
arm_appl_id_t server_appl_id; /* application id */
/* To use User-Defined Metrics in ARM, you need to create a meta-data */
/* definition to pass into arm_getid. This specifies the format of the */
/* actual UDM data which will later be passed into arm_start and stop. */
/* Below is one example of a meta-data format. See the include file */
/* arm.h for exact definitions. The ARM 2.0 API guide describes the */
/* metrics types. The performance tool metric help documentation */
/* describes the expected behavior of the Transaction User Measurements */
/* metrics. */
arm_user_data101_t *mbuf_ptr, mbuf = {
101,
{0, ARM_AllMetrics_f | ARM_String1_f ,0 ,0},
{{1, "1st metric - Type 1 is a COUNTER32"},
{4, "2nd metric - Type 4 is a GAUGE32"},
{5, "3rd metric - Type 5 is a GAUGE64"},
{9, "4th metric - Type 9 is a STRING8"},
{6, "5th metric - Type 6 is a GAUGE32/DIVISOR32"},
{8, "6th metric - Type 8 is a NUMERICID64"},
{10, "The last field is always a STRING32 "}
}};
arm_data_sz_t mbuf_sz; /* size of the meta-data buffer */
/* Initialize ARM for server: normally arm_init and getid calls */
/* should only be done once during the initialization block of the */
/* server program, then repeated transaction instances would be */
/* done in the main body of the program. */
server_appl_id = arm_init("Server_Appl", /* application name */
"*", /* user-name (default) */
0,0,0); /* reserved: zero */
/* We do not check the return codes from the ARM API, as normal */
/* operation of a program should not be affected by whether the ARM */
/* agent is working. */
mbuf_ptr = &mbuf;
mbuf_sz = sizeof(mbuf);
server_tran_id = arm_getid(server_appl_id, /* appl_id from arm_init */
"Server_tran", /* transaction name */
"Transaction in Server program",
/* tran description */
0, /* reserved: zero */
(arm_data_t *)mbuf_ptr, /* udm buffer pointer */
mbuf_sz); /* udm buffer size */
/* Note the ids returned from the ARM API will be invalid if the ARM */
/* agent is not running (ttd/midaemon) */
printf("Server transaction ID = %i\n", server_tran_id);
return;
} /* init_server_application */
/*****************************************************************************/
/* server_application */
/* */
/* This routine is included here to simplify this example. In a real */
/* life situation, this piece of code would actually be running in a */
/* separate program and process on a different system. */
/* We are passed an ARM correlator data structure which was returned to */
/* the client from its arm_start call, and pass it into the server's */
/* arm_start call. This will reference the server transaction back to */
/* the specific client that it is serving. */
/*****************************************************************************/
void server_application(arm_app_correlator_t client_correlator)
{
/* User data buffer for passing User-Defined Metric values and */
/* correlator into arm_start, update, and stop */
arm_user_data1_t *buf_ptr, buf;
arm_data_sz_t buf_sz;
int i, data_len;
arm_start_handle_t server_tran_handle = -1;
/* set up User-Defined Metrics and Correlator buffer */
buf_ptr = &buf;
buf_ptr->format = 1;
buf_ptr->flags[0] = ARM_CorrPar_f;
buf_ptr->flags[1] = ARM_AllMetrics_f | ARM_String1_f;
/* Pass the parent correlator received from the client application to */
/* the ARM agent using the arm_start call. */
/* Note: The correlator is always in network byte order. */
/* It must be converted to host byte order before being accessed. */
buf_ptr->correlator.length = client_correlator.length;
data_len = (ntohs(client_correlator.length) -
sizeof(client_correlator.length));
for (i = 0; i < data_len; i++)
buf_ptr->correlator.agent_data[i] = client_correlator.agent_data[i];
buf_sz=(sizeof(buf)-sizeof(client_correlator) +
ntohs(client_correlator.length));
/* the 1st metric will be a counter for sleep time, since counters */
/* track changes, the value of the derived metric will reflect the */
/* increment not the total */
buf_ptr->metric[0].counter32 = pausecount;
/* the 2nd metric will be a gauge for the sleep time, and as a */
/* gauge will track the increasing total */
buf_ptr->metric[1].gauge32 = pausecount;
/* if the ARM agent defines 64-bit integer fields (long long), then */
/* we'll stuff a big number in there, else divide into halves */
#ifdef _WIN32
buf_ptr->metric[2].gauge64 = 444333222111i64; /* big number >32bitsworth */
#else
#ifdef ARM_INT64
buf_ptr->metric[2].gauge64 = 444333222111LL; /* big number >32bitsworth */
#else
buf_ptr->metric[2].gauge64.upper = 1;
buf_ptr->metric[2].gauge64.lower = 43210;
#endif
#endif
/* 4th metric, a 8-character string field */
strcpy(buf_ptr->metric[3].string8, "String 8");
/* the 5th metric is a floating-point gauge defined by integer */
/* numerator and denominators */
buf_ptr->metric[4].gaugedivr32.gauge = 100;
buf_ptr->metric[4].gaugedivr32.divisor = 3; /* 100/3 == 33.333... */
/* the 6th metric is another 64-bit value */
#ifdef _WIN32
buf_ptr->metric[5].numericid64 = 20000000000i64;
#else
#ifdef ARM_INT64
buf_ptr->metric[5].numericid64 = 20000000000LL;
#else
buf_ptr->metric[5].numericid64.upper = 1;
buf_ptr->metric[5].numericid64.lower = 43210;
#endif
#endif
/* the last UDM is a 32-byte string */
strcpy(buf_ptr->string32,"Initial 32 char string to start ");
/* both correlator and UDM data are passed in same data buffer */
server_tran_handle = arm_start(server_tran_id, /* tran_id from arm_getid */
0, /* reserved: 0 */
(arm_data_t *)buf_ptr, /* UDM and correlator */
buf_sz); /* data buffer size */
/* This is where the "real work" of the server would be performed */
/* In this example, we just sleep for "pausetime" seconds */
sleep(pausetime);
pausecount += pausetime;
/* To show use of arm_update, we'll modify some of the User-Defined */
/* metric fields in the data buffer */
buf_ptr->metric[4].gaugedivr32.divisor = 4; /* 100/4 == 25 */
#ifdef _WIN32
buf_ptr->metric[2].gauge64 = -9876543210i64; /* big negative number */
buf_ptr->metric[5].numericid64 = 10000000000i64; /* big num */
#else
#ifdef ARM_INT64
buf_ptr->metric[2].gauge64 = -9876543210LL; /* big negative number */
buf_ptr->metric[5].numericid64 = 10000000000LL; /* big num */
#endif
#endif
strcpy(buf_ptr->string32,"This is an UPDATED 32char string");
arm_update(server_tran_handle, /* transaction handle from arm_start */
0, /* reserved: zero */
(arm_data_t *)buf_ptr, /* UDM + Correlator buffer pointer */
buf_sz); /* buffer size */
sleep(pausetime);
pausecount += pausetime;
/* Change UDM metrics again before stopping transaction instances: */
buf_ptr->metric[0].counter32 = pausecount;
buf_ptr->metric[1].gauge32 = pausecount;
buf_ptr->metric[4].gaugedivr32.gauge = 445; /* 445/4 == 111.25 */
#ifdef _WIN32
buf_ptr->metric[2].gauge64 = 555444333222i64; /* more big nums */
buf_ptr->metric[5].numericid64 = 60000000000i64;
#else
#ifdef ARM_INT64
buf_ptr->metric[2].gauge64 = 555444333222LL; /* more big nums */
buf_ptr->metric[5].numericid64 = 60000000000LL;
#endif
#endif
arm_stop(server_tran_handle, /* transaction handle from arm_start */
ARM_GOOD, /* successful completion define = 0 */
0, /* reserved: zero */
(arm_data_t *)buf_ptr, /* data buffer */
buf_sz); /* data buffer size */
return;
} /* server_application() */
/*****************************************************************************/
/* init_client_application */
/* */
/* This procedure would be called once by the client program to initialize */
/* variables and the ARM API. */
/*****************************************************************************/
void init_client_application(void)
{
arm_appl_id_t client_appl_id = -1; /* application id */
client_appl_id = arm_init("Client_appl", /* application name */
"*", /* default user name */
0,0,0); /* reserved: zero */
client_tran_id = arm_getid(client_appl_id, /* appl_id from arm_init */
"Client_tran", /* transaction name */
"transaction from client application",
/* tran description */
0, /* reserved: zero */
0,0); /* buffer pointer & size */
/* Although the ARM API calls may fail if the agent is not running, */
/* this would not normally impact the runtime behavior of a production */
/* program. In this example we'll print the return from getid, which */
/* will be -1 if ttd is not running */
printf("Client transaction ID = %i\n", client_tran_id);
return;
} /* init_client_application */
/*****************************************************************************/
/* client_transaction */
/* */
/* This procedure would normally represent the ongoing working section of */
/* a client process. Transactions would be initiated in response to some */
/* user activity, then the server would be contacted to process the request */
/* on behalf of this client. A client transaction ends when the server */
/* completes its action on behalf of the client. */
/*****************************************************************************/
void client_transaction(void)
{
arm_start_handle_t client_tran_handle = -1;
arm_user_data1_t *buf_ptr, buf = {
1, /* Header */
};
arm_data_sz_t buf_sz;
arm_app_correlator_t correlator = {
0, /* correlator length */
0, /* agent data */
};
int i, data_len, sheep;
buf_ptr = &buf;
buf_sz = sizeof(buf);
/* The client application requests a correlator from the ARM Agent */
buf_ptr->format = 1;
buf_ptr->flags[0] = ARM_CorrReq_f;
client_tran_handle = arm_start(client_tran_id, /*tran_id from arm_getid*/
0, /* reserved for future use */
(arm_data_t *)buf_ptr, /* metrics buf ptr */
buf_sz); /* user metric buffer size */
/* Note no transaction instance handle will be returned if the midaemon */
/* is not active. Can use perfstat script to check status of tools */
if (client_tran_handle == -1)
printf("arm_start returned -1, midaemon may be inactive\n");
/* If the ARM Agent returns a correlator, determine the size of the */
/* agent specific data in the correlator and pass the data, along with */
/* the correlator length, to the server application. */
/* Note: The correlator is always in network byte order. */
/* Its data must be converted to host byte order before being accessed. */
if ((buf_ptr->flags[0] & ARM_CorrGen_f) != ARM_CorrGen_f)
printf("client correlator not being returned\n");
else {
correlator.length = buf_ptr->correlator.length;
data_len = (ntohs(correlator.length)
- sizeof(buf_ptr->correlator.length));
for (i = 0; i < data_len; i++)
correlator.agent_data[i] = buf_ptr->correlator.agent_data[i];
}
/* this is normally where the client-specific processing would take */
/* place in a real program (possibly both before and after a request */
/* is sent to the server. To simulate some activity, we'll count */
/* ten million sheep both before and after the server request */
for (sheep = 0; sheep < 10000000; sheep++) i=sheep;
server_application(correlator);
for (sheep = 0; sheep < 10000000; sheep++) i=sheep;
arm_stop(client_tran_handle, /* transaction handle from arm_start */
ARM_GOOD, /* successful completion define = 0 */
0, /* reserved for future use */
0,0); /* buffer pointer & buffer size */
return;
} /* client_transaction() */
/*****************************************************************************/
/* Main */
/*****************************************************************************/
int
main(void)
{
init_server_application();
init_client_application();
printf("processing transactions...\n");
/* loop infinitely doing same old boring transaction... what a life */
while (1)
client_transaction();
}