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