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