// 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();
}