Allow recording individual samples in addition to the aggregated CPU profiles
CPU profiler API is extended with methods that allow to retrieve individual samples from profile. Each sample is presented as a pointer to a node in the top-down profile tree. The samples will let us tie JS performance to time. BUG=None Review URL: https://codereview.chromium.org/12919002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13980 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
87ab8ab1ea
commit
a5be5da2e8
@ -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.
|
||||
|
20
src/api.cc
20
src/api.cc
@ -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]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user