From 707fdd4c6ad0d3a2c863a0a9a2fbdc07f57d4949 Mon Sep 17 00:00:00 2001 From: "yurys@chromium.org" Date: Wed, 7 Aug 2013 17:04:27 +0000 Subject: [PATCH] Support idle time in CPU profiler This change provides an API for the embedder to tell CPU profiler if it is idle or busy with some task. This way we can discriminate between idle time and some native code execution. BUG=268947 R=alph@chromium.org, yangguo@chromium.org Review URL: https://codereview.chromium.org/22412003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16109 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8-profiler.h | 5 ++++ src/api.cc | 13 +++++++++ src/cpu-profiler.cc | 2 +- src/cpu-profiler.h | 2 +- src/isolate.h | 6 ++-- src/profile-generator-inl.h | 2 ++ src/profile-generator.cc | 4 +++ src/profile-generator.h | 2 ++ src/sampler.cc | 3 +- src/v8globals.h | 3 +- test/cctest/test-cpu-profiler.cc | 53 ++++++++++++++++++++++++++++++++++++ test/cctest/test-log-stack-tracer.cc | 2 +- tools/tickprocessor.js | 3 +- 13 files changed, 90 insertions(+), 10 deletions(-) diff --git a/include/v8-profiler.h b/include/v8-profiler.h index 1d7b70d..f2128ce 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -192,6 +192,11 @@ class V8_EXPORT CpuProfiler { */ void DeleteAllCpuProfiles(); + /** + * Tells the profiler whether the embedder is idle. + */ + void SetIdle(bool is_idle); + private: CpuProfiler(); ~CpuProfiler(); diff --git a/src/api.cc b/src/api.cc index 43a7ea0..c80162a 100644 --- a/src/api.cc +++ b/src/api.cc @@ -7646,6 +7646,19 @@ void CpuProfiler::DeleteAllCpuProfiles() { } +void CpuProfiler::SetIdle(bool is_idle) { + i::Isolate* isolate = reinterpret_cast(this)->isolate(); + i::StateTag state = isolate->current_vm_state(); + ASSERT(state == i::EXTERNAL || state == i::IDLE); + if (isolate->js_entry_sp() != NULL) return; + if (is_idle) { + isolate->set_current_vm_state(i::IDLE); + } else if (state == i::IDLE) { + isolate->set_current_vm_state(i::EXTERNAL); + } +} + + static i::HeapGraphEdge* ToInternal(const HeapGraphEdge* edge) { return const_cast( reinterpret_cast(edge)); diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc index 0a83b85..747542f 100644 --- a/src/cpu-profiler.cc +++ b/src/cpu-profiler.cc @@ -106,7 +106,7 @@ bool ProfilerEventsProcessor::ProcessCodeEvent() { bool ProfilerEventsProcessor::ProcessTicks() { while (true) { - if (!ticks_from_vm_buffer_.IsEmpty() + while (!ticks_from_vm_buffer_.IsEmpty() && ticks_from_vm_buffer_.Peek()->order == last_processed_code_event_id_) { TickSampleEventRecord record; diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h index cbe3e3c..1dd405e 100644 --- a/src/cpu-profiler.h +++ b/src/cpu-profiler.h @@ -241,6 +241,7 @@ class CpuProfiler : public CodeEventListener { ProfileGenerator* generator() const { return generator_; } ProfilerEventsProcessor* processor() const { return processor_; } + Isolate* isolate() const { return isolate_; } private: void StartProcessorIfNotStarted(); @@ -258,7 +259,6 @@ class CpuProfiler : public CodeEventListener { bool need_to_stop_sampler_; bool is_profiling_; - private: DISALLOW_COPY_AND_ASSIGN(CpuProfiler); }; diff --git a/src/isolate.h b/src/isolate.h index 97291ce..401505a 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -661,9 +661,9 @@ class Isolate { } inline Address* handler_address() { return &thread_local_top_.handler_; } - // Bottom JS entry (see StackTracer::Trace in sampler.cc). - static Address js_entry_sp(ThreadLocalTop* thread) { - return thread->js_entry_sp_; + // Bottom JS entry. + Address js_entry_sp() { + return thread_local_top_.js_entry_sp_; } inline Address* js_entry_sp_address() { return &thread_local_top_.js_entry_sp_; diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h index d92085a..6791c88 100644 --- a/src/profile-generator-inl.h +++ b/src/profile-generator-inl.h @@ -92,6 +92,8 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { case OTHER: case EXTERNAL: return program_entry_; + case IDLE: + return idle_entry_; default: return NULL; } } diff --git a/src/profile-generator.cc b/src/profile-generator.cc index e772a54..018973b 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -614,6 +614,8 @@ const char* const ProfileGenerator::kAnonymousFunctionName = "(anonymous function)"; const char* const ProfileGenerator::kProgramEntryName = "(program)"; +const char* const ProfileGenerator::kIdleEntryName = + "(idle)"; const char* const ProfileGenerator::kGarbageCollectorEntryName = "(garbage collector)"; const char* const ProfileGenerator::kUnresolvedFunctionName = @@ -624,6 +626,8 @@ ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) : profiles_(profiles), program_entry_( profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)), + idle_entry_( + profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)), gc_entry_( profiles->NewCodeEntry(Logger::BUILTIN_TAG, kGarbageCollectorEntryName)), diff --git a/src/profile-generator.h b/src/profile-generator.h index 0cc397e..99aeb1f 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -342,6 +342,7 @@ class ProfileGenerator { static const char* const kAnonymousFunctionName; static const char* const kProgramEntryName; + static const char* const kIdleEntryName; static const char* const kGarbageCollectorEntryName; // Used to represent frames for which we have no reliable way to // detect function. @@ -353,6 +354,7 @@ class ProfileGenerator { CpuProfilesCollection* profiles_; CodeMap code_map_; CodeEntry* program_entry_; + CodeEntry* idle_entry_; CodeEntry* gc_entry_; CodeEntry* unresolved_entry_; diff --git a/src/sampler.cc b/src/sampler.cc index d72ed1a..5966565 100644 --- a/src/sampler.cc +++ b/src/sampler.cc @@ -615,8 +615,7 @@ DISABLE_ASAN void TickSample::Init(Isolate* isolate, // Avoid collecting traces while doing GC. if (state == GC) return; - const Address js_entry_sp = - Isolate::js_entry_sp(isolate->thread_local_top()); + Address js_entry_sp = isolate->js_entry_sp(); if (js_entry_sp == 0) { // Not executing JS now. return; diff --git a/src/v8globals.h b/src/v8globals.h index c3f1f01..6ec7547 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -363,7 +363,8 @@ enum StateTag { GC, COMPILER, OTHER, - EXTERNAL + EXTERNAL, + IDLE }; diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc index daf8db6..2c3f3ed 100644 --- a/test/cctest/test-cpu-profiler.cc +++ b/test/cctest/test-cpu-profiler.cc @@ -1324,3 +1324,56 @@ TEST(JsNative1JsNative2JsSample) { v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); cpu_profiler->DeleteAllCpuProfiles(); } + + +// [Top down]: +// 6 0 (root) #0 1 +// 3 3 (program) #0 2 +// 3 3 (idle) #0 3 +TEST(IdleTime) { + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); + + v8::Local profile_name = v8::String::New("my_profile"); + cpu_profiler->StartCpuProfiling(profile_name); + + i::Isolate* isolate = i::Isolate::Current(); + i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor(); + processor->AddCurrentStack(isolate); + + cpu_profiler->SetIdle(true); + + for (int i = 0; i < 3; i++) { + processor->AddCurrentStack(isolate); + } + + cpu_profiler->SetIdle(false); + processor->AddCurrentStack(isolate); + + + const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name); + CHECK_NE(NULL, profile); + // Dump collected profile to have a better diagnostic in case of failure. + reinterpret_cast( + const_cast(profile))->Print(); + + const v8::CpuProfileNode* root = profile->GetTopDownRoot(); + ScopedVector > names(3); + names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName); + names[1] = v8::String::New(ProfileGenerator::kProgramEntryName); + names[2] = v8::String::New(ProfileGenerator::kIdleEntryName); + CheckChildrenNames(root, names); + + const v8::CpuProfileNode* programNode = + GetChild(root, ProfileGenerator::kProgramEntryName); + CHECK_EQ(0, programNode->GetChildrenCount()); + CHECK_GE(programNode->GetSelfSamplesCount(), 3); + + const v8::CpuProfileNode* idleNode = + GetChild(root, ProfileGenerator::kIdleEntryName); + CHECK_EQ(0, idleNode->GetChildrenCount()); + CHECK_GE(idleNode->GetSelfSamplesCount(), 3); + + cpu_profiler->DeleteAllCpuProfiles(); +} diff --git a/test/cctest/test-log-stack-tracer.cc b/test/cctest/test-log-stack-tracer.cc index 7c3567c..09df19e 100644 --- a/test/cctest/test-log-stack-tracer.cc +++ b/test/cctest/test-log-stack-tracer.cc @@ -157,7 +157,7 @@ void TraceExtension::JSTrace(const v8::FunctionCallbackInfo& args) { static Address GetJsEntrySp() { CHECK_NE(NULL, i::Isolate::Current()->thread_local_top()); - return Isolate::js_entry_sp(i::Isolate::Current()->thread_local_top()); + return i::Isolate::Current()->js_entry_sp(); } diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js index 967bd3c..f1a11cc 100644 --- a/tools/tickprocessor.js +++ b/tools/tickprocessor.js @@ -249,7 +249,8 @@ TickProcessor.VmStates = { GC: 1, COMPILER: 2, OTHER: 3, - EXTERNAL: 4 + EXTERNAL: 4, + IDLE: 5 }; -- 2.7.4