Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2012 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 #include "chrome/common/profiling.h"
      6 
      7 #include "base/at_exit.h"
      8 #include "base/bind.h"
      9 #include "base/command_line.h"
     10 #include "base/debug/profiler.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/threading/thread.h"
     15 #include "chrome/common/chrome_switches.h"
     16 #include "v8/include/v8.h"
     17 
     18 namespace {
     19 
     20 base::debug::AddDynamicSymbol add_dynamic_symbol_func = NULL;
     21 base::debug::MoveDynamicSymbol move_dynamic_symbol_func = NULL;
     22 
     23 void JitCodeEventHandler(const v8::JitCodeEvent* event) {
     24   DCHECK_NE(static_cast<base::debug::AddDynamicSymbol>(NULL),
     25             add_dynamic_symbol_func);
     26   DCHECK_NE(static_cast<base::debug::MoveDynamicSymbol>(NULL),
     27             move_dynamic_symbol_func);
     28 
     29   switch (event->type) {
     30     case v8::JitCodeEvent::CODE_ADDED:
     31       add_dynamic_symbol_func(event->code_start, event->code_len,
     32                               event->name.str, event->name.len);
     33       break;
     34 
     35     case v8::JitCodeEvent::CODE_MOVED:
     36       move_dynamic_symbol_func(event->code_start, event->new_code_start);
     37       break;
     38 
     39     default:
     40       break;
     41   }
     42 }
     43 
     44 std::string GetProfileName() {
     45   static const char kDefaultProfileName[] = "chrome-profile-{type}-{pid}";
     46   CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ());
     47 
     48   if (profile_name.empty()) {
     49     const CommandLine& command_line = *CommandLine::ForCurrentProcess();
     50     if (command_line.HasSwitch(switches::kProfilingFile))
     51       profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile);
     52     else
     53       profile_name = std::string(kDefaultProfileName);
     54     std::string process_type =
     55         command_line.GetSwitchValueASCII(switches::kProcessType);
     56     std::string type = process_type.empty() ?
     57         std::string("browser") : std::string(process_type);
     58     ReplaceSubstringsAfterOffset(&profile_name, 0, "{type}", type.c_str());
     59   }
     60   return profile_name;
     61 }
     62 
     63 void FlushProfilingData(base::Thread* thread) {
     64   static const int kProfilingFlushSeconds = 10;
     65 
     66   if (!Profiling::BeingProfiled())
     67     return;
     68 
     69   base::debug::FlushProfiling();
     70   static int flush_seconds;
     71   if (!flush_seconds) {
     72     const CommandLine& command_line = *CommandLine::ForCurrentProcess();
     73     std::string profiling_flush =
     74         command_line.GetSwitchValueASCII(switches::kProfilingFlush);
     75     if (!profiling_flush.empty()) {
     76       flush_seconds = atoi(profiling_flush.c_str());
     77       DCHECK(flush_seconds > 0);
     78     } else {
     79       flush_seconds = kProfilingFlushSeconds;
     80     }
     81   }
     82   thread->message_loop()->PostDelayedTask(
     83       FROM_HERE,
     84       base::Bind(&FlushProfilingData, thread),
     85       base::TimeDelta::FromSeconds(flush_seconds));
     86 }
     87 
     88 class ProfilingThreadControl {
     89  public:
     90   ProfilingThreadControl() : thread_(NULL) {}
     91 
     92   void Start() {
     93     base::AutoLock locked(lock_);
     94 
     95     if (thread_ && thread_->IsRunning())
     96       return;
     97     thread_ = new base::Thread("Profiling_Flush");
     98     thread_->Start();
     99     thread_->message_loop()->PostTask(
    100         FROM_HERE, base::Bind(&FlushProfilingData, thread_));
    101   }
    102 
    103   void Stop() {
    104     base::AutoLock locked(lock_);
    105 
    106     if (!thread_ || !thread_->IsRunning())
    107       return;
    108     thread_->Stop();
    109     delete thread_;
    110     thread_ = NULL;
    111   }
    112 
    113  private:
    114   base::Thread* thread_;
    115   base::Lock lock_;
    116 
    117   DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl);
    118 };
    119 
    120 base::LazyInstance<ProfilingThreadControl>::Leaky
    121     g_flush_thread_control = LAZY_INSTANCE_INITIALIZER;
    122 
    123 } // namespace
    124 
    125 // static
    126 void Profiling::ProcessStarted() {
    127   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    128   std::string process_type =
    129       command_line.GetSwitchValueASCII(switches::kProcessType);
    130 
    131   // Establish the V8 profiling hooks if we're an instrumented binary.
    132   if (base::debug::IsBinaryInstrumented()) {
    133     base::debug::ReturnAddressLocationResolver resolve_func =
    134         base::debug::GetProfilerReturnAddrResolutionFunc();
    135 
    136     if (resolve_func != NULL) {
    137       v8::V8::SetReturnAddressLocationResolver(resolve_func);
    138     }
    139 
    140     // Set up the JIT code entry handler and the symbol callbacks if the
    141     // profiler supplies them.
    142     // TODO(siggi): Maybe add a switch or an environment variable to turn off
    143     //     V8 profiling?
    144     base::debug::DynamicFunctionEntryHook entry_hook_func =
    145         base::debug::GetProfilerDynamicFunctionEntryHookFunc();
    146     add_dynamic_symbol_func = base::debug::GetProfilerAddDynamicSymbolFunc();
    147     move_dynamic_symbol_func = base::debug::GetProfilerMoveDynamicSymbolFunc();
    148 
    149     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    150     if (isolate != NULL &&
    151         entry_hook_func != NULL &&
    152         add_dynamic_symbol_func != NULL &&
    153         move_dynamic_symbol_func != NULL) {
    154       v8::V8::SetFunctionEntryHook(isolate, entry_hook_func);
    155       v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault,
    156                                      &JitCodeEventHandler);
    157     }
    158   }
    159 
    160   if (command_line.HasSwitch(switches::kProfilingAtStart)) {
    161     std::string process_type_to_start =
    162         command_line.GetSwitchValueASCII(switches::kProfilingAtStart);
    163     if (process_type == process_type_to_start)
    164       Start();
    165   }
    166 }
    167 
    168 // static
    169 void Profiling::Start() {
    170   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    171   bool flush = command_line.HasSwitch(switches::kProfilingFlush);
    172   base::debug::StartProfiling(GetProfileName());
    173 
    174   // Schedule profile data flushing for single process because it doesn't
    175   // get written out correctly on exit.
    176   if (flush)
    177     g_flush_thread_control.Get().Start();
    178 }
    179 
    180 // static
    181 void Profiling::Stop() {
    182   g_flush_thread_control.Get().Stop();
    183   base::debug::StopProfiling();
    184 }
    185 
    186 // static
    187 bool Profiling::BeingProfiled() {
    188   return base::debug::BeingProfiled();
    189 }
    190 
    191 // static
    192 void Profiling::Toggle() {
    193   if (BeingProfiled())
    194     Stop();
    195   else
    196     Start();
    197 }
    198