OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/cygprofile/cygprofile.h" | 5 #include "tools/cygprofile/cygprofile.h" |
6 | 6 |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <pthread.h> | 8 #include <pthread.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 #include <stdint.h> | 10 #include <stdint.h> |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
44 | 44 |
45 // "cyglog.PID.LWP.PPID" | 45 // "cyglog.PID.LWP.PPID" |
46 const char kLogFilenameFormat[] = "%scyglog.%d.%d-%d"; | 46 const char kLogFilenameFormat[] = "%scyglog.%d.%d-%d"; |
47 | 47 |
48 // Magic value of above to prevent instrumentation. Used when ThreadLog is being | 48 // Magic value of above to prevent instrumentation. Used when ThreadLog is being |
49 // constructed (to prevent reentering by malloc, for example) and by the flush | 49 // constructed (to prevent reentering by malloc, for example) and by the flush |
50 // log thread (to prevent it from being logged0. | 50 // log thread (to prevent it from being logged0. |
51 ThreadLog* const kMagicBeingConstructed = reinterpret_cast<ThreadLog*>(1); | 51 ThreadLog* const kMagicBeingConstructed = reinterpret_cast<ThreadLog*>(1); |
52 | 52 |
53 // Per-thread pointer to the current log object. | 53 // Per-thread pointer to the current log object. |
54 static __thread ThreadLog* g_tls_log = NULL; | 54 pthread_key_t g_tls_slot; |
55 | |
56 // Used to initialize the tls slot, once per the entire process. | |
57 pthread_once_t g_tls_slot_initializer_once = PTHREAD_ONCE_INIT; | |
58 | |
59 // This variable is to prevent re-entrancy in the __cyg_profile_func_enter() | |
60 // while the TLS slot itself is being initialized. Volatile here is required | |
61 // to avoid compiler otpimizations, as this need to be read re-entratively. | |
pasko
2016/04/15 13:00:47
nit: s/read re-entratively/read in a re-entrant wa
Primiano Tucci (use gerrit)
2016/04/15 13:06:08
Done.
| |
62 // This variable is written by one thread only, which is the first thread that | |
63 // happens to run the TLSSlotInitializer(). In practice this will happen very | |
64 // early in the startup process, as soon as the first instrumented function is | |
65 // called. | |
66 volatile bool g_tls_slot_being_initialized = false; | |
67 | |
68 // Initializes the global TLS slot. This is invoked only once per process. | |
69 static void TLSSlotInitializer() | |
70 { | |
71 g_tls_slot_being_initialized = true; | |
72 CHECK_EQ(0, pthread_key_create(&g_tls_slot, NULL)); | |
pasko
2016/04/15 13:00:47
let's do a PCHECK(pthread_key_create(&g_tls_slot,
pasko
2016/04/15 13:03:59
plz disregard this (it returns zero on success, an
Primiano Tucci (use gerrit)
2016/04/15 13:06:08
Done.
Primiano Tucci (use gerrit)
2016/04/15 13:06:08
Done.
| |
73 g_tls_slot_being_initialized = false; | |
74 } | |
55 | 75 |
56 // Returns light-weight process ID. On Linux, this is a system-wide unique | 76 // Returns light-weight process ID. On Linux, this is a system-wide unique |
57 // thread id. | 77 // thread id. |
58 pid_t GetTID() { | 78 pid_t GetTID() { |
59 return syscall(__NR_gettid); | 79 return syscall(__NR_gettid); |
60 } | 80 } |
61 | 81 |
62 timespec GetCurrentTime() { | 82 timespec GetCurrentTime() { |
63 timespec timestamp; | 83 timespec timestamp; |
64 clock_gettime(CLOCK_MONOTONIC, ×tamp); | 84 clock_gettime(CLOCK_MONOTONIC, ×tamp); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 | 176 |
157 ~Thread() { | 177 ~Thread() { |
158 CHECK_EQ(0, pthread_join(handle_, NULL)); | 178 CHECK_EQ(0, pthread_join(handle_, NULL)); |
159 } | 179 } |
160 | 180 |
161 private: | 181 private: |
162 static void* EntryPoint(void* data) { | 182 static void* EntryPoint(void* data) { |
163 // Disable logging on this thread. Although this routine is not instrumented | 183 // Disable logging on this thread. Although this routine is not instrumented |
164 // (cygprofile.gyp provides that), the called routines are and thus will | 184 // (cygprofile.gyp provides that), the called routines are and thus will |
165 // call instrumentation. | 185 // call instrumentation. |
166 CHECK(g_tls_log == NULL); // Must be 0 as this is a new thread. | 186 pthread_once(&g_tls_slot_initializer_once, TLSSlotInitializer); |
167 g_tls_log = kMagicBeingConstructed; | 187 ThreadLog* thread_log = reinterpret_cast<ThreadLog*>( |
188 pthread_getspecific(g_tls_slot)); | |
189 CHECK(thread_log == NULL); // Must be 0 as this is a new thread. | |
190 CHECK_EQ(0, pthread_setspecific(g_tls_slot, kMagicBeingConstructed)); | |
168 | 191 |
169 Thread* const instance = reinterpret_cast<Thread*>(data); | 192 Thread* const instance = reinterpret_cast<Thread*>(data); |
170 instance->thread_callback_.Run(); | 193 instance->thread_callback_.Run(); |
171 return NULL; | 194 return NULL; |
172 } | 195 } |
173 | 196 |
174 const base::Closure thread_callback_; | 197 const base::Closure thread_callback_; |
175 pthread_t handle_; | 198 pthread_t handle_; |
176 | 199 |
177 DISALLOW_COPY_AND_ASSIGN(Thread); | 200 DISALLOW_COPY_AND_ASSIGN(Thread); |
(...skipping 14 matching lines...) Expand all Loading... | |
192 base::Bind(&ThreadLog::FlushInternal, base::Unretained(this))) { | 215 base::Bind(&ThreadLog::FlushInternal, base::Unretained(this))) { |
193 } | 216 } |
194 | 217 |
195 ThreadLog::ThreadLog(const FlushCallback& flush_callback) | 218 ThreadLog::ThreadLog(const FlushCallback& flush_callback) |
196 : tid_(GetTID()), | 219 : tid_(GetTID()), |
197 in_use_(false), | 220 in_use_(false), |
198 flush_callback_(flush_callback) { | 221 flush_callback_(flush_callback) { |
199 } | 222 } |
200 | 223 |
201 ThreadLog::~ThreadLog() { | 224 ThreadLog::~ThreadLog() { |
202 g_tls_log = NULL; | 225 CHECK_EQ(0, pthread_setspecific(g_tls_slot, NULL)); |
203 } | 226 } |
204 | 227 |
205 void ThreadLog::AddEntry(void* address) { | 228 void ThreadLog::AddEntry(void* address) { |
206 if (in_use_) | 229 if (in_use_) |
207 return; | 230 return; |
208 in_use_ = true; | 231 in_use_ = true; |
209 | 232 |
210 CHECK_EQ(tid_, GetTID()); | 233 CHECK_EQ(tid_, GetTID()); |
211 const std::pair<base::hash_set<void*>::iterator, bool> pair = | 234 const std::pair<base::hash_set<void*>::iterator, bool> pair = |
212 called_functions_.insert(address); | 235 called_functions_.insert(address); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
351 extern "C" { | 374 extern "C" { |
352 | 375 |
353 // The GCC compiler callbacks, called on every function invocation providing | 376 // The GCC compiler callbacks, called on every function invocation providing |
354 // addresses of caller and callee codes. | 377 // addresses of caller and callee codes. |
355 void __cyg_profile_func_enter(void* this_fn, void* call_site) | 378 void __cyg_profile_func_enter(void* this_fn, void* call_site) |
356 __attribute__((no_instrument_function)); | 379 __attribute__((no_instrument_function)); |
357 void __cyg_profile_func_exit(void* this_fn, void* call_site) | 380 void __cyg_profile_func_exit(void* this_fn, void* call_site) |
358 __attribute__((no_instrument_function)); | 381 __attribute__((no_instrument_function)); |
359 | 382 |
360 void __cyg_profile_func_enter(void* this_fn, void* callee_unused) { | 383 void __cyg_profile_func_enter(void* this_fn, void* callee_unused) { |
361 if (g_tls_log == NULL) { | 384 // Avoid re-entrancy while initializing the TLS slot (once per process). |
362 g_tls_log = kMagicBeingConstructed; | 385 if (g_tls_slot_being_initialized) |
363 ThreadLog* new_log = new ThreadLog(); | 386 return; |
364 CHECK(new_log); | 387 |
365 g_logs_manager.Pointer()->AddLog(base::WrapUnique(new_log)); | 388 pthread_once(&g_tls_slot_initializer_once, TLSSlotInitializer); |
366 g_tls_log = new_log; | 389 ThreadLog* thread_log = reinterpret_cast<ThreadLog*>( |
390 pthread_getspecific(g_tls_slot)); | |
391 | |
392 if (thread_log == NULL) { | |
393 CHECK_EQ(0, pthread_setspecific(g_tls_slot, kMagicBeingConstructed)); | |
394 thread_log = new ThreadLog(); | |
395 CHECK(thread_log); | |
396 g_logs_manager.Pointer()->AddLog(base::WrapUnique(thread_log)); | |
397 CHECK_EQ(0, pthread_setspecific(g_tls_slot, thread_log)); | |
367 } | 398 } |
368 | 399 |
369 if (g_tls_log != kMagicBeingConstructed) | 400 if (thread_log != kMagicBeingConstructed) |
370 g_tls_log->AddEntry(this_fn); | 401 thread_log->AddEntry(this_fn); |
371 } | 402 } |
372 | 403 |
373 void __cyg_profile_func_exit(void* this_fn, void* call_site) {} | 404 void __cyg_profile_func_exit(void* this_fn, void* call_site) {} |
374 | 405 |
375 } // extern "C" | 406 } // extern "C" |
376 } // namespace cygprofile | 407 } // namespace cygprofile |
OLD | NEW |