Commit 7068caf5 authored by alph's avatar alph Committed by Commit bot

Add CollectSample API function to CpuProfiler

It allows embedder to inject a stack sample on demand.

BUG=chromium:579191
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33527}
parent f53d530b
...@@ -206,6 +206,13 @@ class V8_EXPORT CpuProfiler { ...@@ -206,6 +206,13 @@ class V8_EXPORT CpuProfiler {
*/ */
CpuProfile* StopProfiling(Local<String> title); CpuProfile* StopProfiling(Local<String> title);
/**
* Force collection of a sample. Must be called on the VM thread.
* Recording the forced sample does not contribute to the aggregated
* profile statistics.
*/
void CollectSample();
/** /**
* Tells the profiler whether the embedder is idle. * Tells the profiler whether the embedder is idle.
*/ */
......
...@@ -8049,6 +8049,9 @@ void CpuProfiler::SetSamplingInterval(int us) { ...@@ -8049,6 +8049,9 @@ void CpuProfiler::SetSamplingInterval(int us) {
base::TimeDelta::FromMicroseconds(us)); base::TimeDelta::FromMicroseconds(us));
} }
void CpuProfiler::CollectSample() {
reinterpret_cast<i::CpuProfiler*>(this)->CollectSample();
}
void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) { void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) {
reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling( reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
......
...@@ -49,12 +49,12 @@ void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from, ...@@ -49,12 +49,12 @@ void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from,
regs.sp = fp - fp_to_sp_delta; regs.sp = fp - fp_to_sp_delta;
regs.fp = fp; regs.fp = fp;
regs.pc = from; regs.pc = from;
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame); record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false);
ticks_from_vm_buffer_.Enqueue(record); ticks_from_vm_buffer_.Enqueue(record);
} }
void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate,
void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) { bool update_stats) {
TickSampleEventRecord record(last_code_event_id_.Value()); TickSampleEventRecord record(last_code_event_id_.Value());
RegisterState regs; RegisterState regs;
StackFrameIterator it(isolate); StackFrameIterator it(isolate);
...@@ -64,7 +64,7 @@ void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) { ...@@ -64,7 +64,7 @@ void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
regs.fp = frame->fp(); regs.fp = frame->fp();
regs.pc = frame->pc(); regs.pc = frame->pc();
} }
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame); record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, update_stats);
ticks_from_vm_buffer_.Enqueue(record); ticks_from_vm_buffer_.Enqueue(record);
} }
...@@ -429,6 +429,11 @@ void CpuProfiler::ResetProfiles() { ...@@ -429,6 +429,11 @@ void CpuProfiler::ResetProfiles() {
profiles_ = new CpuProfilesCollection(isolate()->heap()); profiles_ = new CpuProfilesCollection(isolate()->heap());
} }
void CpuProfiler::CollectSample() {
if (processor_ != NULL) {
processor_->AddCurrentStack(isolate_);
}
}
void CpuProfiler::StartProfiling(const char* title, bool record_samples) { void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
if (profiles_->StartProfiling(title, record_samples)) { if (profiles_->StartProfiling(title, record_samples)) {
......
...@@ -139,7 +139,7 @@ class ProfilerEventsProcessor : public base::Thread { ...@@ -139,7 +139,7 @@ class ProfilerEventsProcessor : public base::Thread {
void Enqueue(const CodeEventsContainer& event); void Enqueue(const CodeEventsContainer& event);
// Puts current stack into tick sample events buffer. // Puts current stack into tick sample events buffer.
void AddCurrentStack(Isolate* isolate); void AddCurrentStack(Isolate* isolate, bool update_stats = false);
void AddDeoptStack(Isolate* isolate, Address from, int fp_to_sp_delta); void AddDeoptStack(Isolate* isolate, Address from, int fp_to_sp_delta);
// Tick sample events are filled directly in the buffer of the circular // Tick sample events are filled directly in the buffer of the circular
...@@ -168,8 +168,7 @@ class ProfilerEventsProcessor : public base::Thread { ...@@ -168,8 +168,7 @@ class ProfilerEventsProcessor : public base::Thread {
ProfileGenerator* generator_; ProfileGenerator* generator_;
Sampler* sampler_; Sampler* sampler_;
base::Atomic32 running_; base::Atomic32 running_;
// Sampling period in microseconds. const base::TimeDelta period_; // Samples & code events processing period.
const base::TimeDelta period_;
LockedQueue<CodeEventsContainer> events_buffer_; LockedQueue<CodeEventsContainer> events_buffer_;
static const size_t kTickSampleBufferSize = 1 * MB; static const size_t kTickSampleBufferSize = 1 * MB;
static const size_t kTickSampleQueueLength = static const size_t kTickSampleQueueLength =
...@@ -205,6 +204,7 @@ class CpuProfiler : public CodeEventListener { ...@@ -205,6 +204,7 @@ class CpuProfiler : public CodeEventListener {
virtual ~CpuProfiler(); virtual ~CpuProfiler();
void set_sampling_interval(base::TimeDelta value); void set_sampling_interval(base::TimeDelta value);
void CollectSample();
void StartProfiling(const char* title, bool record_samples = false); void StartProfiling(const char* title, bool record_samples = false);
void StartProfiling(String* title, bool record_samples); void StartProfiling(String* title, bool record_samples);
CpuProfile* StopProfiling(const char* title); CpuProfile* StopProfiling(const char* title);
......
...@@ -274,9 +274,8 @@ unsigned ProfileTree::GetFunctionId(const ProfileNode* node) { ...@@ -274,9 +274,8 @@ unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value)); return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
} }
ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path, ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
int src_line) { int src_line, bool update_stats) {
ProfileNode* node = root_; ProfileNode* node = root_;
CodeEntry* last_entry = NULL; CodeEntry* last_entry = NULL;
for (CodeEntry** entry = path.start() + path.length() - 1; for (CodeEntry** entry = path.start() + path.length() - 1;
...@@ -290,9 +289,11 @@ ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path, ...@@ -290,9 +289,11 @@ ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
if (last_entry && last_entry->has_deopt_info()) { if (last_entry && last_entry->has_deopt_info()) {
node->CollectDeoptInfo(last_entry); node->CollectDeoptInfo(last_entry);
} }
node->IncrementSelfTicks(); if (update_stats) {
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) { node->IncrementSelfTicks();
node->IncrementLineTicks(src_line); if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
}
} }
return node; return node;
} }
...@@ -354,10 +355,11 @@ CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples) ...@@ -354,10 +355,11 @@ CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples)
start_time_(base::TimeTicks::HighResolutionNow()), start_time_(base::TimeTicks::HighResolutionNow()),
top_down_(isolate) {} top_down_(isolate) {}
void CpuProfile::AddPath(base::TimeTicks timestamp, void CpuProfile::AddPath(base::TimeTicks timestamp,
const Vector<CodeEntry*>& path, int src_line) { const Vector<CodeEntry*>& path, int src_line,
ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line); bool update_stats) {
ProfileNode* top_frame_node =
top_down_.AddPathFromEnd(path, src_line, update_stats);
if (record_samples_) { if (record_samples_) {
timestamps_.Add(timestamp); timestamps_.Add(timestamp);
samples_.Add(top_frame_node); samples_.Add(top_frame_node);
...@@ -522,15 +524,15 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) { ...@@ -522,15 +524,15 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
UNREACHABLE(); UNREACHABLE();
} }
void CpuProfilesCollection::AddPathToCurrentProfiles( void CpuProfilesCollection::AddPathToCurrentProfiles(
base::TimeTicks timestamp, const Vector<CodeEntry*>& path, int src_line) { base::TimeTicks timestamp, const Vector<CodeEntry*>& path, int src_line,
bool update_stats) {
// As starting / stopping profiles is rare relatively to this // As starting / stopping profiles is rare relatively to this
// method, we don't bother minimizing the duration of lock holding, // method, we don't bother minimizing the duration of lock holding,
// e.g. copying contents of the list to a local vector. // e.g. copying contents of the list to a local vector.
current_profiles_semaphore_.Wait(); current_profiles_semaphore_.Wait();
for (int i = 0; i < current_profiles_.length(); ++i) { for (int i = 0; i < current_profiles_.length(); ++i) {
current_profiles_[i]->AddPath(timestamp, path, src_line); current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats);
} }
current_profiles_semaphore_.Signal(); current_profiles_semaphore_.Signal();
} }
...@@ -670,7 +672,8 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { ...@@ -670,7 +672,8 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
} }
} }
profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line); profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line,
sample.update_stats);
} }
......
...@@ -192,7 +192,8 @@ class ProfileTree { ...@@ -192,7 +192,8 @@ class ProfileTree {
ProfileNode* AddPathFromEnd( ProfileNode* AddPathFromEnd(
const Vector<CodeEntry*>& path, const Vector<CodeEntry*>& path,
int src_line = v8::CpuProfileNode::kNoLineNumberInfo); int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
bool update_stats = true);
ProfileNode* root() const { return root_; } ProfileNode* root() const { return root_; }
unsigned next_node_id() { return next_node_id_++; } unsigned next_node_id() { return next_node_id_++; }
unsigned GetFunctionId(const ProfileNode* node); unsigned GetFunctionId(const ProfileNode* node);
...@@ -225,7 +226,7 @@ class CpuProfile { ...@@ -225,7 +226,7 @@ class CpuProfile {
// Add pc -> ... -> main() call path to the profile. // Add pc -> ... -> main() call path to the profile.
void AddPath(base::TimeTicks timestamp, const Vector<CodeEntry*>& path, void AddPath(base::TimeTicks timestamp, const Vector<CodeEntry*>& path,
int src_line); int src_line, bool update_stats);
void CalculateTotalTicksAndSamplingRate(); void CalculateTotalTicksAndSamplingRate();
const char* title() const { return title_; } const char* title() const { return title_; }
...@@ -333,7 +334,8 @@ class CpuProfilesCollection { ...@@ -333,7 +334,8 @@ class CpuProfilesCollection {
// Called from profile generator thread. // Called from profile generator thread.
void AddPathToCurrentProfiles(base::TimeTicks timestamp, void AddPathToCurrentProfiles(base::TimeTicks timestamp,
const Vector<CodeEntry*>& path, int src_line); const Vector<CodeEntry*>& path, int src_line,
bool update_stats);
// Limits the number of profiles that can be simultaneously collected. // Limits the number of profiles that can be simultaneously collected.
static const int kMaxSimultaneousProfiles = 100; static const int kMaxSimultaneousProfiles = 100;
......
...@@ -657,10 +657,12 @@ SamplerThread* SamplerThread::instance_ = NULL; ...@@ -657,10 +657,12 @@ SamplerThread* SamplerThread::instance_ = NULL;
// //
DISABLE_ASAN void TickSample::Init(Isolate* isolate, DISABLE_ASAN void TickSample::Init(Isolate* isolate,
const v8::RegisterState& regs, const v8::RegisterState& regs,
RecordCEntryFrame record_c_entry_frame) { RecordCEntryFrame record_c_entry_frame,
bool update_stats) {
timestamp = base::TimeTicks::HighResolutionNow(); timestamp = base::TimeTicks::HighResolutionNow();
pc = reinterpret_cast<Address>(regs.pc); pc = reinterpret_cast<Address>(regs.pc);
state = isolate->current_vm_state(); state = isolate->current_vm_state();
this->update_stats = update_stats;
// Avoid collecting traces while doing GC. // Avoid collecting traces while doing GC.
if (state == GC) return; if (state == GC) return;
...@@ -796,7 +798,7 @@ void Sampler::SampleStack(const v8::RegisterState& state) { ...@@ -796,7 +798,7 @@ void Sampler::SampleStack(const v8::RegisterState& state) {
TickSample* sample = isolate_->cpu_profiler()->StartTickSample(); TickSample* sample = isolate_->cpu_profiler()->StartTickSample();
TickSample sample_obj; TickSample sample_obj;
if (sample == NULL) sample = &sample_obj; if (sample == NULL) sample = &sample_obj;
sample->Init(isolate_, state, TickSample::kIncludeCEntryFrame); sample->Init(isolate_, state, TickSample::kIncludeCEntryFrame, true);
if (is_counting_samples_) { if (is_counting_samples_) {
if (sample->state == JS || sample->state == EXTERNAL) { if (sample->state == JS || sample->state == EXTERNAL) {
++js_and_external_sample_count_; ++js_and_external_sample_count_;
......
...@@ -37,9 +37,10 @@ struct TickSample { ...@@ -37,9 +37,10 @@ struct TickSample {
external_callback(NULL), external_callback(NULL),
frames_count(0), frames_count(0),
has_external_callback(false), has_external_callback(false),
update_stats(true),
top_frame_type(StackFrame::NONE) {} top_frame_type(StackFrame::NONE) {}
void Init(Isolate* isolate, const v8::RegisterState& state, void Init(Isolate* isolate, const v8::RegisterState& state,
RecordCEntryFrame record_c_entry_frame); RecordCEntryFrame record_c_entry_frame, bool update_stats);
static void GetStackSample(Isolate* isolate, const v8::RegisterState& state, static void GetStackSample(Isolate* isolate, const v8::RegisterState& state,
RecordCEntryFrame record_c_entry_frame, RecordCEntryFrame record_c_entry_frame,
void** frames, size_t frames_limit, void** frames, size_t frames_limit,
...@@ -56,6 +57,7 @@ struct TickSample { ...@@ -56,6 +57,7 @@ struct TickSample {
base::TimeTicks timestamp; base::TimeTicks timestamp;
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames. unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
bool has_external_callback : 1; bool has_external_callback : 1;
bool update_stats : 1; // Whether the sample should update aggregated stats.
StackFrame::Type top_frame_type : 4; StackFrame::Type top_frame_type : 4;
}; };
......
...@@ -1603,11 +1603,52 @@ TEST(JsNative1JsNative2JsSample) { ...@@ -1603,11 +1603,52 @@ TEST(JsNative1JsNative2JsSample) {
profile->Delete(); profile->Delete();
} }
static const char* js_force_collect_sample_source =
"function start() {\n"
" CallCollectSample();\n"
"}";
static void CallCollectSample(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetIsolate()->GetCpuProfiler()->CollectSample();
}
TEST(CollectSampleAPI) {
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
v8::Context::Scope context_scope(env);
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(env->GetIsolate(), CallCollectSample);
v8::Local<v8::Function> func =
func_template->GetFunction(env).ToLocalChecked();
func->SetName(v8_str("CallCollectSample"));
env->Global()->Set(env, v8_str("CallCollectSample"), func).FromJust();
CompileRun(js_force_collect_sample_source);
v8::Local<v8::Function> function = GetFunction(env, "start");
v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
const v8::CpuProfileNode* root = profile->GetTopDownRoot();
{
ScopedVector<v8::Local<v8::String> > names(3);
names[0] = v8_str(ProfileGenerator::kGarbageCollectorEntryName);
names[1] = v8_str(ProfileGenerator::kProgramEntryName);
names[2] = v8_str("start");
CheckChildrenNames(env, root, names);
}
const v8::CpuProfileNode* startNode = GetChild(env, root, "start");
CHECK_LE(1, startNode->GetChildrenCount());
GetChild(env, startNode, "CallCollectSample");
profile->Delete();
}
// [Top down]: // [Top down]:
// 6 0 (root) #0 1 // 0 (root) #0 1
// 3 3 (program) #0 2 // 2 (program) #0 2
// 3 3 (idle) #0 3 // 3 (idle) #0 3
TEST(IdleTime) { TEST(IdleTime) {
LocalContext env; LocalContext env;
v8::HandleScope scope(env->GetIsolate()); v8::HandleScope scope(env->GetIsolate());
...@@ -1618,17 +1659,16 @@ TEST(IdleTime) { ...@@ -1618,17 +1659,16 @@ TEST(IdleTime) {
i::Isolate* isolate = CcTest::i_isolate(); i::Isolate* isolate = CcTest::i_isolate();
i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor(); i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
processor->AddCurrentStack(isolate); processor->AddCurrentStack(isolate, true);
cpu_profiler->SetIdle(true); cpu_profiler->SetIdle(true);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
processor->AddCurrentStack(isolate); processor->AddCurrentStack(isolate, true);
} }
cpu_profiler->SetIdle(false); cpu_profiler->SetIdle(false);
processor->AddCurrentStack(isolate); processor->AddCurrentStack(isolate, true);
v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name); v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
CHECK(profile); CHECK(profile);
...@@ -1645,7 +1685,7 @@ TEST(IdleTime) { ...@@ -1645,7 +1685,7 @@ TEST(IdleTime) {
const v8::CpuProfileNode* programNode = const v8::CpuProfileNode* programNode =
GetChild(env.local(), root, ProfileGenerator::kProgramEntryName); GetChild(env.local(), root, ProfileGenerator::kProgramEntryName);
CHECK_EQ(0, programNode->GetChildrenCount()); CHECK_EQ(0, programNode->GetChildrenCount());
CHECK_GE(programNode->GetHitCount(), 3u); CHECK_GE(programNode->GetHitCount(), 2u);
const v8::CpuProfileNode* idleNode = const v8::CpuProfileNode* idleNode =
GetChild(env.local(), root, ProfileGenerator::kIdleEntryName); GetChild(env.local(), root, ProfileGenerator::kIdleEntryName);
......
...@@ -108,7 +108,7 @@ void TraceExtension::DoTrace(Address fp) { ...@@ -108,7 +108,7 @@ void TraceExtension::DoTrace(Address fp) {
regs.sp = regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240; reinterpret_cast<Address>(trace_env.sample) - 10240;
trace_env.sample->Init(CcTest::i_isolate(), regs, trace_env.sample->Init(CcTest::i_isolate(), regs,
TickSample::kSkipCEntryFrame); TickSample::kSkipCEntryFrame, true);
} }
......
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