Chromium Code Reviews| Index: chrome/common/profiling.cc |
| diff --git a/chrome/common/profiling.cc b/chrome/common/profiling.cc |
| index 1acb43fea13a9d4e0e4bb58ffa0d61710f461cba..dc8c79b3429ecbbb610b9d0287683384aeaa0f10 100644 |
| --- a/chrome/common/profiling.cc |
| +++ b/chrome/common/profiling.cc |
| @@ -1,154 +1,194 @@ |
| -// 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.h" |
| -#include "base/strings/string_util.h" |
| -#include "base/threading/thread.h" |
| -#include "chrome/common/chrome_switches.h" |
| -#include "v8/include/v8.h" |
| - |
| -namespace { |
| -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 return address resolution hook 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); |
| - } |
| - } |
| - |
| - 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(); |
| -} |
| +// 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.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; |
| + } |
| +} |
| + |
| +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(); |
|
Nico
2013/07/03 19:26:38
nit: can't you just call this in JitCodeEventHandl
Sigurður Ásgeirsson
2013/07/03 19:35:58
Retrieving these functions is relatively expensive
|
| + |
| + 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(); |
| +} |