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 18namespace { 19 20base::debug::AddDynamicSymbol add_dynamic_symbol_func = NULL; 21base::debug::MoveDynamicSymbol move_dynamic_symbol_func = NULL; 22 23void 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 44std::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 63void 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 88class 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 120base::LazyInstance<ProfilingThreadControl>::Leaky 121 g_flush_thread_control = LAZY_INSTANCE_INITIALIZER; 122 123} // namespace 124 125// static 126void 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 169void 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 181void Profiling::Stop() { 182 g_flush_thread_control.Get().Stop(); 183 base::debug::StopProfiling(); 184} 185 186// static 187bool Profiling::BeingProfiled() { 188 return base::debug::BeingProfiled(); 189} 190 191// static 192void Profiling::Toggle() { 193 if (BeingProfiled()) 194 Stop(); 195 else 196 Start(); 197} 198