/** 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;
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
* 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.
}
+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");
}
+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");
}
-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);
}
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);
}
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);
}
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();
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);
entry_(entry),
total_ticks_(0),
self_ticks_(0),
- children_(CodeEntriesMatch) {
+ children_(CodeEntriesMatch),
+ id_(tree->next_node_id()) {
}
"",
0,
TokenEnumerator::kNoSecurityToken),
+ next_node_id_(1),
root_(new ProfileNode(this, &root_entry_)) {
}
}
-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;
}
}
node->IncrementSelfTicks();
+ return node;
}
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);
}
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;
}
}
-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) {
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) {
INLINE(const List<ProfileNode*>* children() const) { return &children_list_; }
double GetSelfMillis() const;
double GetTotalMillis() const;
+ unsigned id() const { return id_; }
void Print(int indent);
// Mapping from CodeEntry* to ProfileNode*
HashMap children_;
List<ProfileNode*> children_list_;
+ unsigned id_;
DISALLOW_COPY_AND_ASSIGN(ProfileNode);
};
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);
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);
void TraverseDepthFirst(Callback* callback);
CodeEntry root_entry_;
+ unsigned next_node_id_;
ProfileNode* root_;
double ms_to_ticks_scale_;
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);
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();
private:
const char* title_;
unsigned uid_;
+ bool record_samples_;
+ List<ProfileNode*> samples_;
ProfileTree top_down_;
DISALLOW_COPY_AND_ASSIGN(CpuProfile);
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);
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();
TEST(TickEvents) {
TestSetup test_setup;
CpuProfilesCollection profiles;
- profiles.StartProfiling("", 1);
+ profiles.StartProfiling("", 1, false);
ProfileGenerator generator(&profiles);
ProfilerEventsProcessor processor(&generator);
processor.Start();
TEST(Issue1398) {
TestSetup test_setup;
CpuProfilesCollection profiles;
- profiles.StartProfiling("", 1);
+ profiles.StartProfiling("", 1, false);
ProfileGenerator generator(&profiles);
ProfilerEventsProcessor processor(&generator);
processor.Start();
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);
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);
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");
}
+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 {
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]);
}