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 optimizations as this need to be read in a re-entrant way. |
| 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 PCHECK(0 == pthread_key_create(&g_tls_slot, NULL)); |
| 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 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 | 164 |
145 } // namespace | 165 } // namespace |
146 | 166 |
147 // Custom thread implementation that joins on destruction. Note that | 167 // Custom thread implementation that joins on destruction. Note that |
148 // base::Thread has non-trivial dependencies on e.g. AtExitManager which makes | 168 // base::Thread has non-trivial dependencies on e.g. AtExitManager which makes |
149 // it hard to use it early. | 169 // it hard to use it early. |
150 class Thread { | 170 class Thread { |
151 public: | 171 public: |
152 Thread(const base::Closure& thread_callback) | 172 Thread(const base::Closure& thread_callback) |
153 : thread_callback_(thread_callback) { | 173 : thread_callback_(thread_callback) { |
154 CHECK_EQ(0, pthread_create(&handle_, NULL, &Thread::EntryPoint, this)); | 174 PCHECK(0 == pthread_create(&handle_, NULL, &Thread::EntryPoint, this)); |
155 } | 175 } |
156 | 176 |
157 ~Thread() { | 177 ~Thread() { |
158 CHECK_EQ(0, pthread_join(handle_, NULL)); | 178 PCHECK(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 PCHECK(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 PCHECK(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 PCHECK(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 PCHECK(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 |