Home | History | Annotate | Download | only in cygprofile
      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(&timestamp, 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