Commit 21a43cf7 authored by wittman's avatar wittman Committed by Commit bot

Revert of Temporary commit to evaluate perf impact of prototype CPU profiler...

Revert of Temporary commit to evaluate perf impact of prototype CPU profiler (patchset #10 id:170001 of https://codereview.chromium.org/888923004/)

Reason for revert:
Reverting temporary commit.

Original issue's description:
> Temporary commit to evaluate perf impact of prototype CPU profiler
>
> Design doc: https://docs.google.com/document/d/1Bv-I1yM9AjgY3t27ixdyWHCAW9IpVx02YQ-nSbIqccc
>
> BUG=
>
> Committed: https://crrev.com/47c6093645b62fbc9f68cf50375fc0626e32516c
> Cr-Commit-Position: refs/heads/master@{#315196}

TBR=cpu@chromium.org,wfh@chromium.org,vadimt@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=

Review URL: https://codereview.chromium.org/904233002

Cr-Commit-Position: refs/heads/master@{#315217}
parent 6004fe01
...@@ -505,10 +505,6 @@ component("base") { ...@@ -505,10 +505,6 @@ component("base") {
"process/process_win.cc", "process/process_win.cc",
"profiler/alternate_timer.cc", "profiler/alternate_timer.cc",
"profiler/alternate_timer.h", "profiler/alternate_timer.h",
"profiler/cpu_profiler.cc",
"profiler/cpu_profiler.h",
"profiler/cpu_profiler_posix.cc",
"profiler/cpu_profiler_win.cc",
"profiler/scoped_profile.cc", "profiler/scoped_profile.cc",
"profiler/scoped_profile.h", "profiler/scoped_profile.h",
"profiler/scoped_tracker.cc", "profiler/scoped_tracker.cc",
......
...@@ -497,10 +497,6 @@ ...@@ -497,10 +497,6 @@
'process/process_win.cc', 'process/process_win.cc',
'profiler/alternate_timer.cc', 'profiler/alternate_timer.cc',
'profiler/alternate_timer.h', 'profiler/alternate_timer.h',
'profiler/cpu_profiler.cc',
'profiler/cpu_profiler.h',
'profiler/cpu_profiler_posix.cc',
'profiler/cpu_profiler_win.cc',
'profiler/scoped_profile.cc', 'profiler/scoped_profile.cc',
'profiler/scoped_profile.h', 'profiler/scoped_profile.h',
'profiler/scoped_tracker.cc', 'profiler/scoped_tracker.cc',
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/cpu_profiler.h"
#include <stddef.h>
#include "base/debug/stack_trace.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
namespace base {
namespace {
const char kMode[] = "Mode";
const char kInitialDelay[] = "InitialDelay";
const char kNumberOfBursts[] = "NumberOfBursts";
const char kBurstIdleTime[] = "IdleTime";
const char kNumberOfSamples[] = "NumberOfSamples";
const char kSamplingSleepTime[] = "SamplingSleepTime";
} // namespace
class CpuProfiler::SamplingThread : public PlatformThread::Delegate {
public:
SamplingThread(CpuProfiler* cpu_profiler);
~SamplingThread() override;
// Implementation of PlatformThread::Delegate:
void ThreadMain() override;
void Stop();
void GetSamples();
private:
CpuProfiler* cpu_profiler_;
bool thread_running_;
base::WaitableEvent waitable_event_;
DISALLOW_COPY_AND_ASSIGN(SamplingThread);
};
CpuProfiler::SamplingThread::SamplingThread(CpuProfiler* cpu_profiler)
: cpu_profiler_(cpu_profiler),
thread_running_(false),
waitable_event_(false, false) {
}
CpuProfiler::SamplingThread::~SamplingThread() {}
void CpuProfiler::SamplingThread::ThreadMain() {
PlatformThread::SetName("Chrome_CPUProfilerThread");
thread_running_ = true;
if (cpu_profiler_->GetStringParam(kMode) == "bursts") {
int64 initial_delay = cpu_profiler_->GetInt64Param(kInitialDelay);
int number_of_bursts = cpu_profiler_->GetIntParam(kNumberOfBursts);
int64 burst_idle_time = cpu_profiler_->GetInt64Param(kBurstIdleTime);
int number_of_samples = cpu_profiler_->GetIntParam(kNumberOfSamples);
int64 sampling_sleep_time =
cpu_profiler_->GetInt64Param(kSamplingSleepTime);
if (waitable_event_.TimedWait(TimeDelta::FromMicroseconds(initial_delay)))
return;
for (int i = 0; i < number_of_bursts; ++i) {
int64 delta = 0;
for (int j = 0; ; ++j) {
base::ElapsedTimer time;
GetSamples();
delta = time.Elapsed().InMicroseconds();
if (j == (number_of_samples - 1))
break;
if (delta < sampling_sleep_time) {
if (waitable_event_.TimedWait(
TimeDelta::FromMicroseconds(sampling_sleep_time - delta)))
return;
} else {
if (waitable_event_.TimedWait(
TimeDelta::FromMicroseconds(sampling_sleep_time)))
return;
}
}
if (waitable_event_.TimedWait(
TimeDelta::FromMicroseconds(burst_idle_time - delta)))
return;
}
}
}
void CpuProfiler::SamplingThread::Stop() {
waitable_event_.Signal();
}
void CpuProfiler::SamplingThread::GetSamples() {
cpu_profiler_->OnTimer();
}
void CpuProfiler::SamplingThreadDeleter::operator()(
SamplingThread* thread) const {
delete thread;
}
void CpuProfiler::Initialize(const std::map<std::string, std::string>* params) {
if (!CpuProfiler::IsPlatformSupported())
return;
std::map<std::string, std::string> default_params;
default_params[kMode] = "bursts";
default_params[kInitialDelay] = "0"; // 0 sec
default_params[kNumberOfBursts] = "1";
default_params[kBurstIdleTime] = "10000000"; // 10 sec
default_params[kNumberOfSamples] = "300";
default_params[kSamplingSleepTime] = "100000"; // 100 ms
if (!params)
SetParams(default_params);
else
SetParams(*params);
sampling_thread_.reset(new SamplingThread(this));
if (!PlatformThread::Create(0, sampling_thread_.get(),
&sampling_thread_handle_)) {
LOG(ERROR) << "failed to create thread";
}
}
void CpuProfiler::Stop() {
if (sampling_thread_)
sampling_thread_->Stop();
}
std::string CpuProfiler::GetStringParam(const std::string& key) const{
const auto entry = params_.find(key);
if (entry != params_.end()) {
return entry->second;
}
return "";
}
int CpuProfiler::GetIntParam(const std::string& key) const {
const auto entry = params_.find(key);
if (entry != params_.end()) {
int output;
if (base::StringToInt(entry->second, &output))
return output;
}
return 0;
}
int64 CpuProfiler::GetInt64Param(const std::string& key) const {
const auto entry = params_.find(key);
if (entry != params_.end()) {
int64 output;
if (base::StringToInt64(entry->second, &output))
return output;
}
return 0;
}
void CpuProfiler::SetParams(
const std::map<std::string, std::string>& params) {
params_ = params;
}
} // namespace base
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROFILER_CPU_PROFILER_H_
#define BASE_PROFILER_CPU_PROFILER_H_
#include <map>
#include "base/base_export.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "base/synchronization/waitable_event.h"
#include "base/timer/timer.h"
namespace base {
#if defined(OS_WIN)
struct StackTraceEntry {
DWORD64 rsp;
DWORD64 rip;
HMODULE module;
};
#endif
// CpuProfiler is a prototype of a technique that periodically stops the main
// thread to samples its stack. It does not do anything useful with the
// collected information yet; this implementation is for the purposes of
// determining perf impact of the profiler itself.
//
// Currently, this is intended to sample the main thread. It should be allocated
// on that thread, then Initialize should be called to start the sampling, and
// Stop called to stop the sampling.
class BASE_EXPORT CpuProfiler {
public:
CpuProfiler();
~CpuProfiler();
void Initialize(const std::map<std::string, std::string>* params);
void Stop();
std::string GetStringParam(const std::string& key) const;
int GetIntParam(const std::string& key) const;
int64 GetInt64Param(const std::string& key) const;
private:
class SamplingThread;
struct SamplingThreadDeleter {
void operator() (SamplingThread* thread) const;
};
static bool IsPlatformSupported();
void SetParams(const std::map<std::string, std::string>& params);
void OnTimer();
#if defined(OS_WIN)
void ProcessStack(StackTraceEntry* stack, int stack_depth);
HANDLE main_thread_;
int main_thread_stack_depth_;
StackTraceEntry main_thread_stack_[64];
std::map<HMODULE, base::string16> modules_;
#endif
std::map<std::string, std::string> params_;
scoped_ptr<SamplingThread, SamplingThreadDeleter> sampling_thread_;
PlatformThreadHandle sampling_thread_handle_;
DISALLOW_COPY_AND_ASSIGN(CpuProfiler);
};
} // namespace base
#endif // BASE_PROFILER_CPU_PROFILER_H_
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/cpu_profiler.h"
namespace base {
CpuProfiler::CpuProfiler() {}
CpuProfiler::~CpuProfiler() {}
// static
bool CpuProfiler::IsPlatformSupported() {
return false;
}
void CpuProfiler::OnTimer() {}
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/cpu_profiler.h"
#include <Tlhelp32.h>
#include <dbghelp.h>
#include <stddef.h>
#include <windows.h>
#include "base/debug/stack_trace.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/time/time.h"
namespace base {
namespace {
const DWORD kSuspendFailed = static_cast<DWORD>(-1);
int StackTrace64(CONTEXT* context, int max_stack_size,
StackTraceEntry* stack_trace,
bool* last_frame_is_unknown_function) {
#ifdef _WIN64
*last_frame_is_unknown_function = false;
IMAGEHLP_SYMBOL64 sym;
sym.SizeOfStruct = sizeof(sym);
sym.MaxNameLength = 0;
for (int i = 0; i < max_stack_size; ++i, ++stack_trace) {
// Try to look up unwind metadata for the current function.
ULONG64 image_base;
PRUNTIME_FUNCTION runtime_function =
RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
stack_trace->rsp = context->Rsp;
stack_trace->rip = context->Rip;
stack_trace->module = nullptr;
if (runtime_function) {
KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0};
void* handler_data;
ULONG64 establisher_frame;
RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
&handler_data, &establisher_frame, &nvcontext);
} else {
// If we don't have a RUNTIME_FUNCTION, then we've encountered
// a leaf function. Adjust the stack appropriately.
context->Rip = *reinterpret_cast<PDWORD64>(context->Rsp);
context->Rsp += 8;
*last_frame_is_unknown_function = true;
}
if (!context->Rip)
return i;
}
return max_stack_size;
#else
return 0;
#endif
}
// Look up modules from instruction pointers.
void FindModulesForIPs(StackTraceEntry* stack_trace, int stack_depth,
bool last_frame_is_unknown_function) {
const int module_frames = last_frame_is_unknown_function ? stack_depth - 1 :
stack_depth;
for (int i = 0; i < module_frames; ++i) {
HMODULE module = NULL;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCTSTR>(stack_trace[i].rip),
&module)) {
stack_trace->module = module;
}
}
}
int SampleThread(HANDLE thread_handle, int max_stack_size,
StackTraceEntry* stack) {
if (::SuspendThread(thread_handle) == kSuspendFailed) {
LOG(ERROR) << "SuspendThread failed: " << GetLastError();
return 0;
}
CONTEXT thread_context = {0};
thread_context.ContextFlags = CONTEXT_ALL;
if (!::GetThreadContext(thread_handle, &thread_context)) {
LOG(ERROR) << "GetThreadContext failed: " << GetLastError();
}
bool last_frame_is_unknown_function = false;
int stack_depth = StackTrace64(&thread_context, max_stack_size, stack,
&last_frame_is_unknown_function);
if (::ResumeThread(thread_handle) == kSuspendFailed) {
LOG(ERROR) << "ResumeThread failed: " << GetLastError();
}
FindModulesForIPs(stack, stack_depth, last_frame_is_unknown_function);
return stack_depth;
}
void GetNames(StackTraceEntry* stack_trace, int stack_depth) {
HANDLE process = GetCurrentProcess();
DWORD64 displacement = 0;
DWORD line_displacement = 0;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(wchar_t)];
SYMBOL_INFO* symbol_info = reinterpret_cast<SYMBOL_INFO*>(buffer);
symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol_info->MaxNameLen = MAX_SYM_NAME;
std::string file_name;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (int i = 0; i < stack_depth; ++i, ++stack_trace) {
DWORD64 address = stack_trace->rip;
if (!SymFromAddr(process, address, &displacement, symbol_info)) {
wcscpy_s(reinterpret_cast<wchar_t*>(symbol_info->Name),
symbol_info->MaxNameLen, L"failed");
}
if (!SymGetLineFromAddr64(process, address, &line_displacement, &line)) {
line.LineNumber = 0;
file_name.clear();
} else {
file_name.assign(line.FileName);
}
}
}
}
CpuProfiler::CpuProfiler()
: main_thread_(0),
main_thread_stack_depth_(0) {
#ifdef _WIN64
// Record the main thread's handle.
BOOL result = ::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
::GetCurrentProcess(), &main_thread_, 0,
FALSE, DUPLICATE_SAME_ACCESS);
DCHECK(result) << "DuplicateHandle failed";
// Initialization
if (RtlVirtualUnwind == NULL && RtlLookupFunctionEntry == NULL) {
const HMODULE nt_dll_handle = ::GetModuleHandle(L"ntdll.dll");
reinterpret_cast<void*&>(RtlVirtualUnwind) =
::GetProcAddress(nt_dll_handle, "RtlVirtualUnwind");
reinterpret_cast<void*&>(RtlLookupFunctionEntry) =
::GetProcAddress(nt_dll_handle, "RtlLookupFunctionEntry");
}
#endif
}
CpuProfiler::~CpuProfiler() {
#ifdef _WIN64
CloseHandle(main_thread_);
#endif
}
// static
bool CpuProfiler::IsPlatformSupported() {
#ifdef _WIN64
return true;
#else
return false;
#endif
}
void CpuProfiler::OnTimer() {
main_thread_stack_depth_ = SampleThread(
main_thread_, arraysize(main_thread_stack_), main_thread_stack_);
ProcessStack(main_thread_stack_, main_thread_stack_depth_);
modules_.clear();
main_thread_stack_depth_ = 0;
}
void CpuProfiler::ProcessStack(StackTraceEntry* stack, int stack_depth) {
for (int i = 0; i < stack_depth; ++i, ++stack) {
HMODULE module = stack->module;
if (!module || (modules_.find(module) != modules_.end()))
continue;
wchar_t module_name[MAX_PATH];
if (GetModuleFileName(module, module_name, arraysize(module_name))) {
modules_.insert(std::pair<HMODULE, base::string16>(module, module_name));
}
}
}
} // namespace base
...@@ -994,8 +994,6 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() { ...@@ -994,8 +994,6 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() {
// IOThread's initialization which happens in BrowserProcess:PreCreateThreads. // IOThread's initialization which happens in BrowserProcess:PreCreateThreads.
SetupMetricsAndFieldTrials(); SetupMetricsAndFieldTrials();
cpu_profiler_.Initialize(nullptr);
// ChromeOS needs ResourceBundle::InitSharedInstance to be called before this. // ChromeOS needs ResourceBundle::InitSharedInstance to be called before this.
browser_process_->PreCreateThreads(); browser_process_->PreCreateThreads();
...@@ -1650,8 +1648,6 @@ void ChromeBrowserMainParts::PostMainMessageLoopRun() { ...@@ -1650,8 +1648,6 @@ void ChromeBrowserMainParts::PostMainMessageLoopRun() {
NOTREACHED(); NOTREACHED();
#else #else
cpu_profiler_.Stop();
// Start watching for jank during shutdown. It gets disarmed when // Start watching for jank during shutdown. It gets disarmed when
// |shutdown_watcher_| object is destructed. // |shutdown_watcher_| object is destructed.
metrics::MetricsService::SetExecutionPhase( metrics::MetricsService::SetExecutionPhase(
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/profiler/cpu_profiler.h"
#include "base/tracked_objects.h" #include "base/tracked_objects.h"
#include "chrome/browser/chrome_browser_field_trials.h" #include "chrome/browser/chrome_browser_field_trials.h"
#include "chrome/browser/chrome_process_singleton.h" #include "chrome/browser/chrome_process_singleton.h"
...@@ -155,9 +154,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -155,9 +154,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Parts are deleted in the inverse order they are added. // Parts are deleted in the inverse order they are added.
std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_;
// A profiler that periodically samples stack traces.
base::CpuProfiler cpu_profiler_;
// Members initialized after / released before main_message_loop_ ------------ // Members initialized after / released before main_message_loop_ ------------
scoped_ptr<BrowserProcessImpl> browser_process_; scoped_ptr<BrowserProcessImpl> browser_process_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment