Coverage Report

Created: 2025-12-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}