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