Commit c7532f0f authored by yurys@chromium.org's avatar yurys@chromium.org

Allow recording individual samples in addition to the aggregated CPU profiles

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
parent d15321f2
......@@ -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<String> title);
static void StartProfiling(Handle<String> title, bool record_samples = false);
/**
* Stops collecting CPU profile with a given title and returns it.
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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()) {
}
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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();
......
......@@ -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]);
}
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