Git fork

trace2: add global counter mechanism

Add global counters mechanism to Trace2.

The Trace2 counters mechanism adds the ability to create a set of
global counter variables and an API to increment them efficiently.
Counters can optionally report per-thread usage in addition to the sum
across all threads.

Counter events are emitted to the Trace2 logs when a thread exits and
at process exit.

Counters are an alternative to `data` and `data_json` events.

Counters are useful when you want to measure something across the life
of the process, when you don't want per-measurement events for
performance reasons, when the data does not fit conveniently within a
region, or when your control flow does not easily let you write the
final total. For example, you might use this to report the number of
calls to unzip() or the number of de-delta steps during a checkout.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jeff Hostetler and committed by
Junio C Hamano
81071626 8ad57564

+517 -7
+31
Documentation/technical/api-trace2.txt
··· 805 805 } 806 806 ------------ 807 807 808 + `"th_counter"`:: 809 + This event logs the value of a counter variable in a thread. 810 + This event is generated when a thread exits for counters that 811 + requested per-thread events. 812 + + 813 + ------------ 814 + { 815 + "event":"th_counter", 816 + ... 817 + "category":"my_category", 818 + "name":"my_counter", 819 + "count":23 820 + } 821 + ------------ 822 + 823 + `"counter"`:: 824 + This event logs the value of a counter variable across all threads. 825 + This event is generated when the process exits. The total value 826 + reported here is the sum across all threads. 827 + + 828 + ------------ 829 + { 830 + "event":"counter", 831 + ... 832 + "category":"my_category", 833 + "name":"my_counter", 834 + "count":23 835 + } 836 + ------------ 837 + 838 + 808 839 == Example Trace2 API Usage 809 840 810 841 Here is a hypothetical usage of the Trace2 API showing the intended
+1
Makefile
··· 1094 1094 LIB_OBJS += trace2.o 1095 1095 LIB_OBJS += trace2/tr2_cfg.o 1096 1096 LIB_OBJS += trace2/tr2_cmd_name.o 1097 + LIB_OBJS += trace2/tr2_ctr.o 1097 1098 LIB_OBJS += trace2/tr2_dst.o 1098 1099 LIB_OBJS += trace2/tr2_sid.o 1099 1100 LIB_OBJS += trace2/tr2_sysenv.o
+89
t/helper/test-trace2.c
··· 324 324 } 325 325 326 326 /* 327 + * Single-threaded counter test. Add several values to the TEST1 counter. 328 + * The test script can verify that the final sum is reported in the "counter" 329 + * event. 330 + */ 331 + static int ut_200counter(int argc, const char **argv) 332 + { 333 + const char *usage_error = 334 + "expect <v1> [<v2> [...]]"; 335 + int value; 336 + int k; 337 + 338 + if (argc < 1) 339 + die("%s", usage_error); 340 + 341 + for (k = 0; k < argc; k++) { 342 + if (get_i(&value, argv[k])) 343 + die("invalid value[%s] -- %s", 344 + argv[k], usage_error); 345 + trace2_counter_add(TRACE2_COUNTER_ID_TEST1, value); 346 + } 347 + 348 + return 0; 349 + } 350 + 351 + /* 352 + * Multi-threaded counter test. Create seveal threads that each increment 353 + * the TEST2 global counter. The test script can verify that an individual 354 + * "th_counter" event is generated with a partial sum for each thread and 355 + * that a final aggregate "counter" event is generated. 356 + */ 357 + 358 + struct ut_201_data { 359 + int v1; 360 + int v2; 361 + }; 362 + 363 + static void *ut_201counter_thread_proc(void *_ut_201_data) 364 + { 365 + struct ut_201_data *data = _ut_201_data; 366 + 367 + trace2_thread_start("ut_201"); 368 + 369 + trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v1); 370 + trace2_counter_add(TRACE2_COUNTER_ID_TEST2, data->v2); 371 + 372 + trace2_thread_exit(); 373 + return NULL; 374 + } 375 + 376 + static int ut_201counter(int argc, const char **argv) 377 + { 378 + const char *usage_error = 379 + "expect <v1> <v2> <threads>"; 380 + 381 + struct ut_201_data data = { 0, 0 }; 382 + int nr_threads = 0; 383 + int k; 384 + pthread_t *pids = NULL; 385 + 386 + if (argc != 3) 387 + die("%s", usage_error); 388 + if (get_i(&data.v1, argv[0])) 389 + die("%s", usage_error); 390 + if (get_i(&data.v2, argv[1])) 391 + die("%s", usage_error); 392 + if (get_i(&nr_threads, argv[2])) 393 + die("%s", usage_error); 394 + 395 + CALLOC_ARRAY(pids, nr_threads); 396 + 397 + for (k = 0; k < nr_threads; k++) { 398 + if (pthread_create(&pids[k], NULL, ut_201counter_thread_proc, &data)) 399 + die("failed to create thread[%d]", k); 400 + } 401 + 402 + for (k = 0; k < nr_threads; k++) { 403 + if (pthread_join(pids[k], NULL)) 404 + die("failed to join thread[%d]", k); 405 + } 406 + 407 + free(pids); 408 + 409 + return 0; 410 + } 411 + 412 + /* 327 413 * Usage: 328 414 * test-tool trace2 <ut_name_1> <ut_usage_1> 329 415 * test-tool trace2 <ut_name_2> <ut_usage_2> ··· 346 432 347 433 { ut_100timer, "100timer", "<count> <ms_delay>" }, 348 434 { ut_101timer, "101timer", "<count> <ms_delay> <threads>" }, 435 + 436 + { ut_200counter, "200counter", "<v1> [<v2> [<v3> [...]]]" }, 437 + { ut_201counter, "201counter", "<v1> <v2> <threads>" }, 349 438 }; 350 439 /* clang-format on */ 351 440
+46
t/t0211-trace2-perf.sh
··· 222 222 have_timer_event "main" "timer" "test" "test2" 15 actual 223 223 ' 224 224 225 + # Exercise the global counters and confirm that we get the expected values. 226 + # 227 + # The counter "test/test1" should only emit a global summary "counter" event. 228 + # The counter "test/test2" could emit per-thread "th_counter" events and a 229 + # global summary "counter" event. 230 + 231 + have_counter_event () { 232 + thread=$1 event=$2 category=$3 name=$4 value=$5 file=$6 && 233 + 234 + pattern="d0|${thread}|${event}||||${category}|name:${name} value:${value}" && 235 + 236 + grep "${patern}" ${file} 237 + } 238 + 239 + test_expect_success 'global counter test/test1' ' 240 + test_when_finished "rm trace.perf actual" && 241 + test_config_global trace2.perfBrief 1 && 242 + test_config_global trace2.perfTarget "$(pwd)/trace.perf" && 243 + 244 + # Use the counter "test1" and add n integers. 245 + test-tool trace2 200counter 1 2 3 4 5 && 246 + 247 + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && 248 + 249 + have_counter_event "main" "counter" "test" "test1" 15 actual 250 + ' 251 + 252 + test_expect_success 'global counter test/test2' ' 253 + test_when_finished "rm trace.perf actual" && 254 + test_config_global trace2.perfBrief 1 && 255 + test_config_global trace2.perfTarget "$(pwd)/trace.perf" && 256 + 257 + # Add 2 integers to the counter "test2" in each of 3 threads. 258 + test-tool trace2 201counter 7 13 3 && 259 + 260 + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual && 261 + 262 + # So we should have 3 per-thread events of 5 each. 263 + have_counter_event "th01:ut_201" "th_counter" "test" "test2" 20 actual && 264 + have_counter_event "th02:ut_201" "th_counter" "test" "test2" 20 actual && 265 + have_counter_event "th03:ut_201" "th_counter" "test" "test2" 20 actual && 266 + 267 + # And we should have a single event with the total across all threads. 268 + have_counter_event "main" "counter" "test" "test2" 60 actual 269 + ' 270 + 225 271 test_done
+45 -7
trace2.c
··· 8 8 #include "version.h" 9 9 #include "trace2/tr2_cfg.h" 10 10 #include "trace2/tr2_cmd_name.h" 11 + #include "trace2/tr2_ctr.h" 11 12 #include "trace2/tr2_dst.h" 12 13 #include "trace2/tr2_sid.h" 13 14 #include "trace2/tr2_sysenv.h" ··· 101 102 tgt_j->pfn_timer(meta, timer, is_final_data); 102 103 } 103 104 105 + /* 106 + * The signature of this function must match the pfn_counter 107 + * method in the targets. 108 + */ 109 + static void tr2_tgt_emit_a_counter(const struct tr2_counter_metadata *meta, 110 + const struct tr2_counter *counter, 111 + int is_final_data) 112 + { 113 + struct tr2_tgt *tgt_j; 114 + int j; 115 + 116 + for_each_wanted_builtin (j, tgt_j) 117 + if (tgt_j->pfn_counter) 118 + tgt_j->pfn_counter(meta, counter, is_final_data); 119 + } 120 + 104 121 static int tr2main_exit_code; 105 122 106 123 /* ··· 132 149 * Some timers want per-thread details. If the main thread 133 150 * used one of those timers, emit the details now (before 134 151 * we emit the aggregate timer values). 152 + * 153 + * Likewise for counters. 135 154 */ 136 155 tr2_emit_per_thread_timers(tr2_tgt_emit_a_timer); 156 + tr2_emit_per_thread_counters(tr2_tgt_emit_a_counter); 137 157 138 158 /* 139 - * Add stopwatch timer data for the main thread to the final 140 - * totals. And then emit the final timer values. 159 + * Add stopwatch timer and counter data for the main thread to 160 + * the final totals. And then emit the final values. 141 161 * 142 162 * Technically, we shouldn't need to hold the lock to update 143 - * and output the final_timer_block (since all other threads 144 - * should be dead by now), but it doesn't hurt anything. 163 + * and output the final_timer_block and final_counter_block 164 + * (since all other threads should be dead by now), but it 165 + * doesn't hurt anything. 145 166 */ 146 167 tr2tls_lock(); 147 168 tr2_update_final_timers(); 169 + tr2_update_final_counters(); 148 170 tr2_emit_final_timers(tr2_tgt_emit_a_timer); 171 + tr2_emit_final_counters(tr2_tgt_emit_a_counter); 149 172 tr2tls_unlock(); 150 173 151 174 for_each_wanted_builtin (j, tgt_j) ··· 582 605 /* 583 606 * Some timers want per-thread details. If this thread used 584 607 * one of those timers, emit the details now. 608 + * 609 + * Likewise for counters. 585 610 */ 586 611 tr2_emit_per_thread_timers(tr2_tgt_emit_a_timer); 612 + tr2_emit_per_thread_counters(tr2_tgt_emit_a_counter); 587 613 588 614 /* 589 - * Add stopwatch timer data from the current (non-main) thread 590 - * to the final totals. (We'll accumulate data for the main 591 - * thread later during "atexit".) 615 + * Add stopwatch timer and counter data from the current 616 + * (non-main) thread to the final totals. (We'll accumulate 617 + * data for the main thread later during "atexit".) 592 618 */ 593 619 tr2tls_lock(); 594 620 tr2_update_final_timers(); 621 + tr2_update_final_counters(); 595 622 tr2tls_unlock(); 596 623 597 624 for_each_wanted_builtin (j, tgt_j) ··· 868 895 BUG("trace2_timer_stop: invalid timer id: %d", tid); 869 896 870 897 tr2_stop_timer(tid); 898 + } 899 + 900 + void trace2_counter_add(enum trace2_counter_id cid, uint64_t value) 901 + { 902 + if (!trace2_enabled) 903 + return; 904 + 905 + if (cid < 0 || cid >= TRACE2_NUMBER_OF_COUNTERS) 906 + BUG("trace2_counter_add: invalid counter id: %d", cid); 907 + 908 + tr2_counter_increment(cid, value); 871 909 } 872 910 873 911 const char *trace2_session_id(void)
+37
trace2.h
··· 52 52 * [] trace2_data* -- emit region/thread/repo data messages. 53 53 * [] trace2_printf* -- legacy trace[1] messages. 54 54 * [] trace2_timer* -- stopwatch timers (messages are deferred). 55 + * [] trace2_counter* -- global counters (messages are deferred). 55 56 */ 56 57 57 58 /* ··· 527 528 */ 528 529 void trace2_timer_start(enum trace2_timer_id tid); 529 530 void trace2_timer_stop(enum trace2_timer_id tid); 531 + 532 + /* 533 + * Define the set of global counters. 534 + * 535 + * We can add more at any time, but they must be defined at compile 536 + * time (to avoid the need to dynamically allocate and synchronize 537 + * them between different threads). 538 + * 539 + * These must start at 0 and be contiguous (because we use them 540 + * elsewhere as array indexes). 541 + * 542 + * Any values added to this enum be also be added to the 543 + * `tr2_counter_metadata[]` in `trace2/tr2_tr2_ctr.c`. 544 + */ 545 + enum trace2_counter_id { 546 + /* 547 + * Define two counters for testing. See `t/helper/test-trace2.c`. 548 + * These can be used for ad hoc testing, but should not be used 549 + * for permanent analysis code. 550 + */ 551 + TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */ 552 + TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */ 553 + 554 + /* Add additional counter definitions before here. */ 555 + TRACE2_NUMBER_OF_COUNTERS 556 + }; 557 + 558 + /* 559 + * Increase the named global counter by value. 560 + * 561 + * Note that this adds `value` to the current thread's partial sum for 562 + * this counter (without locking) and that the complete sum is not 563 + * available until all threads have exited, so it does not return the 564 + * new value of the counter. 565 + */ 566 + void trace2_counter_add(enum trace2_counter_id cid, uint64_t value); 530 567 531 568 /* 532 569 * Optional platform-specific code to dump information about the
+101
trace2/tr2_ctr.c
··· 1 + #include "cache.h" 2 + #include "thread-utils.h" 3 + #include "trace2/tr2_tgt.h" 4 + #include "trace2/tr2_tls.h" 5 + #include "trace2/tr2_ctr.h" 6 + 7 + /* 8 + * A global counter block to aggregrate values from the partial sums 9 + * from each thread. 10 + */ 11 + static struct tr2_counter_block final_counter_block; /* access under tr2tls_mutex */ 12 + 13 + /* 14 + * Define metadata for each global counter. 15 + * 16 + * This array must match the "enum trace2_counter_id" and the values 17 + * in "struct tr2_counter_block.counter[*]". 18 + */ 19 + static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTERS] = { 20 + [TRACE2_COUNTER_ID_TEST1] = { 21 + .category = "test", 22 + .name = "test1", 23 + .want_per_thread_events = 0, 24 + }, 25 + [TRACE2_COUNTER_ID_TEST2] = { 26 + .category = "test", 27 + .name = "test2", 28 + .want_per_thread_events = 1, 29 + }, 30 + 31 + /* Add additional metadata before here. */ 32 + }; 33 + 34 + void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value) 35 + { 36 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 37 + struct tr2_counter *c = &ctx->counter_block.counter[cid]; 38 + 39 + c->value += value; 40 + 41 + ctx->used_any_counter = 1; 42 + if (tr2_counter_metadata[cid].want_per_thread_events) 43 + ctx->used_any_per_thread_counter = 1; 44 + } 45 + 46 + void tr2_update_final_counters(void) 47 + { 48 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 49 + enum trace2_counter_id cid; 50 + 51 + if (!ctx->used_any_counter) 52 + return; 53 + 54 + /* 55 + * Access `final_counter_block` requires holding `tr2tls_mutex`. 56 + * We assume that our caller is holding the lock. 57 + */ 58 + 59 + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) { 60 + struct tr2_counter *c_final = &final_counter_block.counter[cid]; 61 + const struct tr2_counter *c = &ctx->counter_block.counter[cid]; 62 + 63 + c_final->value += c->value; 64 + } 65 + } 66 + 67 + void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply) 68 + { 69 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 70 + enum trace2_counter_id cid; 71 + 72 + if (!ctx->used_any_per_thread_counter) 73 + return; 74 + 75 + /* 76 + * For each counter, if the counter wants per-thread events 77 + * and this thread used it (the value is non-zero), emit it. 78 + */ 79 + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) 80 + if (tr2_counter_metadata[cid].want_per_thread_events && 81 + ctx->counter_block.counter[cid].value) 82 + fn_apply(&tr2_counter_metadata[cid], 83 + &ctx->counter_block.counter[cid], 84 + 0); 85 + } 86 + 87 + void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply) 88 + { 89 + enum trace2_counter_id cid; 90 + 91 + /* 92 + * Access `final_counter_block` requires holding `tr2tls_mutex`. 93 + * We assume that our caller is holding the lock. 94 + */ 95 + 96 + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) 97 + if (final_counter_block.counter[cid].value) 98 + fn_apply(&tr2_counter_metadata[cid], 99 + &final_counter_block.counter[cid], 100 + 1); 101 + }
+104
trace2/tr2_ctr.h
··· 1 + #ifndef TR2_CTR_H 2 + #define TR2_CTR_H 3 + 4 + #include "trace2.h" 5 + #include "trace2/tr2_tgt.h" 6 + 7 + /* 8 + * Define a mechanism to allow global "counters". 9 + * 10 + * Counters can be used count interesting activity that does not fit 11 + * the "region and data" model, such as code called from many 12 + * different regions and/or where you want to count a number of items, 13 + * but don't have control of when the last item will be processed, 14 + * such as counter the number of calls to `lstat()`. 15 + * 16 + * Counters differ from Trace2 "data" events. Data events are emitted 17 + * immediately and are appropriate for documenting loop counters at 18 + * the end of a region, for example. Counter values are accumulated 19 + * during the program and final counter values are emitted at program 20 + * exit. 21 + * 22 + * To make this model efficient, we define a compile-time fixed set of 23 + * counters and counter ids using a fixed size "counter block" array 24 + * in thread-local storage. This gives us constant time, lock-free 25 + * access to each counter within each thread. This lets us avoid the 26 + * complexities of dynamically allocating a counter and sharing that 27 + * definition with other threads. 28 + * 29 + * Each thread uses the counter block in its thread-local storage to 30 + * increment partial sums for each counter (without locking). When a 31 + * thread exits, those partial sums are (under lock) added to the 32 + * global final sum. 33 + * 34 + * Partial sums for each counter are optionally emitted when a thread 35 + * exits. 36 + * 37 + * Final sums for each counter are emitted between the "exit" and 38 + * "atexit" events. 39 + * 40 + * A parallel "counter metadata" table contains the "category" and 41 + * "name" fields for each counter. This eliminates the need to 42 + * include those args in the various counter APIs. 43 + */ 44 + 45 + /* 46 + * The definition of an individual counter as used by an individual 47 + * thread (and later in aggregation). 48 + */ 49 + struct tr2_counter { 50 + uint64_t value; 51 + }; 52 + 53 + /* 54 + * Metadata for a counter. 55 + */ 56 + struct tr2_counter_metadata { 57 + const char *category; 58 + const char *name; 59 + 60 + /* 61 + * True if we should emit per-thread events for this counter 62 + * when individual threads exit. 63 + */ 64 + unsigned int want_per_thread_events:1; 65 + }; 66 + 67 + /* 68 + * A compile-time fixed block of counters to insert into thread-local 69 + * storage. This wrapper is used to avoid quirks of C and the usual 70 + * need to pass an array size argument. 71 + */ 72 + struct tr2_counter_block { 73 + struct tr2_counter counter[TRACE2_NUMBER_OF_COUNTERS]; 74 + }; 75 + 76 + /* 77 + * Private routines used by trace2.c to increment a counter for the 78 + * current thread. 79 + */ 80 + void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value); 81 + 82 + /* 83 + * Add the current thread's counter data to the global totals. 84 + * This is called during thread-exit. 85 + * 86 + * Caller must be holding the tr2tls_mutex. 87 + */ 88 + void tr2_update_final_counters(void); 89 + 90 + /* 91 + * Emit per-thread counter data for the current thread. 92 + * This is called during thread-exit. 93 + */ 94 + void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply); 95 + 96 + /* 97 + * Emit global counter values. 98 + * This is called during atexit handling. 99 + * 100 + * Caller must be holding the tr2tls_mutex. 101 + */ 102 + void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply); 103 + 104 + #endif /* TR2_CTR_H */
+7
trace2/tr2_tgt.h
··· 6 6 struct json_writer; 7 7 struct tr2_timer_metadata; 8 8 struct tr2_timer; 9 + struct tr2_counter_metadata; 10 + struct tr2_counter; 9 11 10 12 #define NS_TO_SEC(ns) ((double)(ns) / 1.0e9) 11 13 ··· 104 106 const struct tr2_timer *timer, 105 107 int is_final_data); 106 108 109 + typedef void(tr2_tgt_evt_counter_t)(const struct tr2_counter_metadata *meta, 110 + const struct tr2_counter *counter, 111 + int is_final_data); 112 + 107 113 /* 108 114 * "vtable" for a TRACE2 target. Use NULL if a target does not want 109 115 * to emit that message. ··· 141 147 tr2_tgt_evt_data_json_fl_t *pfn_data_json_fl; 142 148 tr2_tgt_evt_printf_va_fl_t *pfn_printf_va_fl; 143 149 tr2_tgt_evt_timer_t *pfn_timer; 150 + tr2_tgt_evt_counter_t *pfn_counter; 144 151 }; 145 152 /* clang-format on */ 146 153
+19
trace2/tr2_tgt_event.c
··· 642 642 jw_release(&jw); 643 643 } 644 644 645 + static void fn_counter(const struct tr2_counter_metadata *meta, 646 + const struct tr2_counter *counter, 647 + int is_final_data) 648 + { 649 + const char *event_name = is_final_data ? "counter" : "th_counter"; 650 + struct json_writer jw = JSON_WRITER_INIT; 651 + 652 + jw_object_begin(&jw, 0); 653 + event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); 654 + jw_object_string(&jw, "category", meta->category); 655 + jw_object_string(&jw, "name", meta->name); 656 + jw_object_intmax(&jw, "count", counter->value); 657 + jw_end(&jw); 658 + 659 + tr2_dst_write_line(&tr2dst_event, &jw.json); 660 + jw_release(&jw); 661 + } 662 + 645 663 struct tr2_tgt tr2_tgt_event = { 646 664 .pdst = &tr2dst_event, 647 665 ··· 674 692 .pfn_data_json_fl = fn_data_json_fl, 675 693 .pfn_printf_va_fl = NULL, 676 694 .pfn_timer = fn_timer, 695 + .pfn_counter = fn_counter, 677 696 };
+16
trace2/tr2_tgt_normal.c
··· 351 351 strbuf_release(&buf_payload); 352 352 } 353 353 354 + static void fn_counter(const struct tr2_counter_metadata *meta, 355 + const struct tr2_counter *counter, 356 + int is_final_data) 357 + { 358 + const char *event_name = is_final_data ? "counter" : "th_counter"; 359 + struct strbuf buf_payload = STRBUF_INIT; 360 + 361 + strbuf_addf(&buf_payload, "%s %s/%s value:%"PRIu64, 362 + event_name, meta->category, meta->name, 363 + counter->value); 364 + 365 + normal_io_write_fl(__FILE__, __LINE__, &buf_payload); 366 + strbuf_release(&buf_payload); 367 + } 368 + 354 369 struct tr2_tgt tr2_tgt_normal = { 355 370 .pdst = &tr2dst_normal, 356 371 ··· 383 398 .pfn_data_json_fl = NULL, 384 399 .pfn_printf_va_fl = fn_printf_va_fl, 385 400 .pfn_timer = fn_timer, 401 + .pfn_counter = fn_counter, 386 402 };
+17
trace2/tr2_tgt_perf.c
··· 578 578 strbuf_release(&buf_payload); 579 579 } 580 580 581 + static void fn_counter(const struct tr2_counter_metadata *meta, 582 + const struct tr2_counter *counter, 583 + int is_final_data) 584 + { 585 + const char *event_name = is_final_data ? "counter" : "th_counter"; 586 + struct strbuf buf_payload = STRBUF_INIT; 587 + 588 + strbuf_addf(&buf_payload, "name:%s value:%"PRIu64, 589 + meta->name, 590 + counter->value); 591 + 592 + perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, NULL, NULL, 593 + meta->category, &buf_payload); 594 + strbuf_release(&buf_payload); 595 + } 596 + 581 597 struct tr2_tgt tr2_tgt_perf = { 582 598 .pdst = &tr2dst_perf, 583 599 ··· 610 626 .pfn_data_json_fl = fn_data_json_fl, 611 627 .pfn_printf_va_fl = fn_printf_va_fl, 612 628 .pfn_timer = fn_timer, 629 + .pfn_counter = fn_counter, 613 630 };
+4
trace2/tr2_tls.h
··· 2 2 #define TR2_TLS_H 3 3 4 4 #include "strbuf.h" 5 + #include "trace2/tr2_ctr.h" 5 6 #include "trace2/tr2_tmr.h" 6 7 7 8 /* ··· 22 23 size_t nr_open_regions; /* plays role of "nr" in ALLOC_GROW */ 23 24 int thread_id; 24 25 struct tr2_timer_block timer_block; 26 + struct tr2_counter_block counter_block; 25 27 unsigned int used_any_timer:1; 26 28 unsigned int used_any_per_thread_timer:1; 29 + unsigned int used_any_counter:1; 30 + unsigned int used_any_per_thread_counter:1; 27 31 }; 28 32 29 33 /*