| 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 |