3.4. Using the Time-Stamp Counter (TSC) Register with Function Entry/Exit HooksΒΆ

The Time-Stamp Counter can be combined with the Function Entry/Exit Hooks feature available in the compiler to generate a quick profiler.

When the Entry/Exit hooks feature is enabled using the --entry_hook and --exit_hook options, the compiler inserts a call to an entry hook on entry to each function in the program. The compiler also inserts a call to an exit hook on exit of each function. Refer to the C7000 Optimizing C/C++ Compiler User's Guide (SPRUIG8) for details, and search for the section on Enabling Entry Hook and Exit Hook Functions.

The following example uses entry and exit hooks with the __TSC register to implement a simple profiler.

hook.h

 1// hook.h
 2
 3#ifndef __hook__
 4#define __hook__
 5
 6#include <stdint.h>
 7#include <c7x.h>
 8
 9// An enum to indicate if a timestamp is associated with
10// function entry or exit
11typedef enum { PD_ENTRY=0, PD_EXIT } PD_Mode;
12
13// Struct for data associated with a single timestamp
14typedef struct {
15    uint64_t function_address;
16    uint64_t timestamp;
17    PD_Mode  mode;
18} ProfileData;
19
20void __entry_hook(void (*addr)());
21void __entry_hook(void (*addr)());
22void ProfileData_init();
23void ProfileData_print();
24
25static inline uint64_t get_tsc()
26{
27    // The TSC (Time Stamp Counter) is a read-only register that
28    // is set to 0 on device reset and increments by 1 for each
29    // C7000 CPU clock cycle. It is readable in all supervisor
30    // and user modes.
31    return __TSC;
32}
33
34#endif /* __hook__ */

hook.cpp

 1// hook.cpp
 2
 3#include "hook.h"
 4#include <stdio.h>
 5
 6#define MAX_ENTRIES (64)
 7
 8// Array to store profile data
 9ProfileData table[MAX_ENTRIES];
10int         index = 0;
11
12// Entry hook function used to record cycle count on entry into function
13void __entry_hook(void (*addr)())
14{
15    if (index >= MAX_ENTRIES) return;
16    
17    table[index].function_address = (uint64_t) addr;
18    table[index].mode             = PD_ENTRY;
19    table[index].timestamp        = get_tsc();
20    index++;
21}
22
23// Exit hook function used to record cycle count on exit from function
24void __exit_hook(void (*addr)())
25{
26    if (index >= MAX_ENTRIES) return;
27    
28    table[index].timestamp        = get_tsc();
29    table[index].function_address = (uint64_t) addr;
30    table[index].mode             = PD_EXIT;
31    index++;
32}
33
34void ProfileData_init()
35{
36    for (int i = 0; i < MAX_ENTRIES; i++)
37    {
38        table[i].function_address = 0;
39        table[i].mode = PD_ENTRY;
40        table[i].timestamp = 0;
41    }
42}
43
44void ProfileData_print()
45{
46    for (int i = 0; i < MAX_ENTRIES; i++)
47        if (table[i].function_address != 0)
48        {
49            printf("0x%lx, %d, %lu\n",
50                   table[i].function_address,
51                   table[i].mode,
52                   table[i].timestamp);
53        }
54}

Files with functions that need to be profiled are compiled using the --entry_hook ``, ``--entry_parm=address, --exit_hook, and --exit_parm=address options.

When an application is built with the entry/exit hooks in this example, the table is populated with profile data. For example, execution of the code snippet below results in the following table being emitted:

main.cpp

 1// hook.cpp
 2
 3#include "hook.h"
 4#include <stdio.h>
 5
 6#define MAX_ENTRIES (64)
 7
 8// Array to store profile data
 9ProfileData table[MAX_ENTRIES];
10int         index = 0;
11
12// Entry hook function used to record cycle count on entry into function
13void __entry_hook(void (*addr)())
14{
15    if (index >= MAX_ENTRIES) return;
16    
17    table[index].function_address = (uint64_t) addr;
18    table[index].mode             = PD_ENTRY;
19    table[index].timestamp        = get_tsc();
20    index++;
21}
22
23// Exit hook function used to record cycle count on exit from function
24void __exit_hook(void (*addr)())
25{
26    if (index >= MAX_ENTRIES) return;
27    
28    table[index].timestamp        = get_tsc();
29    table[index].function_address = (uint64_t) addr;
30    table[index].mode             = PD_EXIT;
31    index++;
32}
33
34void ProfileData_init()
35{
36    for (int i = 0; i < MAX_ENTRIES; i++)
37    {
38        table[i].function_address = 0;
39        table[i].mode = PD_ENTRY;
40        table[i].timestamp = 0;
41    }
42}
43
44void ProfileData_print()
45{
46    for (int i = 0; i < MAX_ENTRIES; i++)
47        if (table[i].function_address != 0)
48        {
49            printf("0x%lx, %d, %lu\n",
50                   table[i].function_address,
51                   table[i].mode,
52                   table[i].timestamp);
53        }
54}

Table output:

0x7004, 0, 8100
0x6fc0, 0, 8516
0x6fc0, 1, 8931
0x6fc0, 0, 9061
0x6fc0, 1, 9476
0x7004, 1, 9603