// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/common/profiling.h" #include "base/at_exit.h" #include "base/bind.h" #include "base/command_line.h" #include "base/debug/profiler.h" #include "base/lazy_instance.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/threading/thread.h" #include "chrome/common/chrome_switches.h" #include "v8/include/v8.h" namespace { base::debug::AddDynamicSymbol add_dynamic_symbol_func = NULL; base::debug::MoveDynamicSymbol move_dynamic_symbol_func = NULL; void JitCodeEventHandler(const v8::JitCodeEvent* event) { DCHECK_NE(static_cast<base::debug::AddDynamicSymbol>(NULL), add_dynamic_symbol_func); DCHECK_NE(static_cast<base::debug::MoveDynamicSymbol>(NULL), move_dynamic_symbol_func); switch (event->type) { case v8::JitCodeEvent::CODE_ADDED: add_dynamic_symbol_func(event->code_start, event->code_len, event->name.str, event->name.len); break; case v8::JitCodeEvent::CODE_MOVED: move_dynamic_symbol_func(event->code_start, event->new_code_start); break; default: break; } } std::string GetProfileName() { static const char kDefaultProfileName[] = "chrome-profile-{type}-{pid}"; CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ()); if (profile_name.empty()) { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kProfilingFile)) profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile); else profile_name = std::string(kDefaultProfileName); std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); std::string type = process_type.empty() ? std::string("browser") : std::string(process_type); ReplaceSubstringsAfterOffset(&profile_name, 0, "{type}", type.c_str()); } return profile_name; } void FlushProfilingData(base::Thread* thread) { static const int kProfilingFlushSeconds = 10; if (!Profiling::BeingProfiled()) return; base::debug::FlushProfiling(); static int flush_seconds; if (!flush_seconds) { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); std::string profiling_flush = command_line.GetSwitchValueASCII(switches::kProfilingFlush); if (!profiling_flush.empty()) { flush_seconds = atoi(profiling_flush.c_str()); DCHECK(flush_seconds > 0); } else { flush_seconds = kProfilingFlushSeconds; } } thread->message_loop()->PostDelayedTask( FROM_HERE, base::Bind(&FlushProfilingData, thread), base::TimeDelta::FromSeconds(flush_seconds)); } class ProfilingThreadControl { public: ProfilingThreadControl() : thread_(NULL) {} void Start() { base::AutoLock locked(lock_); if (thread_ && thread_->IsRunning()) return; thread_ = new base::Thread("Profiling_Flush"); thread_->Start(); thread_->message_loop()->PostTask( FROM_HERE, base::Bind(&FlushProfilingData, thread_)); } void Stop() { base::AutoLock locked(lock_); if (!thread_ || !thread_->IsRunning()) return; thread_->Stop(); delete thread_; thread_ = NULL; } private: base::Thread* thread_; base::Lock lock_; DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl); }; base::LazyInstance<ProfilingThreadControl>::Leaky g_flush_thread_control = LAZY_INSTANCE_INITIALIZER; } // namespace // static void Profiling::ProcessStarted() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); // Establish the V8 profiling hooks if we're an instrumented binary. if (base::debug::IsBinaryInstrumented()) { base::debug::ReturnAddressLocationResolver resolve_func = base::debug::GetProfilerReturnAddrResolutionFunc(); if (resolve_func != NULL) { v8::V8::SetReturnAddressLocationResolver(resolve_func); } // Set up the JIT code entry handler and the symbol callbacks if the // profiler supplies them. // TODO(siggi): Maybe add a switch or an environment variable to turn off // V8 profiling? base::debug::DynamicFunctionEntryHook entry_hook_func = base::debug::GetProfilerDynamicFunctionEntryHookFunc(); add_dynamic_symbol_func = base::debug::GetProfilerAddDynamicSymbolFunc(); move_dynamic_symbol_func = base::debug::GetProfilerMoveDynamicSymbolFunc(); v8::Isolate* isolate = v8::Isolate::GetCurrent(); if (isolate != NULL && entry_hook_func != NULL && add_dynamic_symbol_func != NULL && move_dynamic_symbol_func != NULL) { v8::V8::SetFunctionEntryHook(isolate, entry_hook_func); v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, &JitCodeEventHandler); } } if (command_line.HasSwitch(switches::kProfilingAtStart)) { std::string process_type_to_start = command_line.GetSwitchValueASCII(switches::kProfilingAtStart); if (process_type == process_type_to_start) Start(); } } // static void Profiling::Start() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); bool flush = command_line.HasSwitch(switches::kProfilingFlush); base::debug::StartProfiling(GetProfileName()); // Schedule profile data flushing for single process because it doesn't // get written out correctly on exit. if (flush) g_flush_thread_control.Get().Start(); } // static void Profiling::Stop() { g_flush_thread_control.Get().Stop(); base::debug::StopProfiling(); } // static bool Profiling::BeingProfiled() { return base::debug::BeingProfiled(); } // static void Profiling::Toggle() { if (BeingProfiled()) Stop(); else Start(); }