ARM 4 C Language Tutorial - Introduction
Table of Contents
Here's a variant on the Kernighan and Richie classic:
#include <stdio.h>
#include <arm4.h>
static void hello_world()
{
arm_id_t app_id;
arm_id_t tran_id;
arm_app_start_handle_t app_handle;
arm_tran_start_handle_t tran_handle;
/* -------------------------- 1. step -------------------------- */
/* Register application class with ARM agent. */
arm_register_application("HelloWorld", ARM_ID_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&app_id);
/* Register transaction class with ARM agent. */
arm_register_transaction(&app_id,
"HelloTran", ARM_ID_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&tran_id);
/* -------------------------- 2. step -------------------------- */
/* Now start the application instance for the ARM agent. */
arm_start_application(&app_id,
"Examples", ARM_STRING_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&app_handle);
/* -------------------------- 3. step -------------------------- */
/* Now start the transaction measurement. */
arm_start_transaction(app_handle, &tran_id, ARM_CORR_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&tran_handle, ARM_CORR_NONE);
/* Real transaction takes place here! */
printf("Hello world!\n");
/* Stop the measurement and commit the transaction to ARM. */
arm_stop_transaction(tran_handle, ARM_STATUS_GOOD,
ARM_FLAG_NONE, ARM_BUF4_NONE);
/* -------------------------- 4. step -------------------------- */
/* Stop the application instance. */
arm_stop_application(app_handle, ARM_FLAG_NONE, ARM_BUF4_NONE);
/* -------------------------- 5. step -------------------------- */
/* Destroy all registered metadata. */
arm_destroy_application(&app_id, ARM_FLAG_NONE, ARM_BUF4_NONE);
return;
}
int main(int argc, char *argv[])
{
int rc = 0;
hello_world();
return rc;
}
While this adds a lot to a simple program, you'll find that it doesn't add much more to a more complex one. To understand what's going on, let's look at the steps involved.
The first element of note is the additional include file.
#include <arm4.h>
This includes the function declarations and constants you'll use in all ARM 4 instrumented programs.
Registration
The next stage of instrumentation is registration.
/* Register application class with ARM agent. */
arm_register_application("HelloWorld", ARM_ID_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&app_id);
/* Register transaction class with ARM agent. */
arm_register_transaction(&app_id,
"HelloTran", ARM_ID_NONE,
ARM_FLAG_NONE, ARM_BUF4_NONE,
&tran_id);
As you'll recall from our concepts tutorial, transactions are part of an application, and applications are composed of transactions. In this case, we're declaring a basic application called "HelloWorld?". As it's registered, an identifier is returned in app_id. If the application had been registered previously, then the same identifier is returned. It's possible to register the same application in multiple programs. As long as the parameters are the same, they'll all return the same application identifier even though their transactions might be quite different.
We then register our transactions indicating that their part of our application by passing in the app_id we were given when the application was registered. We can have any number of transactions associated with the application. In this case there's only one which we've called "HelloTran?". It's identifier is treurned as tran_id. As with applications, re-registering a transaction with the same parameters will return the same identifier.
As far as the ARM 4 agent is concerned, we've still got nothing to measure. These registrations give the application some idea about what's going to happen, but there's still no action.
Instantiation
We need instances of our applications or transactions. Registration only needs to happen once, but a new instance is created every time an application or transaction is begun.
/* Now start the application instance for the ARM agent. */ arm_start_application(&app_id, "Examples", ARM_STRING_NONE, ARM_FLAG_NONE, ARM_BUF4_NONE, &app_handle);
This starts the application. Typically, there will only be one application instance per program, although there may be cases where this isn't true (such as multi-threaded programs which may have an instance per thread.) Starting an application instance returns a handle, app_handle, which is required to perform operations on the application instance.
Next we create an instance of the transaction.
/* Now start the transaction measurement. */ arm_start_transaction(app_handle, &tran_id, ARM_CORR_NONE, ARM_FLAG_NONE, ARM_BUF4_NONE, &tran_handle, ARM_CORR_NONE);
This starts the clock. As far as the agent is concerned, work has now begun. Like the application instance, the transaction instance returns a handle, tran_handle, that is used to control it's operation. Unlike the application handle, it's expected to have many of these over the life of the program even though our example only has one.
Get to Work!
This is the focus of our instrumentation. While not part of our arm specification, this is what we are actually measuring.
printf("Hello world!\n");
Complete the transaction
Once the actual work has completed, we need to notify the agent that the transaction has completed and that the measurement should be taken.
/* Stop the measurement and commit the transaction to ARM. */ arm_stop_transaction(tran_handle, ARM_STATUS_GOOD, ARM_FLAG_NONE, ARM_BUF4_NONE);
As mentioned earlier, we use the tran_handle to tell the agent which transaction is completing. It's possible to have many transactions active concurrently so it's not enough to assume the last created will be the first to complete.
Clean Up
When all our transactions are completed and we're not going to create any further instanced, we need to tell the agent that our application instance is also complete.
/* Stop the application instance. */ arm_stop_application(app_handle, ARM_FLAG_NONE, ARM_BUF4_NONE);
Any transactions that were still in progress will be marked as aborted.
Finally, we should clean up after ourselves and release any resources allocated by the agent.
/* Destroy all registered metadata. */ arm_destroy_application(&app_id, ARM_FLAG_NONE, ARM_BUF4_NONE);
This typically won't wipe the definition from the database, but it may wipe it from memory.
Compiling, Linking, and Running
A typical command line to compile the program may look like:
$ cc -o arm4_hello arm4_hello.c -larm4
If you need to include paths, this may extend to:
$ cc -I/usr/local/include -L/usr/local/lib -o arm4_hello arm4_hello.c -larm4
You can now run the program, and you should see the same results as before. Except now you know how long it took!
$ ./arm4_hello Hello world!
