[cpu-profiler] Add static CollectSample method to the CpuProfiler API.

The method forces all running profilers attached to the provided isolate
to collect a sample with the current stack.

It is going to be used to synchronize trace events generated by embedder with the samples
collected by the profiler.

Also it will finally allow us to break dependency of isolate on CPU profiler.

BUG=chromium:721099

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I81a0f8a463f837b5201bc8edaf2eb4f3761e3ff8
Reviewed-on: https://chromium-review.googlesource.com/750264
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Alexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49236}
This commit is contained in:
Alexei Filippov 2017-11-06 17:39:59 -08:00 committed by Commit Bot
parent 7e78506fc2
commit 295c9cc643
5 changed files with 105 additions and 7 deletions

View File

@ -286,6 +286,13 @@ class V8_EXPORT CpuProfiler {
*/
static CpuProfiler* New(Isolate* isolate);
/**
* Synchronously collect current stack sample in all profilers attached to
* the |isolate|. The call does not affect number of ticks recorded for
* the current top node.
*/
static void CollectSample(Isolate* isolate);
/**
* Disposes the CPU profiler object.
*/

View File

@ -10508,6 +10508,11 @@ CpuProfiler* CpuProfiler::New(Isolate* isolate) {
void CpuProfiler::Dispose() { delete reinterpret_cast<i::CpuProfiler*>(this); }
// static
void CpuProfiler::CollectSample(Isolate* isolate) {
i::CpuProfiler::CollectSample(reinterpret_cast<i::Isolate*>(isolate));
}
void CpuProfiler::SetSamplingInterval(int us) {
DCHECK_GE(us, 0);
return reinterpret_cast<i::CpuProfiler*>(this)->set_sampling_interval(

View File

@ -4,6 +4,9 @@
#include "src/profiler/cpu-profiler.h"
#include "src/base/lazy-instance.h"
#include "src/base/platform/mutex.h"
#include "src/base/template-utils.h"
#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/frames-inl.h"
@ -241,14 +244,50 @@ void CpuProfiler::CodeEventHandler(const CodeEventsContainer& evt_rec) {
}
}
namespace {
class CpuProfilersManager {
public:
void AddProfiler(Isolate* isolate, CpuProfiler* profiler) {
base::LockGuard<base::Mutex> lock(&mutex_);
auto result = profilers_.insert(
std::pair<Isolate*, std::unique_ptr<std::set<CpuProfiler*>>>(
isolate, base::make_unique<std::set<CpuProfiler*>>()));
result.first->second->insert(profiler);
}
void RemoveProfiler(Isolate* isolate, CpuProfiler* profiler) {
base::LockGuard<base::Mutex> lock(&mutex_);
auto it = profilers_.find(isolate);
DCHECK(it != profilers_.end());
it->second->erase(profiler);
if (it->second->empty()) {
profilers_.erase(it);
}
}
void CallCollectSample(Isolate* isolate) {
base::LockGuard<base::Mutex> lock(&mutex_);
auto profilers = profilers_.find(isolate);
if (profilers == profilers_.end()) return;
for (auto it : *profilers->second) {
it->CollectSample();
}
}
private:
std::map<Isolate*, std::unique_ptr<std::set<CpuProfiler*>>> profilers_;
base::Mutex mutex_;
};
base::LazyInstance<CpuProfilersManager>::type g_profilers_manager =
LAZY_INSTANCE_INITIALIZER;
} // namespace
CpuProfiler::CpuProfiler(Isolate* isolate)
: isolate_(isolate),
sampling_interval_(base::TimeDelta::FromMicroseconds(
FLAG_cpu_profiler_sampling_interval)),
profiles_(new CpuProfilesCollection(isolate)),
is_profiling_(false) {
profiles_->set_cpu_profiler(this);
}
: CpuProfiler(isolate, new CpuProfilesCollection(isolate), nullptr,
nullptr) {}
CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles,
ProfileGenerator* test_generator,
@ -261,10 +300,12 @@ CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles,
processor_(test_processor),
is_profiling_(false) {
profiles_->set_cpu_profiler(this);
g_profilers_manager.Pointer()->AddProfiler(isolate, this);
}
CpuProfiler::~CpuProfiler() {
DCHECK(!is_profiling_);
g_profilers_manager.Pointer()->RemoveProfiler(isolate_, this);
}
void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
@ -292,6 +333,11 @@ void CpuProfiler::CreateEntriesForRuntimeCallStats() {
}
}
// static
void CpuProfiler::CollectSample(Isolate* isolate) {
g_profilers_manager.Pointer()->CallCollectSample(isolate);
}
void CpuProfiler::CollectSample() {
if (processor_) {
processor_->AddCurrentStack(isolate_);

View File

@ -195,6 +195,8 @@ class CpuProfiler : public CodeEventObserver {
~CpuProfiler() override;
static void CollectSample(Isolate* isolate);
void set_sampling_interval(base::TimeDelta value);
void CollectSample();
void StartProfiling(const char* title, bool record_samples = false);

View File

@ -2227,6 +2227,44 @@ TEST(Issue763073) {
cpu_profiler->Dispose();
}
static const char* js_collect_sample_api_source =
"%NeverOptimizeFunction(start);\n"
"function start() {\n"
" CallStaticCollectSample();\n"
"}";
static void CallStaticCollectSample(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::CpuProfiler::CollectSample(info.GetIsolate());
}
TEST(StaticCollectSampleAPI) {
i::FLAG_allow_natives_syntax = true;
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(env->GetIsolate(), CallStaticCollectSample);
v8::Local<v8::Function> func =
func_template->GetFunction(env.local()).ToLocalChecked();
func->SetName(v8_str("CallStaticCollectSample"));
env->Global()
->Set(env.local(), v8_str("CallStaticCollectSample"), func)
.FromJust();
CompileRun(js_collect_sample_api_source);
v8::Local<v8::Function> function = GetFunction(env.local(), "start");
ProfilerHelper helper(env.local());
v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100);
const v8::CpuProfileNode* root = profile->GetTopDownRoot();
const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
GetChild(env.local(), start_node, "CallStaticCollectSample");
profile->Delete();
}
} // namespace test_cpu_profiler
} // namespace internal
} // namespace v8