Skip to content

Commit

Permalink
trace2: add global counters
Browse files Browse the repository at this point in the history
Add global counters to Trace2.

Create a mechanism in Trace2 to count an activity and emit a single
"counter" event at the end of the program.  This is an alternative
to the existing "data" events that are emitted immediately.

Create an array of counters (indexed by `enum trace2_counter_id`)
to allow various activites to be tracked as desired.

Preload the array with two counters for testing purposes.

Create unit tests to demonstrate and verify.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
  • Loading branch information
jeffhostetler committed Dec 22, 2021
1 parent c1f5916 commit f5f7086
Show file tree
Hide file tree
Showing 11 changed files with 506 additions and 1 deletion.
67 changes: 67 additions & 0 deletions Documentation/technical/api-trace2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,73 @@ d0 | main | atexit | | 0.111026 | |
In this example, the "test1" timer was started 4 times and ran for
0.000393 seconds.

Counter Events::

Trace2 also provides global counters as an alternative to regions
and data events. These make it possible to count an activity of
interest, such a call to a library routine, during the program
and get a single counter event at the end.
+
At the end of the program, a single summary event is emitted; this
value is aggregated across all threads. These events have "summary"
as their thread name.
+
For some counters, individual (per-thread) counter events are also
generated. This may be helpful in understanding how work is balanced
between threads in some circumstances.
+
----------------
static void *load_cache_entries_thread(void *_data)
{
struct load_cache_entries_thread_data *p = _data;
int i;

trace2_thread_start("load_cache_entries");
...
trace2_thread_exit();
}

static unsigned long load_cache_entry_block(struct index_state *istate,
struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
unsigned long start_offset, const struct cache_entry *previous_ce)
{
int i;
unsigned long src_offset = start_offset;

trace2_counter_add(TRACE2_COUNTER_ID_TEST2, nr);

for (i = offset; i < offset + nr; i++) {
...
}
}
----------------
+
----------------
$ export GIT_TRACE2_PERF_BRIEF=1
$ export GIT_TRACE2_PERF=~/log.perf
$ git status
...
$ cat ~/log.perf
...
d0 | main | exit | | 53.977680 | | | code:0
d0 | th12:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193220
d0 | th11:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th10:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th09:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th08:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th07:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th06:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th05:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th04:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th03:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | th02:load_cache_entries | counter | | 53.977708 | | test | name:test2 value:193221
d0 | summary | counter | | 53.977708 | | test | name:test2 value:2125430
d0 | main | atexit | | 53.977708 | | | code:0
----------------
+
This example shows the value computed by each of the 11
`load_cache_entries` threads and the total across all threads.

== Future Work

=== Relationship to the Existing Trace Api (api-trace.txt)
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ LIB_OBJS += trace.o
LIB_OBJS += trace2.o
LIB_OBJS += trace2/tr2_cfg.o
LIB_OBJS += trace2/tr2_cmd_name.o
LIB_OBJS += trace2/tr2_ctr.o
LIB_OBJS += trace2/tr2_dst.o
LIB_OBJS += trace2/tr2_sid.o
LIB_OBJS += trace2/tr2_tmr.o
Expand Down
88 changes: 87 additions & 1 deletion t/helper/test-trace2.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ static int ut_009timer(int argc, const char **argv)
const char *usage_error =
"expect <count> <ms_delay> <threads>";

struct ut_009_data data = { 0, 0 };
struct ut_009_data data = { 0 };
int nr_threads = 0;
int k;
pthread_t *pids = NULL;
Expand Down Expand Up @@ -301,6 +301,90 @@ static int ut_009timer(int argc, const char **argv)
return 0;
}

/*
* Single-threaded counter test. Add several values to the TEST1 counter.
* The test script can verify that an aggregate Trace2 "counter" event is
* emitted containing the sum of the values provided.
*/
static int ut_010counter(int argc, const char **argv)
{
const char *usage_error =
"expect <v1> [<v2> [...]]";
int value;
int k;

if (argc < 1)
die("%s", usage_error);

for (k = 0; k < argc; k++) {
if (get_i(&value, argv[k]))
die("invalid value[%s] -- %s",
argv[k], usage_error);
trace2_counter_add(TRACE2_COUNTER_ID_TEST1, value);
}

return 0;
}

struct ut_011_data {
int v1, v2;
};

static void *ut_011counter_thread_proc(void *_ut_011_data)
{
struct ut_011_data *data = _ut_011_data;

trace2_thread_start("ut_011");

trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v1);
trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v2);

trace2_thread_exit();
return NULL;
}

/*
* Multi-threaded counter test. Create several threads that each
* increment the TEST2 global counter. The test script can verify
* that an individual Trace2 "counter" event for each thread and an
* aggregate "counter" event are generated.
*/
static int ut_011counter(int argc, const char **argv)
{
const char *usage_error =
"expect <v1> <v2> <threads>";

struct ut_011_data data = { 0, 0 };
int nr_threads = 0;
int k;
pthread_t *pids = NULL;

if (argc != 3)
die("%s", usage_error);
if (get_i(&data.v1, argv[0]))
die("%s", usage_error);
if (get_i(&data.v2, argv[1]))
die("%s", usage_error);
if (get_i(&nr_threads, argv[2]))
die("%s", usage_error);

CALLOC_ARRAY(pids, nr_threads);

for (k = 0; k < nr_threads; k++) {
if (pthread_create(&pids[k], NULL, ut_011counter_thread_proc, &data))
die("failed to create thread[%d]", k);
}

for (k = 0; k < nr_threads; k++) {
if (pthread_join(pids[k], NULL))
die("failed to join thread[%d]", k);
}

free(pids);

return 0;
}

/*
* Usage:
* test-tool trace2 <ut_name_1> <ut_usage_1>
Expand All @@ -320,6 +404,8 @@ static struct unit_test ut_table[] = {
{ ut_007bug, "007bug", "" },
{ ut_008timer, "008timer", "<count> <ms_delay>" },
{ ut_009timer, "009timer", "<count> <ms_delay> <threads>" },
{ ut_010counter, "010counter","<v1> [<v2> [<v3> [...]]]" },
{ ut_011counter, "011counter","<v1> <v2> <threads>" },
};
/* clang-format on */

Expand Down
42 changes: 42 additions & 0 deletions t/t0211-trace2-perf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,46 @@ test_expect_success 'test stopwatch timers - summary and threads' '
have_timer_event "summary" "test2" 15 actual
'

# Exercise the global counter "test" in a loop and confirm that we get an
# event with the sum.
#

have_counter_event () {
thread=$1
name=$2
value=$3
file=$4

pattern="d0|${thread}|counter||_T_ABS_||test"
pattern="${pattern}|name:${name}"
pattern="${pattern} value:${value}"

grep "${pattern}" ${file}

return $?
}

test_expect_success 'test global counters - global, single-thead' '
test_when_finished "rm trace.perf actual" &&
test_config_global trace2.perfBrief 1 &&
test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
test-tool trace2 010counter 2 3 5 7 11 13 &&
perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
have_counter_event "summary" "test1" 41 actual
'

test_expect_success 'test global counters - global+threads' '
test_when_finished "rm trace.perf actual" &&
test_config_global trace2.perfBrief 1 &&
test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
test-tool trace2 011counter 5 10 3 &&
perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
have_counter_event "th01:ut_011" "test2" 15 actual &&
have_counter_event "th02:ut_011" "test2" 15 actual &&
have_counter_event "th03:ut_011" "test2" 15 actual &&
have_counter_event "summary" "test2" 45 actual
'

test_done
41 changes: 41 additions & 0 deletions t/t0212-trace2-event.sh
Original file line number Diff line number Diff line change
Expand Up @@ -368,4 +368,45 @@ test_expect_success 'test stopwatch timers - global+threads' '
have_timer_event "summary" "test2" 15 trace.event
'

# Exercise the global counter in a loop and confirm that we get the
# expected sum in an event record.
#

have_counter_event () {
thread=$1
name=$2
value=$3
file=$4

pattern="\"event\":\"counter\""
pattern="${pattern}.*\"thread\":\"${thread}\""
pattern="${pattern}.*\"name\":\"${name}\""
pattern="${pattern}.*\"value\":${value}"

grep "${pattern}" ${file}

return $?
}

test_expect_success 'test global counter - global, single-thread' '
test_when_finished "rm trace.event" &&
test_config_global trace2.eventBrief 1 &&
test_config_global trace2.eventTarget "$(pwd)/trace.event" &&
test-tool trace2 010counter 2 3 5 7 11 13 &&
have_counter_event "summary" "test1" 41 trace.event
'

test_expect_success 'test global counter - global+threads' '
test_when_finished "rm trace.event" &&
test_config_global trace2.eventBrief 1 &&
test_config_global trace2.eventTarget "$(pwd)/trace.event" &&
test-tool trace2 011counter 5 10 3 &&
have_counter_event "th01:ut_011" "test2" 15 trace.event &&
have_counter_event "th02:ut_011" "test2" 15 trace.event &&
have_counter_event "th03:ut_011" "test2" 15 trace.event &&
have_counter_event "summary" "test2" 45 trace.event
'

test_done
50 changes: 50 additions & 0 deletions trace2.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "version.h"
#include "trace2/tr2_cfg.h"
#include "trace2/tr2_cmd_name.h"
#include "trace2/tr2_ctr.h"
#include "trace2/tr2_dst.h"
#include "trace2/tr2_sid.h"
#include "trace2/tr2_sysenv.h"
Expand Down Expand Up @@ -118,6 +119,41 @@ static void tr2main_emit_thread_timers(uint64_t us_elapsed_absolute)
us_elapsed_absolute);
}

static void tr2main_emit_summary_counters(uint64_t us_elapsed_absolute)
{
struct tr2_tgt *tgt_j;
int j;
struct tr2ctr_block merged = { { { 0 } } };

/*
* Sum across all of the per-thread counter data into
* a single composite block of counter values.
*/
tr2tls_aggregate_counter_blocks(&merged);

/*
* Emit "summary" counter events for each composite counter value
* that had activity.
*/
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_counter)
tr2ctr_emit_block(tgt_j->pfn_counter,
us_elapsed_absolute,
&merged, "summary");
}

static void tr2main_emit_thread_counters(uint64_t us_elapsed_absolute)
{
struct tr2_tgt *tgt_j;
int j;

for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_counter)
tr2tls_emit_counter_blocks_by_thread(
tgt_j->pfn_counter,
us_elapsed_absolute);
}

static int tr2main_exit_code;

/*
Expand Down Expand Up @@ -148,6 +184,9 @@ static void tr2main_atexit_handler(void)
tr2main_emit_thread_timers(us_elapsed_absolute);
tr2main_emit_summary_timers(us_elapsed_absolute);

tr2main_emit_thread_counters(us_elapsed_absolute);
tr2main_emit_summary_counters(us_elapsed_absolute);

for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_atexit)
tgt_j->pfn_atexit(us_elapsed_absolute,
Expand Down Expand Up @@ -901,3 +940,14 @@ void trace2_timer_stop(enum trace2_timer_id tid)

tr2tmr_stop(tid);
}

void trace2_counter_add(enum trace2_counter_id cid, uint64_t value)
{
if (!trace2_enabled)
return;

if (cid < 0 || cid >= TRACE2_NUMBER_OF_COUNTERS)
BUG("invalid counter id: %d", cid);

tr2ctr_add(cid, value);
}

0 comments on commit f5f7086

Please sign in to comment.