diff --git a/include/v8-profiler.h b/include/v8-profiler.h index 90a3d4d41c..41fe8a1999 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -105,6 +105,9 @@ class V8EXPORT CpuProfileNode { /** Returns function entry UID. */ unsigned GetCallUid() const; + /** Returns id of the node. The id is unique within the tree */ + unsigned GetNodeId() const; + /** Returns child nodes count of the node. */ int GetChildrenCount() const; @@ -130,6 +133,18 @@ class V8EXPORT CpuProfile { /** Returns the root node of the top down call tree. */ const CpuProfileNode* GetTopDownRoot() const; + /** + * Returns number of samples recorded. The samples are not recorded unless + * |record_samples| parameter of CpuProfiler::StartProfiling is true. + */ + int GetSamplesCount() const; + + /** + * Returns profile node corresponding to the top frame the sample at + * the given index. + */ + const CpuProfileNode* GetSample(int index) const; + /** * Deletes the profile and removes it from CpuProfiler's list. * All pointers to nodes previously returned become invalid. @@ -179,8 +194,11 @@ class V8EXPORT CpuProfiler { * title are silently ignored. While collecting a profile, functions * from all security contexts are included in it. The token-based * filtering is only performed when querying for a profile. + * + * |record_samples| parameter controls whether individual samples should + * be recorded in addition to the aggregated tree. */ - static void StartProfiling(Handle title); + static void StartProfiling(Handle title, bool record_samples = false); /** * Stops collecting CPU profile with a given title and returns it. diff --git a/src/api.cc b/src/api.cc index 64c00220f3..26db0de73b 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6454,6 +6454,11 @@ unsigned CpuProfileNode::GetCallUid() const { } +unsigned CpuProfileNode::GetNodeId() const { + return reinterpret_cast(this)->id(); +} + + int CpuProfileNode::GetChildrenCount() const { i::Isolate* isolate = i::Isolate::Current(); IsDeadCheck(isolate, "v8::CpuProfileNode::GetChildrenCount"); @@ -6506,6 +6511,17 @@ const CpuProfileNode* CpuProfile::GetTopDownRoot() const { } +const CpuProfileNode* CpuProfile::GetSample(int index) const { + const i::CpuProfile* profile = reinterpret_cast(this); + return reinterpret_cast(profile->sample(index)); +} + + +int CpuProfile::GetSamplesCount() const { + return reinterpret_cast(this)->samples_count(); +} + + int CpuProfiler::GetProfilesCount() { i::Isolate* isolate = i::Isolate::Current(); IsDeadCheck(isolate, "v8::CpuProfiler::GetProfilesCount"); @@ -6535,10 +6551,10 @@ const CpuProfile* CpuProfiler::FindProfile(unsigned uid, } -void CpuProfiler::StartProfiling(Handle title) { +void CpuProfiler::StartProfiling(Handle title, bool record_samples) { i::Isolate* isolate = i::Isolate::Current(); IsDeadCheck(isolate, "v8::CpuProfiler::StartProfiling"); - i::CpuProfiler::StartProfiling(*Utils::OpenHandle(*title)); + i::CpuProfiler::StartProfiling(*Utils::OpenHandle(*title), record_samples); } diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc index 15edc1ebdc..043f58cbd8 100644 --- a/src/cpu-profiler.cc +++ b/src/cpu-profiler.cc @@ -260,13 +260,14 @@ void ProfilerEventsProcessor::Run() { void CpuProfiler::StartProfiling(const char* title) { ASSERT(Isolate::Current()->cpu_profiler() != NULL); - Isolate::Current()->cpu_profiler()->StartCollectingProfile(title); + Isolate::Current()->cpu_profiler()->StartCollectingProfile(title, false); } -void CpuProfiler::StartProfiling(String* title) { +void CpuProfiler::StartProfiling(String* title, bool record_samples) { ASSERT(Isolate::Current()->cpu_profiler() != NULL); - Isolate::Current()->cpu_profiler()->StartCollectingProfile(title); + Isolate::Current()->cpu_profiler()->StartCollectingProfile( + title, record_samples); } @@ -468,16 +469,17 @@ void CpuProfiler::ResetProfiles() { profiles_ = new CpuProfilesCollection(); } -void CpuProfiler::StartCollectingProfile(const char* title) { - if (profiles_->StartProfiling(title, next_profile_uid_++)) { +void CpuProfiler::StartCollectingProfile(const char* title, + bool record_samples) { + if (profiles_->StartProfiling(title, next_profile_uid_++, record_samples)) { StartProcessorIfNotStarted(); } processor_->AddCurrentStack(); } -void CpuProfiler::StartCollectingProfile(String* title) { - StartCollectingProfile(profiles_->GetName(title)); +void CpuProfiler::StartCollectingProfile(String* title, bool record_samples) { + StartCollectingProfile(profiles_->GetName(title), record_samples); } diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h index 3dc766bc75..2ec4c70a8f 100644 --- a/src/cpu-profiler.h +++ b/src/cpu-profiler.h @@ -207,7 +207,7 @@ class CpuProfiler { static void TearDown(); static void StartProfiling(const char* title); - static void StartProfiling(String* title); + static void StartProfiling(String* title, bool record_samples); static CpuProfile* StopProfiling(const char* title); static CpuProfile* StopProfiling(Object* security_token, String* title); static int GetProfilesCount(); @@ -253,8 +253,8 @@ class CpuProfiler { private: CpuProfiler(); ~CpuProfiler(); - void StartCollectingProfile(const char* title); - void StartCollectingProfile(String* title); + void StartCollectingProfile(const char* title, bool record_samples); + void StartCollectingProfile(String* title, bool record_samples); void StartProcessorIfNotStarted(); CpuProfile* StopCollectingProfile(const char* title); CpuProfile* StopCollectingProfile(Object* security_token, String* title); diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h index 6e7f499fbc..4e6302c37e 100644 --- a/src/profile-generator-inl.h +++ b/src/profile-generator-inl.h @@ -74,7 +74,8 @@ ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry) entry_(entry), total_ticks_(0), self_ticks_(0), - children_(CodeEntriesMatch) { + children_(CodeEntriesMatch), + id_(tree->next_node_id()) { } diff --git a/src/profile-generator.cc b/src/profile-generator.cc index 2bf1724715..ce07213b02 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -296,6 +296,7 @@ ProfileTree::ProfileTree() "", 0, TokenEnumerator::kNoSecurityToken), + next_node_id_(1), root_(new ProfileNode(this, &root_entry_)) { } @@ -306,7 +307,7 @@ ProfileTree::~ProfileTree() { } -void ProfileTree::AddPathFromEnd(const Vector& path) { +ProfileNode* ProfileTree::AddPathFromEnd(const Vector& path) { ProfileNode* node = root_; for (CodeEntry** entry = path.start() + path.length() - 1; entry != path.start() - 1; @@ -316,6 +317,7 @@ void ProfileTree::AddPathFromEnd(const Vector& path) { } } node->IncrementSelfTicks(); + return node; } @@ -467,7 +469,8 @@ void ProfileTree::ShortPrint() { void CpuProfile::AddPath(const Vector& path) { - top_down_.AddPathFromEnd(path); + ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path); + if (record_samples_) samples_.Add(top_frame_node); } @@ -483,7 +486,7 @@ void CpuProfile::SetActualSamplingRate(double actual_sampling_rate) { CpuProfile* CpuProfile::FilteredClone(int security_token_id) { ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken); - CpuProfile* clone = new CpuProfile(title_, uid_); + CpuProfile* clone = new CpuProfile(title_, uid_, false); clone->top_down_.FilteredClone(&top_down_, security_token_id); return clone; } @@ -609,7 +612,8 @@ CpuProfilesCollection::~CpuProfilesCollection() { } -bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) { +bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid, + bool record_samples) { ASSERT(uid > 0); current_profiles_semaphore_->Wait(); if (current_profiles_.length() >= kMaxSimultaneousProfiles) { @@ -623,17 +627,12 @@ bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) { return false; } } - current_profiles_.Add(new CpuProfile(title, uid)); + current_profiles_.Add(new CpuProfile(title, uid, record_samples)); current_profiles_semaphore_->Signal(); return true; } -bool CpuProfilesCollection::StartProfiling(String* title, unsigned uid) { - return StartProfiling(GetName(title), uid); -} - - CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id, const char* title, double actual_sampling_rate) { diff --git a/src/profile-generator.h b/src/profile-generator.h index b128dad4a8..4ddb75337e 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -150,6 +150,7 @@ class ProfileNode { INLINE(const List* children() const) { return &children_list_; } double GetSelfMillis() const; double GetTotalMillis() const; + unsigned id() const { return id_; } void Print(int indent); @@ -170,6 +171,7 @@ class ProfileNode { // Mapping from CodeEntry* to ProfileNode* HashMap children_; List children_list_; + unsigned id_; DISALLOW_COPY_AND_ASSIGN(ProfileNode); }; @@ -180,7 +182,7 @@ class ProfileTree { ProfileTree(); ~ProfileTree(); - void AddPathFromEnd(const Vector& path); + ProfileNode* AddPathFromEnd(const Vector& path); void AddPathFromStart(const Vector& path); void CalculateTotalTicks(); void FilteredClone(ProfileTree* src, int security_token_id); @@ -191,6 +193,8 @@ class ProfileTree { ProfileNode* root() const { return root_; } void SetTickRatePerMs(double ticks_per_ms); + unsigned next_node_id() { return next_node_id_++; } + void ShortPrint(); void Print() { root_->Print(0); @@ -201,6 +205,7 @@ class ProfileTree { void TraverseDepthFirst(Callback* callback); CodeEntry root_entry_; + unsigned next_node_id_; ProfileNode* root_; double ms_to_ticks_scale_; @@ -210,8 +215,8 @@ class ProfileTree { class CpuProfile { public: - CpuProfile(const char* title, unsigned uid) - : title_(title), uid_(uid) { } + CpuProfile(const char* title, unsigned uid, bool record_samples) + : title_(title), uid_(uid), record_samples_(record_samples) { } // Add pc -> ... -> main() call path to the profile. void AddPath(const Vector& path); @@ -223,6 +228,9 @@ class CpuProfile { INLINE(unsigned uid() const) { return uid_; } INLINE(const ProfileTree* top_down() const) { return &top_down_; } + INLINE(int samples_count() const) { return samples_.length(); } + INLINE(ProfileNode* sample(int index) const) { return samples_.at(index); } + void UpdateTicksScale(); void ShortPrint(); @@ -231,6 +239,8 @@ class CpuProfile { private: const char* title_; unsigned uid_; + bool record_samples_; + List samples_; ProfileTree top_down_; DISALLOW_COPY_AND_ASSIGN(CpuProfile); @@ -288,8 +298,7 @@ class CpuProfilesCollection { CpuProfilesCollection(); ~CpuProfilesCollection(); - bool StartProfiling(const char* title, unsigned uid); - bool StartProfiling(String* title, unsigned uid); + bool StartProfiling(const char* title, unsigned uid, bool record_samples); CpuProfile* StopProfiling(int security_token_id, const char* title, double actual_sampling_rate); diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc index 75e594ee30..d44f349011 100644 --- a/test/cctest/test-cpu-profiler.cc +++ b/test/cctest/test-cpu-profiler.cc @@ -107,7 +107,7 @@ TEST(CodeEvents) { i::Factory* factory = isolate->factory(); TestSetup test_setup; CpuProfilesCollection profiles; - profiles.StartProfiling("", 1); + profiles.StartProfiling("", 1, false); ProfileGenerator generator(&profiles); ProfilerEventsProcessor processor(&generator); processor.Start(); @@ -168,7 +168,7 @@ static int CompareProfileNodes(const T* p1, const T* p2) { TEST(TickEvents) { TestSetup test_setup; CpuProfilesCollection profiles; - profiles.StartProfiling("", 1); + profiles.StartProfiling("", 1, false); ProfileGenerator generator(&profiles); ProfilerEventsProcessor processor(&generator); processor.Start(); @@ -233,7 +233,7 @@ TEST(CrashIfStoppingLastNonExistentProfile) { TEST(Issue1398) { TestSetup test_setup; CpuProfilesCollection profiles; - profiles.StartProfiling("", 1); + profiles.StartProfiling("", 1, false); ProfileGenerator generator(&profiles); ProfilerEventsProcessor processor(&generator); processor.Start(); diff --git a/test/cctest/test-profile-generator.cc b/test/cctest/test-profile-generator.cc index 22b4e059a2..884ca834fb 100644 --- a/test/cctest/test-profile-generator.cc +++ b/test/cctest/test-profile-generator.cc @@ -85,7 +85,8 @@ TEST(TokenEnumerator) { TEST(ProfileNodeFindOrAddChild) { - ProfileNode node(NULL, NULL); + ProfileTree tree; + ProfileNode node(&tree, NULL); CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0, TokenEnumerator::kNoSecurityToken); ProfileNode* childNode1 = node.FindOrAddChild(&entry1); @@ -113,7 +114,8 @@ TEST(ProfileNodeFindOrAddChild) { TEST(ProfileNodeFindOrAddChildForSameFunction) { const char* empty = ""; const char* aaa = "aaa"; - ProfileNode node(NULL, NULL); + ProfileTree tree; + ProfileNode node(&tree, NULL); CodeEntry entry1(i::Logger::FUNCTION_TAG, empty, aaa, empty, 0, TokenEnumerator::kNoSecurityToken); ProfileNode* childNode1 = node.FindOrAddChild(&entry1); @@ -607,7 +609,7 @@ class TestSetup { TEST(RecordTickSample) { TestSetup test_setup; CpuProfilesCollection profiles; - profiles.StartProfiling("", 1); + profiles.StartProfiling("", 1, false); ProfileGenerator generator(&profiles); CodeEntry* entry1 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); CodeEntry* entry2 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); @@ -713,6 +715,88 @@ TEST(SampleRateCalculator) { } +static void CheckNodeIds(ProfileNode* node, int* expectedId) { + CHECK_EQ((*expectedId)++, node->id()); + for (int i = 0; i < node->children()->length(); i++) { + CheckNodeIds(node->children()->at(i), expectedId); + } +} + +TEST(SampleIds) { + TestSetup test_setup; + CpuProfilesCollection profiles; + profiles.StartProfiling("", 1, true); + ProfileGenerator generator(&profiles); + CodeEntry* entry1 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); + CodeEntry* entry2 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); + CodeEntry* entry3 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc"); + generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); + generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100); + generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50); + + // We are building the following calls tree: + // -> aaa #3 - sample1 + // (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2 + // -> ccc #6 -> aaa #7 - sample3 + TickSample sample1; + sample1.pc = ToAddress(0x1600); + sample1.stack[0] = ToAddress(0x1510); + sample1.frames_count = 1; + generator.RecordTickSample(sample1); + TickSample sample2; + sample2.pc = ToAddress(0x1925); + sample2.stack[0] = ToAddress(0x1780); + sample2.stack[1] = ToAddress(0x10000); // non-existent. + sample2.stack[2] = ToAddress(0x1620); + sample2.frames_count = 3; + generator.RecordTickSample(sample2); + TickSample sample3; + sample3.pc = ToAddress(0x1510); + sample3.stack[0] = ToAddress(0x1910); + sample3.stack[1] = ToAddress(0x1610); + sample3.frames_count = 2; + generator.RecordTickSample(sample3); + + CpuProfile* profile = + profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1); + int nodeId = 1; + CheckNodeIds(profile->top_down()->root(), &nodeId); + CHECK_EQ(7, nodeId - 1); + + CHECK_EQ(3, profile->samples_count()); + int expected_id[] = {3, 5, 7}; + for (int i = 0; i < 3; i++) { + CHECK_EQ(expected_id[i], profile->sample(i)->id()); + } +} + + +TEST(NoSamples) { + TestSetup test_setup; + CpuProfilesCollection profiles; + profiles.StartProfiling("", 1, false); + ProfileGenerator generator(&profiles); + CodeEntry* entry1 = generator.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); + generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); + + // We are building the following calls tree: + // (root)#1 -> aaa #2 -> aaa #3 - sample1 + TickSample sample1; + sample1.pc = ToAddress(0x1600); + sample1.stack[0] = ToAddress(0x1510); + sample1.frames_count = 1; + generator.RecordTickSample(sample1); + + CpuProfile* profile = + profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1); + int nodeId = 1; + CheckNodeIds(profile->top_down()->root(), &nodeId); + CHECK_EQ(3, nodeId - 1); + + CHECK_EQ(0, profile->samples_count()); +} + + // --- P r o f i l e r E x t e n s i o n --- class ProfilerExtension : public v8::Extension { @@ -838,11 +922,12 @@ TEST(Issue51919) { for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) { i::Vector title = i::Vector::New(16); i::OS::SNPrintF(title, "%d", i); - CHECK(collection.StartProfiling(title.start(), i + 1)); // UID must be > 0. + // UID must be > 0. + CHECK(collection.StartProfiling(title.start(), i + 1, false)); titles[i] = title.start(); } CHECK(!collection.StartProfiling( - "maximum", CpuProfilesCollection::kMaxSimultaneousProfiles + 1)); + "maximum", CpuProfilesCollection::kMaxSimultaneousProfiles + 1, false)); for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) i::DeleteArray(titles[i]); }