15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/profiling.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/at_exit.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/profiler.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/lazy_instance.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "gin/public/debug.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "v8/include/v8.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbase::debug::AddDynamicSymbol add_dynamic_symbol_func = NULL;
227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbase::debug::MoveDynamicSymbol move_dynamic_symbol_func = NULL;
237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid JitCodeEventHandler(const v8::JitCodeEvent* event) {
257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  DCHECK_NE(static_cast<base::debug::AddDynamicSymbol>(NULL),
267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            add_dynamic_symbol_func);
277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  DCHECK_NE(static_cast<base::debug::MoveDynamicSymbol>(NULL),
287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            move_dynamic_symbol_func);
297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  switch (event->type) {
317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    case v8::JitCodeEvent::CODE_ADDED:
327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      add_dynamic_symbol_func(event->code_start, event->code_len,
337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                              event->name.str, event->name.len);
347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      break;
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    case v8::JitCodeEvent::CODE_MOVED:
377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      move_dynamic_symbol_func(event->code_start, event->new_code_start);
387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      break;
397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    default:
417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      break;
427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string GetProfileName() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kDefaultProfileName[] = "chrome-profile-{type}-{pid}";
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ());
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_name.empty()) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command_line.HasSwitch(switches::kProfilingFile))
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_name = std::string(kDefaultProfileName);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string process_type =
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        command_line.GetSwitchValueASCII(switches::kProcessType);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string type = process_type.empty() ?
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::string("browser") : std::string(process_type);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ReplaceSubstringsAfterOffset(&profile_name, 0, "{type}", type.c_str());
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_name;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FlushProfilingData(base::Thread* thread) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kProfilingFlushSeconds = 10;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!Profiling::BeingProfiled())
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::debug::FlushProfiling();
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int flush_seconds;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!flush_seconds) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string profiling_flush =
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        command_line.GetSwitchValueASCII(switches::kProfilingFlush);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!profiling_flush.empty()) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flush_seconds = atoi(profiling_flush.c_str());
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(flush_seconds > 0);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flush_seconds = kProfilingFlushSeconds;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  thread->message_loop()->PostDelayedTask(
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&FlushProfilingData, thread),
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::TimeDelta::FromSeconds(flush_seconds));
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ProfilingThreadControl {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfilingThreadControl() : thread_(NULL) {}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Start() {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock locked(lock_);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (thread_ && thread_->IsRunning())
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_ = new base::Thread("Profiling_Flush");
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_->Start();
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_->message_loop()->PostTask(
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE, base::Bind(&FlushProfilingData, thread_));
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Stop() {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock locked(lock_);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!thread_ || !thread_->IsRunning())
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_->Stop();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete thread_;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_ = NULL;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Thread* thread_;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Lock lock_;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::LazyInstance<ProfilingThreadControl>::Leaky
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_flush_thread_control = LAZY_INSTANCE_INITIALIZER;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Profiling::ProcessStarted() {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string process_type =
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_line.GetSwitchValueASCII(switches::kProcessType);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // Establish the V8 profiling hooks if we're an instrumented binary.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::debug::IsBinaryInstrumented()) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::debug::ReturnAddressLocationResolver resolve_func =
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::debug::GetProfilerReturnAddrResolutionFunc();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (resolve_func != NULL) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      v8::V8::SetReturnAddressLocationResolver(resolve_func);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // Set up the JIT code entry handler and the symbol callbacks if the
1427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // profiler supplies them.
1437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // TODO(siggi): Maybe add a switch or an environment variable to turn off
1447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    //     V8 profiling?
1457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::debug::DynamicFunctionEntryHook entry_hook_func =
1467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        base::debug::GetProfilerDynamicFunctionEntryHookFunc();
1477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    add_dynamic_symbol_func = base::debug::GetProfilerAddDynamicSymbolFunc();
1487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    move_dynamic_symbol_func = base::debug::GetProfilerMoveDynamicSymbolFunc();
1497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (entry_hook_func != NULL &&
1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        add_dynamic_symbol_func != NULL &&
1527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        move_dynamic_symbol_func != NULL) {
1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gin::Debug::SetFunctionEntryHook(entry_hook_func);
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gin::Debug::SetJitCodeEventHandler(&JitCodeEventHandler);
1557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line.HasSwitch(switches::kProfilingAtStart)) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string process_type_to_start =
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        command_line.GetSwitchValueASCII(switches::kProfilingAtStart);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (process_type == process_type_to_start)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Start();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Profiling::Start() {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool flush = command_line.HasSwitch(switches::kProfilingFlush);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::debug::StartProfiling(GetProfileName());
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Schedule profile data flushing for single process because it doesn't
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // get written out correctly on exit.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (flush)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_flush_thread_control.Get().Start();
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Profiling::Stop() {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_flush_thread_control.Get().Stop();
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::debug::StopProfiling();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Profiling::BeingProfiled() {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base::debug::BeingProfiled();
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Profiling::Toggle() {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (BeingProfiled())
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Stop();
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Start();
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
196