Allow recording individual samples in addition to the aggregated CPU profiles
authoryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Apr 2013 07:48:25 +0000 (07:48 +0000)
committeryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Apr 2013 07:48:25 +0000 (07:48 +0000)
Re-landing r13980 that was reverted in r14031

TBR=danno
BUG=None

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14104 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8-profiler.h
src/api.cc
src/cpu-profiler.cc
src/cpu-profiler.h
src/profile-generator-inl.h
src/profile-generator.cc
src/profile-generator.h
test/cctest/test-cpu-profiler.cc
test/cctest/test-profile-generator.cc

index 90a3d4d..41fe8a1 100644 (file)
@@ -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;
 
@@ -131,6 +134,18 @@ class V8EXPORT CpuProfile {
   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.
    * Profiles with the same uid but obtained using different
@@ -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<String> title);
+  static void StartProfiling(Handle<String> title, bool record_samples = false);
 
   /**
    * Stops collecting CPU profile with a given title and returns it.
index f1fe6ea..0c63627 100644 (file)
@@ -6454,6 +6454,11 @@ unsigned CpuProfileNode::GetCallUid() const {
 }
 
 
+unsigned CpuProfileNode::GetNodeId() const {
+  return reinterpret_cast<const i::ProfileNode*>(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<const i::CpuProfile*>(this);
+  return reinterpret_cast<const CpuProfileNode*>(profile->sample(index));
+}
+
+
+int CpuProfile::GetSamplesCount() const {
+  return reinterpret_cast<const i::CpuProfile*>(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<String> title) {
+void CpuProfiler::StartProfiling(Handle<String> 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);
 }
 
 
index 15edc1e..043f58c 100644 (file)
@@ -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);
 }
 
 
index 3dc766b..2ec4c70 100644 (file)
@@ -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);
index 6e7f499..4e6302c 100644 (file)
@@ -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()) {
 }
 
 
index 2bf1724..ce07213 100644 (file)
@@ -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<CodeEntry*>& path) {
+ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& 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<CodeEntry*>& path) {
     }
   }
   node->IncrementSelfTicks();
+  return node;
 }
 
 
@@ -467,7 +469,8 @@ void ProfileTree::ShortPrint() {
 
 
 void CpuProfile::AddPath(const Vector<CodeEntry*>& 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) {
index b128dad..4ddb753 100644 (file)
@@ -150,6 +150,7 @@ class ProfileNode {
   INLINE(const List<ProfileNode*>* 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<ProfileNode*> children_list_;
+  unsigned id_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileNode);
 };
@@ -180,7 +182,7 @@ class ProfileTree {
   ProfileTree();
   ~ProfileTree();
 
-  void AddPathFromEnd(const Vector<CodeEntry*>& path);
+  ProfileNode* AddPathFromEnd(const Vector<CodeEntry*>& path);
   void AddPathFromStart(const Vector<CodeEntry*>& 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<CodeEntry*>& 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<ProfileNode*> 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);
index 75e594e..d44f349 100644 (file)
@@ -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();
index 22b4e05..884ca83 100644 (file)
@@ -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<char> title = i::Vector<char>::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]);
 }