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.
5 #include "chrome/common/profiling.h"
7 #include "base/at_exit.h"
9 #include "base/command_line.h"
10 #include "base/debug/profiler.h"
11 #include "base/lazy_instance.h"
12 #include "base/location.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/threading/thread.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "gin/public/debug.h"
18 #include "v8/include/v8.h"
22 base::debug::AddDynamicSymbol add_dynamic_symbol_func
= NULL
;
23 base::debug::MoveDynamicSymbol move_dynamic_symbol_func
= NULL
;
25 void JitCodeEventHandler(const v8::JitCodeEvent
* event
) {
26 DCHECK_NE(static_cast<base::debug::AddDynamicSymbol
>(NULL
),
27 add_dynamic_symbol_func
);
28 DCHECK_NE(static_cast<base::debug::MoveDynamicSymbol
>(NULL
),
29 move_dynamic_symbol_func
);
31 switch (event
->type
) {
32 case v8::JitCodeEvent::CODE_ADDED
:
33 add_dynamic_symbol_func(event
->code_start
, event
->code_len
,
34 event
->name
.str
, event
->name
.len
);
37 case v8::JitCodeEvent::CODE_MOVED
:
38 move_dynamic_symbol_func(event
->code_start
, event
->new_code_start
);
46 std::string
GetProfileName() {
47 static const char kDefaultProfileName
[] = "chrome-profile-{type}-{pid}";
48 CR_DEFINE_STATIC_LOCAL(std::string
, profile_name
, ());
50 if (profile_name
.empty()) {
51 const base::CommandLine
& command_line
=
52 *base::CommandLine::ForCurrentProcess();
53 if (command_line
.HasSwitch(switches::kProfilingFile
))
54 profile_name
= command_line
.GetSwitchValueASCII(switches::kProfilingFile
);
56 profile_name
= std::string(kDefaultProfileName
);
57 std::string process_type
=
58 command_line
.GetSwitchValueASCII(switches::kProcessType
);
59 std::string type
= process_type
.empty() ?
60 std::string("browser") : std::string(process_type
);
61 base::ReplaceSubstringsAfterOffset(&profile_name
, 0, "{type}", type
);
66 void FlushProfilingData(base::Thread
* thread
) {
67 static const int kProfilingFlushSeconds
= 10;
69 if (!Profiling::BeingProfiled())
72 base::debug::FlushProfiling();
73 static int flush_seconds
;
75 const base::CommandLine
& command_line
=
76 *base::CommandLine::ForCurrentProcess();
77 std::string profiling_flush
=
78 command_line
.GetSwitchValueASCII(switches::kProfilingFlush
);
79 if (!profiling_flush
.empty()) {
80 flush_seconds
= atoi(profiling_flush
.c_str());
81 DCHECK(flush_seconds
> 0);
83 flush_seconds
= kProfilingFlushSeconds
;
86 thread
->task_runner()->PostDelayedTask(
87 FROM_HERE
, base::Bind(&FlushProfilingData
, thread
),
88 base::TimeDelta::FromSeconds(flush_seconds
));
91 class ProfilingThreadControl
{
93 ProfilingThreadControl() : thread_(NULL
) {}
96 base::AutoLock
locked(lock_
);
98 if (thread_
&& thread_
->IsRunning())
100 thread_
= new base::Thread("Profiling_Flush");
102 thread_
->task_runner()->PostTask(FROM_HERE
,
103 base::Bind(&FlushProfilingData
, thread_
));
107 base::AutoLock
locked(lock_
);
109 if (!thread_
|| !thread_
->IsRunning())
117 base::Thread
* thread_
;
120 DISALLOW_COPY_AND_ASSIGN(ProfilingThreadControl
);
123 base::LazyInstance
<ProfilingThreadControl
>::Leaky
124 g_flush_thread_control
= LAZY_INSTANCE_INITIALIZER
;
129 void Profiling::ProcessStarted() {
130 const base::CommandLine
& command_line
=
131 *base::CommandLine::ForCurrentProcess();
132 std::string process_type
=
133 command_line
.GetSwitchValueASCII(switches::kProcessType
);
135 // Establish the V8 profiling hooks if we're an instrumented binary.
136 if (base::debug::IsBinaryInstrumented()) {
137 base::debug::ReturnAddressLocationResolver resolve_func
=
138 base::debug::GetProfilerReturnAddrResolutionFunc();
140 if (resolve_func
!= NULL
) {
141 v8::V8::SetReturnAddressLocationResolver(resolve_func
);
144 // Set up the JIT code entry handler and the symbol callbacks if the
145 // profiler supplies them.
146 // TODO(siggi): Maybe add a switch or an environment variable to turn off
148 base::debug::DynamicFunctionEntryHook entry_hook_func
=
149 base::debug::GetProfilerDynamicFunctionEntryHookFunc();
150 add_dynamic_symbol_func
= base::debug::GetProfilerAddDynamicSymbolFunc();
151 move_dynamic_symbol_func
= base::debug::GetProfilerMoveDynamicSymbolFunc();
153 if (entry_hook_func
!= NULL
&&
154 add_dynamic_symbol_func
!= NULL
&&
155 move_dynamic_symbol_func
!= NULL
) {
156 gin::Debug::SetFunctionEntryHook(entry_hook_func
);
157 gin::Debug::SetJitCodeEventHandler(&JitCodeEventHandler
);
161 if (command_line
.HasSwitch(switches::kProfilingAtStart
)) {
162 std::string process_type_to_start
=
163 command_line
.GetSwitchValueASCII(switches::kProfilingAtStart
);
164 if (process_type
== process_type_to_start
)
170 void Profiling::Start() {
171 const base::CommandLine
& command_line
=
172 *base::CommandLine::ForCurrentProcess();
173 bool flush
= command_line
.HasSwitch(switches::kProfilingFlush
);
174 base::debug::StartProfiling(GetProfileName());
176 // Schedule profile data flushing for single process because it doesn't
177 // get written out correctly on exit.
179 g_flush_thread_control
.Get().Start();
183 void Profiling::Stop() {
184 g_flush_thread_control
.Get().Stop();
185 base::debug::StopProfiling();
189 bool Profiling::BeingProfiled() {
190 return base::debug::BeingProfiled();
194 void Profiling::Toggle() {