profiling.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.h"
13#include "base/string_util.h"
14#include "base/threading/thread.h"
15#include "chrome/common/chrome_switches.h"
16#include "v8/include/v8.h"
17
18namespace {
19std::string GetProfileName() {
20  static const char kDefaultProfileName[] = "chrome-profile-{type}-{pid}";
21  CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ());
22
23  if (profile_name.empty()) {
24    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
25    if (command_line.HasSwitch(switches::kProfilingFile))
26      profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile);
27    else
28      profile_name = std::string(kDefaultProfileName);
29    std::string process_type =
30        command_line.GetSwitchValueASCII(switches::kProcessType);
31    std::string type = process_type.empty() ?
32        std::string("browser") : std::string(process_type);
33    ReplaceSubstringsAfterOffset(&profile_name, 0, "{type}", type.c_str());
34  }
35  return profile_name;
36}
37
38void FlushProfilingData(base::Thread* thread) {
39  static const int kProfilingFlushSeconds = 10;
40
41  if (!Profiling::BeingProfiled())
42    return;
43
44  base::debug::FlushProfiling();
45  static int flush_seconds;
46  if (!flush_seconds) {
47    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
48    std::string profiling_flush =
49        command_line.GetSwitchValueASCII(switches::kProfilingFlush);
50    if (!profiling_flush.empty()) {
51      flush_seconds = atoi(profiling_flush.c_str());
52      DCHECK(flush_seconds > 0);
53    } else {
54      flush_seconds = kProfilingFlushSeconds;
55    }
56  }
57  thread->message_loop()->PostDelayedTask(
58      FROM_HERE,
59      base::Bind(&FlushProfilingData, thread),
60      base::TimeDelta::FromSeconds(flush_seconds));
61}
62
63class ProfilingThreadControl {
64 public:
65  ProfilingThreadControl() : thread_(NULL) {}
66
67  void Start() {
68    base::AutoLock locked(lock_);
69
70    if (thread_ && thread_->IsRunning())
71      return;
72    thread_ = new base::Thread("Profiling_Flush");
73    thread_->Start();
74    thread_->message_loop()->PostTask(
75        FROM_HERE, base::Bind(&FlushProfilingData, thread_));
76  }
77
78  void Stop() {
79    base::AutoLock locked(lock_);
80
81    if (!thread_ || !thread_->IsRunning())
82      return;
83    thread_->Stop();
84    delete thread_;
85    thread_ = NULL;
86  }
87
88 private:
89  base::Thread* thread_;
90  base::Lock lock_;
91
92  DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl);
93};
94
95base::LazyInstance<ProfilingThreadControl>::Leaky
96    g_flush_thread_control = LAZY_INSTANCE_INITIALIZER;
97
98} // namespace
99
100// static
101void Profiling::ProcessStarted() {
102  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
103  std::string process_type =
104      command_line.GetSwitchValueASCII(switches::kProcessType);
105
106  // Establish the V8 return address resolution hook if we're
107  // an instrumented binary.
108  if (base::debug::IsBinaryInstrumented()) {
109    base::debug::ReturnAddressLocationResolver resolve_func =
110        base::debug::GetProfilerReturnAddrResolutionFunc();
111
112    if (resolve_func != NULL) {
113      v8::V8::SetReturnAddressLocationResolver(resolve_func);
114    }
115  }
116
117  if (command_line.HasSwitch(switches::kProfilingAtStart)) {
118    std::string process_type_to_start =
119        command_line.GetSwitchValueASCII(switches::kProfilingAtStart);
120    if (process_type == process_type_to_start)
121      Start();
122  }
123}
124
125// static
126void Profiling::Start() {
127  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
128  bool flush = command_line.HasSwitch(switches::kProfilingFlush);
129  base::debug::StartProfiling(GetProfileName());
130
131  // Schedule profile data flushing for single process because it doesn't
132  // get written out correctly on exit.
133  if (flush)
134    g_flush_thread_control.Get().Start();
135}
136
137// static
138void Profiling::Stop() {
139  g_flush_thread_control.Get().Stop();
140  base::debug::StopProfiling();
141}
142
143// static
144bool Profiling::BeingProfiled() {
145  return base::debug::BeingProfiled();
146}
147
148// static
149void Profiling::Toggle() {
150  if (BeingProfiled())
151    Stop();
152  else
153    Start();
154}
155