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 3 // found in the LICENSE file. 4 // 5 // Tool to log the execution of the process (Chrome). Writes logs containing 6 // time and address of the callback being called for the first time. 7 // 8 // To speed up the logging, buffering logs is implemented. Every thread have its 9 // own buffer and log file so the contention between threads is minimal. As a 10 // side-effect, functions called might be mentioned in many thread logs. 11 // 12 // Special thread is created in the process to periodically flushes logs for all 13 // threads for the case the thread has stopped before flushing its logs. 14 // 15 // Use this profiler with linux_use_tcmalloc=0. 16 // 17 // Note for the ChromeOS Chrome. Remove renderer process from the sandbox (add 18 // --no-sandbox option to running Chrome in /sbin/session_manager_setup.sh). 19 // Otherwise renderer will not be able to write logs (and will assert on that). 20 // 21 // Also note that the instrumentation code is self-activated. It begins to 22 // record the log data when it is called first, including the run-time startup. 23 // Have it in mind when modifying it, in particular do not use global objects 24 // with constructors as they are called during startup (too late for us). 25 26 #include <fcntl.h> 27 #include <fstream> 28 #include <pthread.h> 29 #include <stdarg.h> 30 #include <string> 31 #include <sys/stat.h> 32 #include <sys/syscall.h> 33 #include <sys/time.h> 34 #include <sys/types.h> 35 #include <vector> 36 37 #include "base/containers/hash_tables.h" 38 #include "base/lazy_instance.h" 39 #include "base/logging.h" 40 #include "base/memory/singleton.h" 41 #include "base/synchronization/lock.h" 42 43 namespace cygprofile { 44 45 extern "C" { 46 47 // Note that these are linked internally by the compiler. Don't call 48 // them directly! 49 void __cyg_profile_func_enter(void* this_fn, void* call_site) 50 __attribute__((no_instrument_function)); 51 void __cyg_profile_func_exit(void* this_fn, void* call_site) 52 __attribute__((no_instrument_function)); 53 54 } 55 56 // Single log entry layout. 57 struct CygLogEntry { 58 time_t seconds; 59 long int usec; 60 pid_t pid; 61 pthread_t tid; 62 const void* this_fn; 63 CygLogEntry(time_t seconds, long int usec, 64 pid_t pid, pthread_t tid, const void* this_fn) 65 : seconds(seconds), usec(usec), 66 pid(pid), tid(tid), this_fn(this_fn) {} 67 }; 68 69 // Common data for the process. Singleton. 70 class CygCommon { 71 public: 72 static CygCommon* GetInstance(); 73 std::string header() const { return header_line_; } 74 private: 75 CygCommon(); 76 std::string header_line_; 77 friend struct DefaultSingletonTraits<CygCommon>; 78 79 DISALLOW_COPY_AND_ASSIGN(CygCommon); 80 }; 81 82 // Returns light-weight process ID. On linux, this is a system-wide 83 // unique thread id. 84 static pid_t GetLwp() { 85 return syscall(__NR_gettid); 86 } 87 88 // A per-thread structure representing the log itself. 89 class CygTlsLog { 90 public: 91 CygTlsLog() 92 : in_use_(false), lwp_(GetLwp()), pthread_self_(pthread_self()) { } 93 94 // Enter a log entity. 95 void LogEnter(void* this_fn); 96 97 // Add newly created CygTlsLog object to the list of all such objects. 98 // Needed for the timer callback: it will enumerate each object and flush. 99 static void AddNewLog(CygTlsLog* newlog); 100 101 // Starts a thread in this process that periodically flushes all the threads. 102 // Must be called once per process. 103 static void StartFlushLogThread(); 104 105 private: 106 static const int kBufMaxSize; 107 static const char kLogFilenameFmt[]; 108 static const char kLogFileNamePrefix[]; 109 110 // Flush the log to file. Create file if needed. 111 // Must be called with locked log_mutex_. 112 void FlushLog(); 113 114 // Fork hooks. Needed to keep data in consistent state during fork(). 115 static void AtForkPrepare(); 116 static void AtForkParent(); 117 static void AtForkChild(); 118 119 // Thread callback to flush all logs periodically. 120 static void* FlushLogThread(void*); 121 122 std::string log_filename_; 123 std::vector<CygLogEntry> buf_; 124 125 // A lock that guards buf_ usage between per-thread instrumentation 126 // routine and timer flush callback. So the contention could happen 127 // only during the flush, every 30 secs. 128 base::Lock log_mutex_; 129 130 // Current thread is inside the instrumentation routine. 131 bool in_use_; 132 133 // Keeps track of all functions that have been logged on this thread 134 // so we do not record dublicates. 135 std::hash_set<void*> functions_called_; 136 137 // Thread identifier as Linux kernel shows it. For debugging purposes. 138 // LWP (light-weight process) is a unique ID of the thread in the system, 139 // unlike pthread_self() which is the same for fork()-ed threads. 140 pid_t lwp_; 141 pthread_t pthread_self_; 142 143 DISALLOW_COPY_AND_ASSIGN(CygTlsLog); 144 }; 145 146 // Storage for logs for all threads in the process. 147 // Using std::list may be better, but it fails when used before main(). 148 struct AllLogs { 149 std::vector<CygTlsLog*> logs; 150 base::Lock mutex; 151 }; 152 153 base::LazyInstance<AllLogs>::Leaky all_logs_ = LAZY_INSTANCE_INITIALIZER; 154 155 // Per-thread pointer to the current log object. 156 static __thread CygTlsLog* tls_current_log = NULL; 157 158 // Magic value of above to prevent the instrumentation. Used when CygTlsLog is 159 // being constructed (to prevent reentering by malloc, for example) and by 160 // the FlushLogThread (to prevent it being logged - see comment in its code). 161 CygTlsLog* const kMagicBeingConstructed = reinterpret_cast<CygTlsLog*>(1); 162 163 // Number of entries in the per-thread log buffer before we flush. 164 // Note, that we also flush by timer so not all thread logs may grow up to this. 165 const int CygTlsLog::kBufMaxSize = 3000; 166 167 #if defined(OS_ANDROID) 168 const char CygTlsLog::kLogFileNamePrefix[] = 169 "/data/local/tmp/chrome/cyglog/"; 170 #else 171 const char CygTlsLog::kLogFileNamePrefix[] = "/var/log/chrome/"; 172 #endif 173 174 // "cyglog.PID.LWP.pthread_self.PPID" 175 const char CygTlsLog::kLogFilenameFmt[] = "%scyglog.%d.%d.%ld-%d"; 176 177 CygCommon* CygCommon::GetInstance() { 178 return Singleton<CygCommon>::get(); 179 } 180 181 CygCommon::CygCommon() { 182 // Determine our module addresses. 183 std::ifstream mapsfile("/proc/self/maps"); 184 CHECK(mapsfile.good()); 185 static const int kMaxLineSize = 512; 186 char line[kMaxLineSize]; 187 void (*this_fn)(void) = 188 reinterpret_cast<void(*)()>(__cyg_profile_func_enter); 189 while (mapsfile.getline(line, kMaxLineSize)) { 190 const std::string str_line = line; 191 size_t permindex = str_line.find("r-xp"); 192 if (permindex != std::string::npos) { 193 int dashindex = str_line.find("-"); 194 int spaceindex = str_line.find(" "); 195 char* p; 196 void* start = reinterpret_cast<void*> 197 (strtol((str_line.substr(0, dashindex)).c_str(), 198 &p, 16)); 199 CHECK(*p == 0); // Could not determine start address. 200 void* end = reinterpret_cast<void*> 201 (strtol((str_line.substr(dashindex + 1, 202 spaceindex - dashindex - 1)).c_str(), 203 &p, 16)); 204 CHECK(*p == 0); // Could not determine end address. 205 206 if (this_fn >= start && this_fn < end) 207 header_line_ = str_line; 208 } 209 } 210 mapsfile.close(); 211 header_line_.append("\nsecs\tmsecs\tpid:threadid\tfunc\n"); 212 } 213 214 void CygTlsLog::LogEnter(void* this_fn) { 215 if (in_use_) 216 return; 217 in_use_ = true; 218 219 if (functions_called_.find(this_fn) == 220 functions_called_.end()) { 221 functions_called_.insert(this_fn); 222 base::AutoLock lock(log_mutex_); 223 if (buf_.capacity() < kBufMaxSize) 224 buf_.reserve(kBufMaxSize); 225 struct timeval timestamp; 226 gettimeofday(×tamp, NULL); 227 buf_.push_back(CygLogEntry(time(NULL), timestamp.tv_usec, 228 getpid(), pthread_self(), this_fn)); 229 if (buf_.size() == kBufMaxSize) { 230 FlushLog(); 231 } 232 } 233 234 in_use_ = false; 235 } 236 237 void CygTlsLog::AtForkPrepare() { 238 CHECK(tls_current_log); 239 CHECK(tls_current_log->lwp_ == GetLwp()); 240 CHECK(tls_current_log->pthread_self_ == pthread_self()); 241 all_logs_.Get().mutex.Acquire(); 242 } 243 244 void CygTlsLog::AtForkParent() { 245 CHECK(tls_current_log); 246 CHECK(tls_current_log->lwp_ == GetLwp()); 247 CHECK(tls_current_log->pthread_self_ == pthread_self()); 248 all_logs_.Get().mutex.Release(); 249 } 250 251 void CygTlsLog::AtForkChild() { 252 CHECK(tls_current_log); 253 254 // Update the IDs of this new thread of the new process. 255 // Note that the process may (and Chrome main process forks zygote this way) 256 // call exec(self) after we return (to launch new shiny self). If done like 257 // that, PID and LWP will remain the same, but pthread_self() changes. 258 pid_t lwp = GetLwp(); 259 CHECK(tls_current_log->lwp_ != lwp); // LWP is system-wide unique thread ID. 260 tls_current_log->lwp_ = lwp; 261 262 CHECK(tls_current_log->pthread_self_ == pthread_self()); 263 264 // Leave the only current thread tls object because fork() clones only the 265 // current thread (the one that called fork) to the child process. 266 AllLogs& all_logs = all_logs_.Get(); 267 all_logs.logs.clear(); 268 all_logs.logs.push_back(tls_current_log); 269 CHECK(all_logs.logs.size() == 1); 270 271 // Clear log filename so it will be re-calculated with the new PIDs. 272 tls_current_log->log_filename_.clear(); 273 274 // Create the thread that will periodically flush all logs for this process. 275 StartFlushLogThread(); 276 277 // We do not update log header line (CygCommon data) as it will be the same 278 // because the new process is just a forked copy. 279 all_logs.mutex.Release(); 280 } 281 282 void CygTlsLog::StartFlushLogThread() { 283 pthread_t tid; 284 CHECK(!pthread_create(&tid, NULL, &CygTlsLog::FlushLogThread, NULL)); 285 } 286 287 void CygTlsLog::AddNewLog(CygTlsLog* newlog) { 288 CHECK(tls_current_log == kMagicBeingConstructed); 289 AllLogs& all_logs = all_logs_.Get(); 290 base::AutoLock lock(all_logs.mutex); 291 if (all_logs.logs.empty()) { 292 293 // An Android app never fork, it always starts with a pre-defined number of 294 // process descibed by the android manifest file. In fact, there is not 295 // support for pthread_atfork at the android system libraries. All chrome 296 // for android processes will start as independent processs and each one 297 // will generate its own logs that will later have to be merged as usual. 298 #if !defined(OS_ANDROID) 299 CHECK(!pthread_atfork(CygTlsLog::AtForkPrepare, 300 CygTlsLog::AtForkParent, 301 CygTlsLog::AtForkChild)); 302 #endif 303 304 // The very first process starts its flush thread here. Forked processes 305 // will do it in AtForkChild(). 306 StartFlushLogThread(); 307 } 308 all_logs.logs.push_back(newlog); 309 } 310 311 // Printf-style routine to write to open file. 312 static void WriteLogLine(int fd, const char* fmt, ...) { 313 va_list arg_ptr; 314 va_start(arg_ptr, fmt); 315 char msg[160]; 316 int len = vsnprintf(msg, sizeof(msg), fmt, arg_ptr); 317 int rc = write(fd, msg, (len > sizeof(msg))? sizeof(msg): len); 318 va_end(arg_ptr); 319 }; 320 321 void CygTlsLog::FlushLog() { 322 bool first_log_write = false; 323 if (log_filename_.empty()) { 324 first_log_write = true; 325 char buf[80]; 326 snprintf(buf, sizeof(buf), kLogFilenameFmt, 327 kLogFileNamePrefix, getpid(), lwp_, pthread_self_, getppid()); 328 log_filename_ = buf; 329 unlink(log_filename_.c_str()); 330 } 331 332 int file = open(log_filename_.c_str(), O_CREAT | O_WRONLY | O_APPEND, 00600); 333 CHECK(file != -1); 334 335 if (first_log_write) 336 WriteLogLine(file, "%s", CygCommon::GetInstance()->header().c_str()); 337 338 for (int i = 0; i != buf_.size(); ++i) { 339 const CygLogEntry& p = buf_[i]; 340 WriteLogLine(file, "%ld %ld\t%d:%ld\t%p\n", 341 p.seconds, p.usec, p.pid, p.tid, p.this_fn); 342 } 343 344 close(file); 345 buf_.clear(); 346 } 347 348 void* CygTlsLog::FlushLogThread(void*) { 349 // Disable logging this thread. Although this routine is not instrumented 350 // (cygprofile.gyp provides that), the called routines are and thus will 351 // call instrumentation. 352 CHECK(tls_current_log == NULL); // Must be 0 as this is a new thread. 353 tls_current_log = kMagicBeingConstructed; 354 355 // Run this loop infinitely: sleep 30 secs and the flush all thread's 356 // buffers. There is a danger that, when quitting Chrome, this thread may 357 // see unallocated data and segfault. We do not care because we need logs 358 // when Chrome is working. 359 while (true) { 360 for(int secs_to_sleep = 30; secs_to_sleep != 0;) 361 secs_to_sleep = sleep(secs_to_sleep); 362 363 AllLogs& all_logs = all_logs_.Get(); 364 base::AutoLock lock(all_logs.mutex); 365 for (int i = 0; i != all_logs.logs.size(); ++i) { 366 CygTlsLog* current_log = all_logs.logs[i]; 367 base::AutoLock current_lock(current_log->log_mutex_); 368 if (current_log->buf_.size()) { 369 current_log->FlushLog(); 370 } else { 371 // The thread's log is still empty. Probably the thread finished prior 372 // to previous timer fired - deallocate its buffer. Even if the thread 373 // ever resumes, it will allocate its buffer again in 374 // std::vector::push_back(). 375 current_log->buf_.clear(); 376 } 377 } 378 } 379 } 380 381 // Gcc Compiler callback, called on every function invocation providing 382 // addresses of caller and callee codes. 383 void __cyg_profile_func_enter(void* this_fn, void* callee_unused) { 384 if (tls_current_log == NULL) { 385 tls_current_log = kMagicBeingConstructed; 386 CygTlsLog* newlog = new CygTlsLog; 387 CHECK(newlog); 388 CygTlsLog::AddNewLog(newlog); 389 tls_current_log = newlog; 390 } 391 if (tls_current_log != kMagicBeingConstructed) { 392 tls_current_log->LogEnter(this_fn); 393 } 394 } 395 396 // Gcc Compiler callback, called after every function invocation providing 397 // addresses of caller and callee codes. 398 void __cyg_profile_func_exit(void* this_fn, void* call_site) { 399 } 400 401 } // namespace cygprofile 402