profiling.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 "gin/public/debug.h"
17#include "v8/include/v8.h"
18
19namespace {
20
21base::debug::AddDynamicSymbol add_dynamic_symbol_func = NULL;
22base::debug::MoveDynamicSymbol move_dynamic_symbol_func = NULL;
23
24void JitCodeEventHandler(const v8::JitCodeEvent* event) {
25  DCHECK_NE(static_cast<base::debug::AddDynamicSymbol>(NULL),
26            add_dynamic_symbol_func);
27  DCHECK_NE(static_cast<base::debug::MoveDynamicSymbol>(NULL),
28            move_dynamic_symbol_func);
29
30  switch (event->type) {
31    case v8::JitCodeEvent::CODE_ADDED:
32      add_dynamic_symbol_func(event->code_start, event->code_len,
33                              event->name.str, event->name.len);
34      break;
35
36    case v8::JitCodeEvent::CODE_MOVED:
37      move_dynamic_symbol_func(event->code_start, event->new_code_start);
38      break;
39
40    default:
41      break;
42  }
43}
44
45std::string GetProfileName() {
46  static const char kDefaultProfileName[] = "chrome-profile-{type}-{pid}";
47  CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ());
48
49  if (profile_name.empty()) {
50    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
51    if (command_line.HasSwitch(switches::kProfilingFile))
52      profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile);
53    else
54      profile_name = std::string(kDefaultProfileName);
55    std::string process_type =
56        command_line.GetSwitchValueASCII(switches::kProcessType);
57    std::string type = process_type.empty() ?
58        std::string("browser") : std::string(process_type);
59    ReplaceSubstringsAfterOffset(&profile_name, 0, "{type}", type.c_str());
60  }
61  return profile_name;
62}
63
64void FlushProfilingData(base::Thread* thread) {
65  static const int kProfilingFlushSeconds = 10;
66
67  if (!Profiling::BeingProfiled())
68    return;
69
70  base::debug::FlushProfiling();
71  static int flush_seconds;
72  if (!flush_seconds) {
73    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
74    std::string profiling_flush =
75        command_line.GetSwitchValueASCII(switches::kProfilingFlush);
76    if (!profiling_flush.empty()) {
77      flush_seconds = atoi(profiling_flush.c_str());
78      DCHECK(flush_seconds > 0);
79    } else {
80      flush_seconds = kProfilingFlushSeconds;
81    }
82  }
83  thread->message_loop()->PostDelayedTask(
84      FROM_HERE,
85      base::Bind(&FlushProfilingData, thread),
86      base::TimeDelta::FromSeconds(flush_seconds));
87}
88
89class ProfilingThreadControl {
90 public:
91  ProfilingThreadControl() : thread_(NULL) {}
92
93  void Start() {
94    base::AutoLock locked(lock_);
95
96    if (thread_ && thread_->IsRunning())
97      return;
98    thread_ = new base::Thread("Profiling_Flush");
99    thread_->Start();
100    thread_->message_loop()->PostTask(
101        FROM_HERE, base::Bind(&FlushProfilingData, thread_));
102  }
103
104  void Stop() {
105    base::AutoLock locked(lock_);
106
107    if (!thread_ || !thread_->IsRunning())
108      return;
109    thread_->Stop();
110    delete thread_;
111    thread_ = NULL;
112  }
113
114 private:
115  base::Thread* thread_;
116  base::Lock lock_;
117
118  DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl);
119};
120
121base::LazyInstance<ProfilingThreadControl>::Leaky
122    g_flush_thread_control = LAZY_INSTANCE_INITIALIZER;
123
124} // namespace
125
126// static
127void Profiling::ProcessStarted() {
128  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
129  std::string process_type =
130      command_line.GetSwitchValueASCII(switches::kProcessType);
131
132  // Establish the V8 profiling hooks if we're an instrumented binary.
133  if (base::debug::IsBinaryInstrumented()) {
134    base::debug::ReturnAddressLocationResolver resolve_func =
135        base::debug::GetProfilerReturnAddrResolutionFunc();
136
137    if (resolve_func != NULL) {
138      v8::V8::SetReturnAddressLocationResolver(resolve_func);
139    }
140
141    // Set up the JIT code entry handler and the symbol callbacks if the
142    // profiler supplies them.
143    // TODO(siggi): Maybe add a switch or an environment variable to turn off
144    //     V8 profiling?
145    base::debug::DynamicFunctionEntryHook entry_hook_func =
146        base::debug::GetProfilerDynamicFunctionEntryHookFunc();
147    add_dynamic_symbol_func = base::debug::GetProfilerAddDynamicSymbolFunc();
148    move_dynamic_symbol_func = base::debug::GetProfilerMoveDynamicSymbolFunc();
149
150    if (entry_hook_func != NULL &&
151        add_dynamic_symbol_func != NULL &&
152        move_dynamic_symbol_func != NULL) {
153      gin::Debug::SetFunctionEntryHook(entry_hook_func);
154      gin::Debug::SetJitCodeEventHandler(&JitCodeEventHandler);
155    }
156  }
157
158  if (command_line.HasSwitch(switches::kProfilingAtStart)) {
159    std::string process_type_to_start =
160        command_line.GetSwitchValueASCII(switches::kProfilingAtStart);
161    if (process_type == process_type_to_start)
162      Start();
163  }
164}
165
166// static
167void Profiling::Start() {
168  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
169  bool flush = command_line.HasSwitch(switches::kProfilingFlush);
170  base::debug::StartProfiling(GetProfileName());
171
172  // Schedule profile data flushing for single process because it doesn't
173  // get written out correctly on exit.
174  if (flush)
175    g_flush_thread_control.Get().Start();
176}
177
178// static
179void Profiling::Stop() {
180  g_flush_thread_control.Get().Stop();
181  base::debug::StopProfiling();
182}
183
184// static
185bool Profiling::BeingProfiled() {
186  return base::debug::BeingProfiled();
187}
188
189// static
190void Profiling::Toggle() {
191  if (BeingProfiled())
192    Stop();
193  else
194    Start();
195}
196