Home | History | Annotate | Download | only in cygprofile
      1 // Copyright 2014 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 // For performance reasons logs are buffered. Every thread has its own buffer
      9 // and log file so the contention between threads is minimal. As a side-effect,
     10 // functions called might be mentioned in many thread logs.
     11 //
     12 // A special thread is created in the process to periodically flush logs for all
     13 // threads in case the thread had stopped before flushing its logs.
     14 //
     15 // Also note that the instrumentation code is self-activated. It begins to
     16 // record the log data when it is called first, including the run-time startup.
     17 // Have it in mind when modifying it, in particular do not use global objects
     18 // with constructors as they are called during startup (too late for us).
     19 
     20 #ifndef TOOLS_CYGPROFILE_CYGPROFILE_H_
     21 #define TOOLS_CYGPROFILE_CYGPROFILE_H_
     22 
     23 #include <vector>
     24 
     25 #include <sys/time.h>
     26 #include <sys/types.h>
     27 
     28 #include "base/callback.h"
     29 #include "base/containers/hash_tables.h"
     30 #include "base/macros.h"
     31 #include "base/memory/scoped_ptr.h"
     32 #include "base/synchronization/lock.h"
     33 #include "build/build_config.h"
     34 
     35 #if !defined(OS_ANDROID)
     36 // This is only supported on Android thanks to the fact that on Android
     37 // processes (other than the system's zygote) don't fork.
     38 //
     39 // To make cygprofile truly work (i.e. without any deadlock) on Chrome
     40 // platforms that use fork(), cygprofile.cc should be written in a way that
     41 // guarantees that:
     42 // - No lock is acquired by a foreign thread during fork(). In particular this
     43 // means that cygprofile.cc should not perform any heap allocation (since heap
     44 // allocators, including TCMalloc generally use locks).
     45 // - Only cygprofile.cc uses pthread_atfork() in the whole process. Unlike POSIX
     46 // signals, pthread_atfork() doesn't provide a way to install multiple handlers.
     47 // Calling pthread_atfork() in cygprofile.cc would override any handler that
     48 // could have been installed previously.
     49 //
     50 // Chrome happens to violate the first requirement at least once by having its
     51 // process launcher thread fork. However the child process in that case, when
     52 // it's not instrumented with cygprofile, directly calls exec(). This is safe
     53 // since the child process doesn't try to release a lock acquired by another
     54 // thread in the parent process which would lead to a deadlock. This problem was
     55 // actually observed by trying to port the current version of cygprofile.cc to
     56 // Linux.
     57 #error This is only supported on Android.
     58 #endif
     59 
     60 // The following is only exposed for testing.
     61 namespace cygprofile {
     62 
     63 class Thread;
     64 
     65 // Single log entry recorded for each function call.
     66 struct LogEntry {
     67   LogEntry(const void* address);
     68 
     69   const timespec time;
     70   const pid_t pid;
     71   const pid_t tid;
     72   const void* const address;
     73 };
     74 
     75 // Per-thread function calls log.
     76 class ThreadLog {
     77  public:
     78   // Callback invoked for flushing that can be provided for testing.
     79   typedef base::Callback<void (std::vector<LogEntry>*)> FlushCallback;
     80 
     81   ThreadLog();
     82 
     83   // Used for testing.
     84   ThreadLog(const FlushCallback& flush_callback);
     85 
     86   ~ThreadLog();
     87 
     88   // Must only be called from the thread this ThreadLog instance is watching.
     89   void AddEntry(void* address);
     90 
     91   // Can be called from any thread.
     92   void TakeEntries(std::vector<LogEntry>* output);
     93 
     94   // Flushes the provided vector of entries to a file and clears it. Note that
     95   // this can be called from any thread.
     96   void Flush(std::vector<LogEntry>* entries) const;
     97 
     98  private:
     99   // Default implementation (that can be overridden for testing) of the method
    100   // above.
    101   void FlushInternal(std::vector<LogEntry>* entries) const;
    102 
    103   // Thread identifier as Linux kernel shows it.  LWP (light-weight process) is
    104   // a unique ID of the thread in the system, unlike pthread_self() which is the
    105   // same for fork()-ed threads.
    106   const pid_t tid_;
    107 
    108   // Current thread is inside the instrumentation routine.
    109   bool in_use_;
    110 
    111   // Callback used to flush entries.
    112   const FlushCallback flush_callback_;
    113 
    114   // Keeps track of all functions that have been logged on this thread so we do
    115   // not record duplicates.
    116   std::hash_set<void*> called_functions_;
    117 
    118   // A lock that guards |entries_| usage between per-thread instrumentation
    119   // routine and timer flush callback. So the contention could happen only
    120   // during the flush, every 15 secs.
    121   base::Lock lock_;
    122 
    123   std::vector<LogEntry> entries_;
    124 
    125   DISALLOW_COPY_AND_ASSIGN(ThreadLog);
    126 };
    127 
    128 // Manages a list of per-thread logs.
    129 class ThreadLogsManager {
    130  public:
    131   ThreadLogsManager();
    132 
    133   // Used for testing. The provided callbacks are used for testing to
    134   // synchronize the internal thread with the unit test running on the main
    135   // thread.
    136   ThreadLogsManager(const base::Closure& wait_callback,
    137                     const base::Closure& notify_callback);
    138 
    139   ~ThreadLogsManager();
    140 
    141   // Can be called from any thread.
    142   void AddLog(scoped_ptr<ThreadLog> new_log);
    143 
    144  private:
    145   void StartInternalFlushThread_Locked();
    146 
    147   // Flush thread's entry point.
    148   void FlushAllLogsOnFlushThread();
    149 
    150   // Used to make the internal thread sleep before each flush iteration.
    151   const base::Closure wait_callback_;
    152   // Used to trigger a notification when a flush happened on the internal
    153   // thread.
    154   const base::Closure notify_callback_;
    155 
    156   // Protects the state below.
    157   base::Lock lock_;
    158   scoped_ptr<Thread> flush_thread_;
    159   std::vector<ThreadLog*> logs_;
    160 
    161   DISALLOW_COPY_AND_ASSIGN(ThreadLogsManager);
    162 };
    163 
    164 }  // namespace cygprofile
    165 
    166 #endif  // TOOLS_CYGPROFILE_CYGPROFILE_H_
    167