/src/git/trace2/tr2_tls.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "git-compat-util.h" |
2 | | #include "strbuf.h" |
3 | | #include "thread-utils.h" |
4 | | #include "trace.h" |
5 | | #include "trace2/tr2_tls.h" |
6 | | |
7 | | /* |
8 | | * Initialize size of the thread stack for nested regions. |
9 | | * This is used to store nested region start times. Note that |
10 | | * this stack is per-thread and not per-trace-key. |
11 | | */ |
12 | 0 | #define TR2_REGION_NESTING_INITIAL_SIZE (100) |
13 | | |
14 | | static struct tr2tls_thread_ctx *tr2tls_thread_main; |
15 | | static uint64_t tr2tls_us_start_process; |
16 | | |
17 | | static pthread_mutex_t tr2tls_mutex; |
18 | | static pthread_key_t tr2tls_key; |
19 | | |
20 | | static int tr2_next_thread_id; /* modify under lock */ |
21 | | |
22 | | void tr2tls_start_process_clock(void) |
23 | 0 | { |
24 | 0 | if (tr2tls_us_start_process) |
25 | 0 | return; |
26 | | |
27 | | /* |
28 | | * Keep the absolute start time of the process (i.e. the main |
29 | | * process) in a fixed variable since other threads need to |
30 | | * access it. This allows them to do that without a lock on |
31 | | * main thread's array data (because of reallocs). |
32 | | */ |
33 | 0 | tr2tls_us_start_process = getnanotime() / 1000; |
34 | 0 | } |
35 | | |
36 | | struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_base_name, |
37 | | uint64_t us_thread_start) |
38 | 0 | { |
39 | 0 | struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx)); |
40 | 0 | struct strbuf buf = STRBUF_INIT; |
41 | | |
42 | | /* |
43 | | * Implicitly "tr2tls_push_self()" to capture the thread's start |
44 | | * time in array_us_start[0]. For the main thread this gives us the |
45 | | * application run time. |
46 | | */ |
47 | 0 | ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE; |
48 | 0 | ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t)); |
49 | 0 | ctx->array_us_start[ctx->nr_open_regions++] = us_thread_start; |
50 | |
|
51 | 0 | ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id); |
52 | |
|
53 | 0 | strbuf_init(&buf, 0); |
54 | 0 | if (ctx->thread_id) |
55 | 0 | strbuf_addf(&buf, "th%02d:", ctx->thread_id); |
56 | 0 | strbuf_addstr(&buf, thread_base_name); |
57 | 0 | if (buf.len > TR2_MAX_THREAD_NAME) |
58 | 0 | strbuf_setlen(&buf, TR2_MAX_THREAD_NAME); |
59 | 0 | ctx->thread_name = strbuf_detach(&buf, NULL); |
60 | |
|
61 | 0 | pthread_setspecific(tr2tls_key, ctx); |
62 | |
|
63 | 0 | return ctx; |
64 | 0 | } |
65 | | |
66 | | struct tr2tls_thread_ctx *tr2tls_get_self(void) |
67 | 0 | { |
68 | 0 | struct tr2tls_thread_ctx *ctx; |
69 | |
|
70 | 0 | if (!HAVE_THREADS) |
71 | 0 | return tr2tls_thread_main; |
72 | | |
73 | 0 | ctx = pthread_getspecific(tr2tls_key); |
74 | | |
75 | | /* |
76 | | * If the current thread's thread-proc did not call |
77 | | * trace2_thread_start(), then the thread will not have any |
78 | | * thread-local storage. Create it now and silently continue. |
79 | | */ |
80 | 0 | if (!ctx) |
81 | 0 | ctx = tr2tls_create_self("unknown", getnanotime() / 1000); |
82 | |
|
83 | 0 | return ctx; |
84 | 0 | } |
85 | | |
86 | | int tr2tls_is_main_thread(void) |
87 | 0 | { |
88 | 0 | if (!HAVE_THREADS) |
89 | 0 | return 1; |
90 | | |
91 | 0 | return pthread_getspecific(tr2tls_key) == tr2tls_thread_main; |
92 | 0 | } |
93 | | |
94 | | void tr2tls_unset_self(void) |
95 | 0 | { |
96 | 0 | struct tr2tls_thread_ctx *ctx; |
97 | |
|
98 | 0 | ctx = tr2tls_get_self(); |
99 | |
|
100 | 0 | pthread_setspecific(tr2tls_key, NULL); |
101 | |
|
102 | 0 | free((char *)ctx->thread_name); |
103 | 0 | free(ctx->array_us_start); |
104 | 0 | free(ctx); |
105 | 0 | } |
106 | | |
107 | | void tr2tls_push_self(uint64_t us_now) |
108 | 0 | { |
109 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
110 | |
|
111 | 0 | ALLOC_GROW(ctx->array_us_start, ctx->nr_open_regions + 1, ctx->alloc); |
112 | 0 | ctx->array_us_start[ctx->nr_open_regions++] = us_now; |
113 | 0 | } |
114 | | |
115 | | void tr2tls_pop_self(void) |
116 | 0 | { |
117 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
118 | |
|
119 | 0 | if (!ctx->nr_open_regions) |
120 | 0 | BUG("no open regions in thread '%s'", ctx->thread_name); |
121 | | |
122 | 0 | ctx->nr_open_regions--; |
123 | 0 | } |
124 | | |
125 | | void tr2tls_pop_unwind_self(void) |
126 | 0 | { |
127 | 0 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
128 | |
|
129 | 0 | while (ctx->nr_open_regions > 1) |
130 | 0 | tr2tls_pop_self(); |
131 | 0 | } |
132 | | |
133 | | uint64_t tr2tls_region_elasped_self(uint64_t us) |
134 | 0 | { |
135 | 0 | struct tr2tls_thread_ctx *ctx; |
136 | 0 | uint64_t us_start; |
137 | |
|
138 | 0 | ctx = tr2tls_get_self(); |
139 | 0 | if (!ctx->nr_open_regions) |
140 | 0 | return 0; |
141 | | |
142 | 0 | us_start = ctx->array_us_start[ctx->nr_open_regions - 1]; |
143 | |
|
144 | 0 | return us - us_start; |
145 | 0 | } |
146 | | |
147 | | uint64_t tr2tls_absolute_elapsed(uint64_t us) |
148 | 0 | { |
149 | 0 | if (!tr2tls_thread_main) |
150 | 0 | return 0; |
151 | | |
152 | 0 | return us - tr2tls_us_start_process; |
153 | 0 | } |
154 | | |
155 | | void tr2tls_init(void) |
156 | 0 | { |
157 | 0 | tr2tls_start_process_clock(); |
158 | |
|
159 | 0 | pthread_key_create(&tr2tls_key, NULL); |
160 | 0 | init_recursive_mutex(&tr2tls_mutex); |
161 | |
|
162 | 0 | tr2tls_thread_main = |
163 | 0 | tr2tls_create_self("main", tr2tls_us_start_process); |
164 | 0 | } |
165 | | |
166 | | void tr2tls_release(void) |
167 | 0 | { |
168 | 0 | tr2tls_unset_self(); |
169 | 0 | tr2tls_thread_main = NULL; |
170 | |
|
171 | 0 | pthread_mutex_destroy(&tr2tls_mutex); |
172 | 0 | pthread_key_delete(tr2tls_key); |
173 | 0 | } |
174 | | |
175 | | int tr2tls_locked_increment(int *p) |
176 | 0 | { |
177 | 0 | int current_value; |
178 | |
|
179 | 0 | pthread_mutex_lock(&tr2tls_mutex); |
180 | 0 | current_value = *p; |
181 | 0 | *p = current_value + 1; |
182 | 0 | pthread_mutex_unlock(&tr2tls_mutex); |
183 | |
|
184 | 0 | return current_value; |
185 | 0 | } |
186 | | |
187 | | void tr2tls_lock(void) |
188 | 0 | { |
189 | 0 | pthread_mutex_lock(&tr2tls_mutex); |
190 | 0 | } |
191 | | |
192 | | void tr2tls_unlock(void) |
193 | 0 | { |
194 | 0 | pthread_mutex_unlock(&tr2tls_mutex); |
195 | 0 | } |