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_tmr.h"
6
+
7
+#define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
8
+#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
+{
39
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
40
+ struct tr2_timer *t = &ctx->timer_block.timer[tid];
41
+
42
+ t->recursion_count++;
43
+ if (t->recursion_count > 1)
44
+ return; /* ignore recursive starts */
45
+
46
+ t->start_ns = getnanotime();
47
+}
48
+
49
+void tr2_stop_timer(enum trace2_timer_id tid)
50
+{
51
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
52
+ struct tr2_timer *t = &ctx->timer_block.timer[tid];
53
+ uint64_t ns_now;
54
+ uint64_t ns_interval;
55
+
56
+ assert(t->recursion_count > 0);
57
+
58
+ t->recursion_count--;
59
+ if (t->recursion_count)
60
+ return; /* still in recursive call(s) */
61
+
62
+ ns_now = getnanotime();
63
+ ns_interval = ns_now - t->start_ns;
64
+
65
+ 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
+ if (!t->interval_count) {
74
+ t->min_ns = ns_interval;
75
+ t->max_ns = ns_interval;
76
+ } else {
77
+ t->min_ns = MY_MIN(ns_interval, t->min_ns);
78
+ t->max_ns = MY_MAX(ns_interval, t->max_ns);
79
+ }
80
+
81
+ t->interval_count++;
82
+
83
+ ctx->used_any_timer = 1;
84
+ if (tr2_timer_metadata[tid].want_per_thread_events)
85
+ ctx->used_any_per_thread_timer = 1;
86
+}
87
+
88
+void tr2_update_final_timers(void)
89
+{
90
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
91
+ enum trace2_timer_id tid;
92
+
93
+ if (!ctx->used_any_timer)
94
+ 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
+ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) {
102
+ struct tr2_timer *t_final = &final_timer_block.timer[tid];
103
+ struct tr2_timer *t = &ctx->timer_block.timer[tid];
104
+
105
+ if (t->recursion_count) {
106
+ /*
107
+ * The current thread is exiting with
108
+ * timer[tid] still running.
109
+ *
110
+ * Technically, this is a bug, but I'm going
111
+ * to ignore it.
112
+ *
113
+ * I don't think it is worth calling die()
114
+ * for. I don't think it is worth killing the
115
+ * process for this bookkeeping error. We
116
+ * might want to call warning(), but I'm going
117
+ * to wait on that.
118
+ *
119
+ * The downside here is that total_ns won't
120
+ * include the current open interval (now -
121
+ * start_ns). I can live with that.
122
+ */
123
+ }
124
+
125
+ if (!t->interval_count)
126
+ continue; /* this timer was not used by this thread */
127
+
128
+ t_final->total_ns += t->total_ns;
129
+
130
+ /*
131
+ * final_timer_block.timer[tid].min_ns was initialized to
132
+ * was initialized to zero rather than UINT_MAX, so we should
133
+ * always set both the min_ns and max_ns values the first time
134
+ * that we add a partial sum into it.
135
+ */
136
+ if (!t_final->interval_count) {
137
+ t_final->min_ns = t->min_ns;
138
+ t_final->max_ns = t->max_ns;
139
+ } else {
140
+ t_final->min_ns = MY_MIN(t_final->min_ns, t->min_ns);
141
+ t_final->max_ns = MY_MAX(t_final->max_ns, t->max_ns);
142
+ }
143
+
144
+ t_final->interval_count += t->interval_count;
145
+ }
146
+}
147
+
148
+void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply)
149
+{
150
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
151
+ enum trace2_timer_id tid;
152
+
153
+ if (!ctx->used_any_per_thread_timer)
154
+ return;
155
+
156
+ /*
157
+ * For each timer, if the timer wants per-thread events and
158
+ * this thread used it, emit it.
159
+ */
160
+ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++)
161
+ if (tr2_timer_metadata[tid].want_per_thread_events &&
162
+ ctx->timer_block.timer[tid].interval_count)
163
+ fn_apply(&tr2_timer_metadata[tid],
164
+ &ctx->timer_block.timer[tid],
165
+ 0);
166
+}
167
+
168
+void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply)
169
+{
170
+ enum trace2_timer_id tid;
171
+
172
+ /*
173
+ * Accessing `final_timer_block` requires holding `tr2tls_mutex`.
174
+ * We assume that our caller is holding the lock.
175
+ */
176
+
177
+ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++)
178
+ if (final_timer_block.timer[tid].interval_count)
179
+ fn_apply(&tr2_timer_metadata[tid],
180
+ &final_timer_block.timer[tid],
181
+ 1);
182
+}