/src/git/trace2/tr2_tmr.c
Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | #include "trace2/tr2_tgt.h" |
3 | | #include "trace2/tr2_tls.h" |
4 | | #include "trace2/tr2_tmr.h" |
5 | | #include "trace.h" |
6 | | |
7 | 0 | #define MY_MAX(a, b) ((a) > (b) ? (a) : (b)) |
8 | 0 | #define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) |
9 | | |
10 | | /* |
11 | | * A global timer block to aggregate values from the partial sums from |
12 | | * each thread. |
13 | | */ |
14 | | static struct tr2_timer_block final_timer_block; /* access under tr2tls_mutex */ |
15 | | |
16 | | /* |
17 | | * Define metadata for each stopwatch timer. |
18 | | * |
19 | | * This array must match "enum trace2_timer_id" and the values |
20 | | * in "struct tr2_timer_block.timer[*]". |
21 | | */ |
22 | | static struct tr2_timer_metadata tr2_timer_metadata[TRACE2_NUMBER_OF_TIMERS] = { |
23 | | [TRACE2_TIMER_ID_TEST1] = { |
24 | | .category = "test", |
25 | | .name = "test1", |
26 | | .want_per_thread_events = 0, |
27 | | }, |
28 | | [TRACE2_TIMER_ID_TEST2] = { |
29 | | .category = "test", |
30 | | .name = "test2", |
31 | | .want_per_thread_events = 1, |
32 | | }, |
33 | | |
34 | | /* Add additional metadata before here. */ |
35 | | }; |
36 | | |
37 | | void tr2_start_timer(enum trace2_timer_id tid) |
38 | 0 | { |
39 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
40 | 0 | struct tr2_timer *t = &ctx->timer_block.timer[tid]; |
41 | |
|
42 | 0 | t->recursion_count++; |
43 | 0 | if (t->recursion_count > 1) |
44 | 0 | return; /* ignore recursive starts */ |
45 | | |
46 | 0 | t->start_ns = getnanotime(); |
47 | 0 | } |
48 | | |
49 | | void tr2_stop_timer(enum trace2_timer_id tid) |
50 | 0 | { |
51 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
52 | 0 | struct tr2_timer *t = &ctx->timer_block.timer[tid]; |
53 | 0 | uint64_t ns_now; |
54 | 0 | uint64_t ns_interval; |
55 | |
|
56 | 0 | assert(t->recursion_count > 0); |
57 | |
|
58 | 0 | t->recursion_count--; |
59 | 0 | if (t->recursion_count) |
60 | 0 | return; /* still in recursive call(s) */ |
61 | | |
62 | 0 | ns_now = getnanotime(); |
63 | 0 | ns_interval = ns_now - t->start_ns; |
64 | |
|
65 | 0 | t->total_ns += ns_interval; |
66 | | |
67 | | /* |
68 | | * min_ns was initialized to zero (in the xcalloc()) rather |
69 | | * than UINT_MAX when the block of timers was allocated, |
70 | | * so we should always set both the min_ns and max_ns values |
71 | | * the first time that the timer is used. |
72 | | */ |
73 | 0 | if (!t->interval_count) { |
74 | 0 | t->min_ns = ns_interval; |
75 | 0 | t->max_ns = ns_interval; |
76 | 0 | } else { |
77 | 0 | t->min_ns = MY_MIN(ns_interval, t->min_ns); |
78 | 0 | t->max_ns = MY_MAX(ns_interval, t->max_ns); |
79 | 0 | } |
80 | |
|
81 | 0 | t->interval_count++; |
82 | |
|
83 | 0 | ctx->used_any_timer = 1; |
84 | 0 | if (tr2_timer_metadata[tid].want_per_thread_events) |
85 | 0 | ctx->used_any_per_thread_timer = 1; |
86 | 0 | } |
87 | | |
88 | | void tr2_update_final_timers(void) |
89 | 0 | { |
90 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
91 | 0 | enum trace2_timer_id tid; |
92 | |
|
93 | 0 | if (!ctx->used_any_timer) |
94 | 0 | return; |
95 | | |
96 | | /* |
97 | | * Accessing `final_timer_block` requires holding `tr2tls_mutex`. |
98 | | * We assume that our caller is holding the lock. |
99 | | */ |
100 | | |
101 | 0 | for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) { |
102 | 0 | struct tr2_timer *t_final = &final_timer_block.timer[tid]; |
103 | 0 | struct tr2_timer *t = &ctx->timer_block.timer[tid]; |
104 | | |
105 | | /* |
106 | | * `t->recursion_count` could technically be non-zero, which |
107 | | * would constitute a bug. Reporting the bug would potentially |
108 | | * cause an infinite recursion, though, so let's ignore it. |
109 | | */ |
110 | |
|
111 | 0 | if (!t->interval_count) |
112 | 0 | continue; /* this timer was not used by this thread */ |
113 | | |
114 | 0 | t_final->total_ns += t->total_ns; |
115 | | |
116 | | /* |
117 | | * final_timer_block.timer[tid].min_ns was initialized to |
118 | | * was initialized to zero rather than UINT_MAX, so we should |
119 | | * always set both the min_ns and max_ns values the first time |
120 | | * that we add a partial sum into it. |
121 | | */ |
122 | 0 | if (!t_final->interval_count) { |
123 | 0 | t_final->min_ns = t->min_ns; |
124 | 0 | t_final->max_ns = t->max_ns; |
125 | 0 | } else { |
126 | 0 | t_final->min_ns = MY_MIN(t_final->min_ns, t->min_ns); |
127 | 0 | t_final->max_ns = MY_MAX(t_final->max_ns, t->max_ns); |
128 | 0 | } |
129 | |
|
130 | 0 | t_final->interval_count += t->interval_count; |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply) |
135 | 0 | { |
136 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
137 | 0 | enum trace2_timer_id tid; |
138 | |
|
139 | 0 | if (!ctx->used_any_per_thread_timer) |
140 | 0 | return; |
141 | | |
142 | | /* |
143 | | * For each timer, if the timer wants per-thread events and |
144 | | * this thread used it, emit it. |
145 | | */ |
146 | 0 | for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) |
147 | 0 | if (tr2_timer_metadata[tid].want_per_thread_events && |
148 | 0 | ctx->timer_block.timer[tid].interval_count) |
149 | 0 | fn_apply(&tr2_timer_metadata[tid], |
150 | 0 | &ctx->timer_block.timer[tid], |
151 | 0 | 0); |
152 | 0 | } |
153 | | |
154 | | void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply) |
155 | 0 | { |
156 | 0 | enum trace2_timer_id tid; |
157 | | |
158 | | /* |
159 | | * Accessing `final_timer_block` requires holding `tr2tls_mutex`. |
160 | | * We assume that our caller is holding the lock. |
161 | | */ |
162 | |
|
163 | 0 | for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) |
164 | 0 | if (final_timer_block.timer[tid].interval_count) |
165 | 0 | fn_apply(&tr2_timer_metadata[tid], |
166 | 0 | &final_timer_block.timer[tid], |
167 | 0 | 1); |
168 | 0 | } |