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 {
*/
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.
*/
......
......@@ -8049,6 +8049,9 @@ void CpuProfiler::SetSamplingInterval(int us) {
base::TimeDelta::FromMicroseconds(us));
}
void CpuProfiler::CollectSample() {
reinterpret_cast<i::CpuProfiler*>(this)->CollectSample();
}
void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) {
reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
......
......@@ -49,12 +49,12 @@ void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from,
regs.sp = fp - fp_to_sp_delta;
regs.fp = fp;
regs.pc = from;
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame);
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false);
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());
RegisterState regs;
StackFrameIterator it(isolate);
......@@ -64,7 +64,7 @@ void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
regs.fp = frame->fp();
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);
}
......@@ -429,6 +429,11 @@ void CpuProfiler::ResetProfiles() {
profiles_ = new CpuProfilesCollection(isolate()->heap());
}
void CpuProfiler::CollectSample() {
if (processor_ != NULL) {
processor_->AddCurrentStack(isolate_);
}
}
void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
if (profiles_->StartProfiling(title, record_samples)) {
......
......@@ -139,7 +139,7 @@ class ProfilerEventsProcessor : public base::Thread {
void Enqueue(const CodeEventsContainer& event);
// 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);
// Tick sample events are filled directly in the buffer of the circular
......@@ -168,8 +168,7 @@ class ProfilerEventsProcessor : public base::Thread {
ProfileGenerator* generator_;
Sampler* sampler_;
base::Atomic32 running_;
// Sampling period in microseconds.
const base::TimeDelta period_;
const base::TimeDelta period_; // Samples & code events processing period.
LockedQueue<CodeEventsContainer> events_buffer_;
static const size_t kTickSampleBufferSize = 1 * MB;
static const size_t kTickSampleQueueLength =
......@@ -205,6 +204,7 @@ class CpuProfiler : public CodeEventListener {
virtual ~CpuProfiler();
void set_sampling_interval(base::TimeDelta value);
void CollectSample();
void StartProfiling(const char* title, bool record_samples = false);
void StartProfiling(String* title, bool record_samples);
CpuProfile* StopProfiling(const char* title);
......
......@@ -274,9 +274,8 @@ unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
}
ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
int src_line) {
int src_line, bool update_stats) {
ProfileNode* node = root_;
CodeEntry* last_entry = NULL;
for (CodeEntry** entry = path.start() + path.length() - 1;
......@@ -290,9 +289,11 @@ ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
if (last_entry && last_entry->has_deopt_info()) {
node->CollectDeoptInfo(last_entry);
}
node->IncrementSelfTicks();
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
if (update_stats) {
node->IncrementSelfTicks();
if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
node->IncrementLineTicks(src_line);
}
}
return node;
}
......@@ -354,10 +355,11 @@ CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples)
start_time_(base::TimeTicks::HighResolutionNow()),
top_down_(isolate) {}
void CpuProfile::AddPath(base::TimeTicks timestamp,
const Vector<CodeEntry*>& path, int src_line) {
ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line);
const Vector<CodeEntry*>& path, int src_line,
bool update_stats) {
ProfileNode* top_frame_node =
top_down_.AddPathFromEnd(path, src_line, update_stats);
if (record_samples_) {
timestamps_.Add(timestamp);
samples_.Add(top_frame_node);
......@@ -522,15 +524,15 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
UNREACHABLE();
}
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
// method, we don't bother minimizing the duration of lock holding,
// e.g. copying contents of the list to a local vector.
current_profiles_semaphore_.Wait();
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();
}
......@@ -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 {
ProfileNode* AddPathFromEnd(
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_; }
unsigned next_node_id() { return next_node_id_++; }
unsigned GetFunctionId(const ProfileNode* node);
......@@ -225,7 +226,7 @@ class CpuProfile {
// Add pc -> ... -> main() call path to the profile.
void AddPath(base::TimeTicks timestamp, const Vector<CodeEntry*>& path,
int src_line);
int src_line, bool update_stats);
void CalculateTotalTicksAndSamplingRate();
const char* title() const { return title_; }
......@@ -333,7 +334,8 @@ class CpuProfilesCollection {
// Called from profile generator thread.
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.
static const int kMaxSimultaneousProfiles = 100;
......
......@@ -657,10 +657,12 @@ SamplerThread* SamplerThread::instance_ = NULL;
//
DISABLE_ASAN void TickSample::Init(Isolate* isolate,
const v8::RegisterState& regs,
RecordCEntryFrame record_c_entry_frame) {
RecordCEntryFrame record_c_entry_frame,
bool update_stats) {
timestamp = base::TimeTicks::HighResolutionNow();
pc = reinterpret_cast<Address>(regs.pc);
state = isolate->current_vm_state();
this->update_stats = update_stats;
// Avoid collecting traces while doing GC.
if (state == GC) return;
......@@ -796,7 +798,7 @@ void Sampler::SampleStack(const v8::RegisterState& state) {
TickSample* sample = isolate_->cpu_profiler()->StartTickSample();
TickSample 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 (sample->state == JS || sample->state == EXTERNAL) {
++js_and_external_sample_count_;
......
......@@ -37,9 +37,10 @@ struct TickSample {
external_callback(NULL),
frames_count(0),
has_external_callback(false),
update_stats(true),
top_frame_type(StackFrame::NONE) {}
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,
RecordCEntryFrame record_c_entry_frame,
void** frames, size_t frames_limit,
......@@ -56,6 +57,7 @@ struct TickSample {
base::TimeTicks timestamp;
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
bool has_external_callback : 1;
bool update_stats : 1; // Whether the sample should update aggregated stats.
StackFrame::Type top_frame_type : 4;
};
......
......@@ -1603,11 +1603,52 @@ TEST(JsNative1JsNative2JsSample) {
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]:
// 6 0 (root) #0 1
// 3 3 (program) #0 2
// 3 3 (idle) #0 3
// 0 (root) #0 1
// 2 (program) #0 2
// 3 (idle) #0 3
TEST(IdleTime) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
......@@ -1618,17 +1659,16 @@ TEST(IdleTime) {
i::Isolate* isolate = CcTest::i_isolate();
i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
processor->AddCurrentStack(isolate);
processor->AddCurrentStack(isolate, true);
cpu_profiler->SetIdle(true);
for (int i = 0; i < 3; i++) {
processor->AddCurrentStack(isolate);
processor->AddCurrentStack(isolate, true);
}
cpu_profiler->SetIdle(false);
processor->AddCurrentStack(isolate);
processor->AddCurrentStack(isolate, true);
v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
CHECK(profile);
......@@ -1645,7 +1685,7 @@ TEST(IdleTime) {
const v8::CpuProfileNode* programNode =
GetChild(env.local(), root, ProfileGenerator::kProgramEntryName);
CHECK_EQ(0, programNode->GetChildrenCount());
CHECK_GE(programNode->GetHitCount(), 3u);
CHECK_GE(programNode->GetHitCount(), 2u);
const v8::CpuProfileNode* idleNode =
GetChild(env.local(), root, ProfileGenerator::kIdleEntryName);
......
......@@ -108,7 +108,7 @@ void TraceExtension::DoTrace(Address fp) {
regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240;
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