Add basic C++ implementation of CPU profiler.

Review URL: http://codereview.chromium.org/1079006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4189 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 103db91e
......@@ -51,6 +51,7 @@ SOURCES = {
contexts.cc
conversions.cc
counters.cc
cpu-profiler.cc
data-flow.cc
dateparser.cc
debug-agent.cc
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_CPU_PROFILER_INL_H_
#define V8_CPU_PROFILER_INL_H_
#include "circular-queue-inl.h"
#include "profile-generator-inl.h"
#include "cpu-profiler.h"
namespace v8 {
namespace internal {
TickSample* ProfilerEventsProcessor::TickSampleEvent() {
TickSampleEventRecord* evt =
reinterpret_cast<TickSampleEventRecord*>(ticks_buffer_.Enqueue());
evt->order = enqueue_order_; // No increment!
return &evt->sample;
}
} } // namespace v8::internal
#endif // V8_CPU_PROFILER_INL_H_
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "cpu-profiler-inl.h"
namespace v8 {
namespace internal {
static const int kEventsBufferSize = 256*KB;
static const int kTickSamplesBufferChunkSize = 64*KB;
static const int kTickSamplesBufferChunksCount = 16;
ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
: generator_(generator),
running_(false),
events_buffer_(kEventsBufferSize),
ticks_buffer_(sizeof(TickSampleEventRecord),
kTickSamplesBufferChunkSize,
kTickSamplesBufferChunksCount),
enqueue_order_(0) { }
void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
String* name,
String* resource_name,
int line_number,
Address start,
unsigned size) {
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
rec->order = ++enqueue_order_;
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
rec->size = size;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name,
Address start,
unsigned size) {
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
rec->order = ++enqueue_order_;
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name);
rec->size = size;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
int args_count,
Address start,
unsigned size) {
CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->type = CodeEventRecord::CODE_CREATION;
rec->order = ++enqueue_order_;
rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, args_count);
rec->size = size;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::CodeMoveEvent(Address from, Address to) {
CodeEventsContainer evt_rec;
CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
rec->type = CodeEventRecord::CODE_MOVE;
rec->order = ++enqueue_order_;
rec->from = from;
rec->to = to;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::CodeDeleteEvent(Address from) {
CodeEventsContainer evt_rec;
CodeDeleteEventRecord* rec = &evt_rec.CodeDeleteEventRecord_;
rec->type = CodeEventRecord::CODE_DELETE;
rec->order = ++enqueue_order_;
rec->start = from;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::FunctionCreateEvent(Address alias,
Address start) {
CodeEventsContainer evt_rec;
CodeAliasEventRecord* rec = &evt_rec.CodeAliasEventRecord_;
rec->type = CodeEventRecord::CODE_ALIAS;
rec->order = ++enqueue_order_;
rec->alias = alias;
rec->start = start;
events_buffer_.Enqueue(evt_rec);
}
void ProfilerEventsProcessor::FunctionMoveEvent(Address from, Address to) {
CodeMoveEvent(from, to);
}
void ProfilerEventsProcessor::FunctionDeleteEvent(Address from) {
CodeDeleteEvent(from);
}
bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) {
if (!events_buffer_.IsEmpty()) {
CodeEventsContainer record;
events_buffer_.Dequeue(&record);
switch (record.generic.type) {
#define PROFILER_TYPE_CASE(type, clss) \
case CodeEventRecord::type: \
record.clss##_.UpdateCodeMap(generator_->code_map()); \
break;
CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
#undef PROFILER_TYPE_CASE
default: return true; // Skip record.
}
*dequeue_order = record.generic.order;
return true;
}
return false;
}
bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) {
while (true) {
const TickSampleEventRecord* rec =
reinterpret_cast<TickSampleEventRecord*>(ticks_buffer_.StartDequeue());
if (rec == NULL) return false;
if (rec->order == dequeue_order) {
generator_->RecordTickSample(rec->sample);
ticks_buffer_.FinishDequeue();
} else {
return true;
}
}
}
void ProfilerEventsProcessor::Run() {
ticks_buffer_.SetUpConsumer();
unsigned dequeue_order = 0;
running_ = true;
while (running_) {
// Process ticks until we have any.
if (ProcessTicks(dequeue_order)) {
// All ticks of the current dequeue_order are processed,
// proceed to the next code event.
ProcessCodeEvent(&dequeue_order);
}
YieldCPU();
}
// Process remaining tick events.
ticks_buffer_.FlushResidualRecords();
// Perform processing until we have tick events, skip remaining code events.
while (ProcessTicks(dequeue_order) && ProcessCodeEvent(&dequeue_order)) { }
ticks_buffer_.TearDownConsumer();
}
} } // namespace v8::internal
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_CPU_PROFILER_H_
#define V8_CPU_PROFILER_H_
#include "circular-queue.h"
#include "profile-generator.h"
namespace v8 {
namespace internal {
#define CODE_EVENTS_TYPE_LIST(V) \
V(CODE_CREATION, CodeCreateEventRecord) \
V(CODE_MOVE, CodeMoveEventRecord) \
V(CODE_DELETE, CodeDeleteEventRecord) \
V(CODE_ALIAS, CodeAliasEventRecord)
class CodeEventRecord {
public:
#define DECLARE_TYPE(type, ignore) type,
enum Type {
NONE = 0,
CODE_EVENTS_TYPE_LIST(DECLARE_TYPE)
NUMBER_OF_TYPES
};
#undef DECLARE_TYPE
Type type;
unsigned order;
};
class CodeCreateEventRecord : public CodeEventRecord {
public:
Address start;
CodeEntry* entry;
unsigned size;
INLINE(void UpdateCodeMap(CodeMap* code_map)) {
code_map->AddCode(start, entry, size);
}
};
class CodeMoveEventRecord : public CodeEventRecord {
public:
Address from;
Address to;
INLINE(void UpdateCodeMap(CodeMap* code_map)) {
code_map->MoveCode(from, to);
}
};
class CodeDeleteEventRecord : public CodeEventRecord {
public:
Address start;
INLINE(void UpdateCodeMap(CodeMap* code_map)) {
code_map->DeleteCode(start);
}
};
class CodeAliasEventRecord : public CodeEventRecord {
public:
Address alias;
Address start;
INLINE(void UpdateCodeMap(CodeMap* code_map)) {
code_map->AddAlias(alias, start);
}
};
class TickSampleEventRecord {
public:
// In memory, the first machine word of a TickSampleEventRecord will be the
// first entry of TickSample, that is -- a program counter field.
// TickSample is put first, because 'order' can become equal to
// SamplingCircularQueue::kClear, while program counter can't.
TickSample sample;
unsigned order;
private:
// Disable instantiation.
TickSampleEventRecord();
DISALLOW_COPY_AND_ASSIGN(TickSampleEventRecord);
};
// This class implements both the profile events processor thread and
// methods called by event producers: VM and stack sampler threads.
class ProfilerEventsProcessor : public Thread {
public:
explicit ProfilerEventsProcessor(ProfileGenerator* generator);
virtual ~ProfilerEventsProcessor() { }
// Thread control.
virtual void Run();
inline void Stop() { running_ = false; }
// Events adding methods. Called by VM threads.
void CodeCreateEvent(Logger::LogEventsAndTags tag,
String* name,
String* resource_name, int line_number,
Address start, unsigned size);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name,
Address start, unsigned size);
void CodeCreateEvent(Logger::LogEventsAndTags tag,
int args_count,
Address start, unsigned size);
void CodeMoveEvent(Address from, Address to);
void CodeDeleteEvent(Address from);
void FunctionCreateEvent(Address alias, Address start);
void FunctionMoveEvent(Address from, Address to);
void FunctionDeleteEvent(Address from);
// Tick sampler registration. Called by sampler thread or signal handler.
inline void SetUpSamplesProducer() { ticks_buffer_.SetUpProducer(); }
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
// stack frame entries are filled.) This method returns a pointer to the
// next record of the buffer.
INLINE(TickSample* TickSampleEvent());
inline void TearDownSamplesProducer() { ticks_buffer_.TearDownProducer(); }
private:
union CodeEventsContainer {
CodeEventRecord generic;
#define DECLARE_CLASS(ignore, type) type type##_;
CODE_EVENTS_TYPE_LIST(DECLARE_CLASS)
#undef DECLARE_TYPE
};
// Called from events processing thread (Run() method.)
bool ProcessCodeEvent(unsigned* dequeue_order);
bool ProcessTicks(unsigned dequeue_order);
ProfileGenerator* generator_;
bool running_;
CircularQueue<CodeEventsContainer> events_buffer_;
SamplingCircularQueue ticks_buffer_;
unsigned enqueue_order_;
};
} } // namespace v8::internal
#endif // V8_CPU_PROFILER_H_
......@@ -529,7 +529,7 @@ class TickSample {
Address function; // The last called JS function.
StateTag state; // The state of the VM.
static const int kMaxFramesCount = 100;
EmbeddedVector<Address, kMaxFramesCount> stack; // Call stack.
Address stack[kMaxFramesCount]; // Call stack.
int frames_count; // Number of captured frames.
};
......
......@@ -34,6 +34,17 @@ namespace v8 {
namespace internal {
CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
const char* name,
const char* resource_name,
int line_number)
: tag_(tag),
name_(name),
resource_name_(resource_name),
line_number_(line_number) {
}
bool CodeEntry::is_js_function() {
return tag_ == Logger::FUNCTION_TAG
|| tag_ == Logger::LAZY_COMPILE_TAG
......@@ -41,24 +52,6 @@ bool CodeEntry::is_js_function() {
}
StaticNameCodeEntry::StaticNameCodeEntry(Logger::LogEventsAndTags tag,
const char* name)
: CodeEntry(tag),
name_(name) {
}
ManagedNameCodeEntry::ManagedNameCodeEntry(Logger::LogEventsAndTags tag,
String* name,
const char* resource_name,
int line_number)
: CodeEntry(tag),
name_(name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach()),
resource_name_(resource_name),
line_number_(line_number) {
}
ProfileNode::ProfileNode(CodeEntry* entry)
: entry_(entry),
total_ticks_(0),
......
......@@ -233,63 +233,143 @@ CodeEntry* CodeMap::FindEntry(Address addr) {
}
ProfileGenerator::ProfileGenerator()
: resource_names_(StringsMatch) {
CpuProfilesCollection::CpuProfilesCollection()
: function_and_resource_names_(StringsMatch) {
}
static void CodeEntriesDeleter(CodeEntry** entry_ptr) {
static void DeleteArgsCountName(char** name_ptr) {
DeleteArray(*name_ptr);
}
static void DeleteCodeEntry(CodeEntry** entry_ptr) {
delete *entry_ptr;
}
static void DeleteCpuProfile(CpuProfile** profile_ptr) {
delete *profile_ptr;
}
ProfileGenerator::~ProfileGenerator() {
for (HashMap::Entry* p = resource_names_.Start();
CpuProfilesCollection::~CpuProfilesCollection() {
profiles_.Iterate(DeleteCpuProfile);
code_entries_.Iterate(DeleteCodeEntry);
args_count_names_.Iterate(DeleteArgsCountName);
for (HashMap::Entry* p = function_and_resource_names_.Start();
p != NULL;
p = resource_names_.Next(p)) {
p = function_and_resource_names_.Next(p)) {
DeleteArray(reinterpret_cast<const char*>(p->value));
}
}
code_entries_.Iterate(CodeEntriesDeleter);
void CpuProfilesCollection::AddProfile(unsigned uid) {
profiles_.Add(new CpuProfile());
}
CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
String* name,
String* resource_name,
int line_number) {
CodeEntry* entry = new CodeEntry(tag,
GetName(name),
GetName(resource_name),
line_number);
code_entries_.Add(entry);
return entry;
}
CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
const char* name) {
CodeEntry* entry = new CodeEntry(tag, name, "", 0);
code_entries_.Add(entry);
return entry;
}
CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
int args_count) {
CodeEntry* entry = new CodeEntry(tag, GetName(args_count), "", 0);
code_entries_.Add(entry);
return entry;
}
CodeEntry* ProfileGenerator::NewCodeEntry(
Logger::LogEventsAndTags tag,
String* name,
String* resource_name, int line_number) {
const char* cached_resource_name = NULL;
if (resource_name->IsString()) {
// As we copy contents of resource names, and usually they are repeated,
// we cache names by string hashcode.
const char* CpuProfilesCollection::GetName(String* name) {
if (name->IsString()) {
char* c_name =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
HashMap::Entry* cache_entry =
resource_names_.Lookup(resource_name,
StringEntryHash(resource_name),
true);
function_and_resource_names_.Lookup(c_name,
name->Hash(),
true);
if (cache_entry->value == NULL) {
// New entry added.
cache_entry->value =
resource_name->ToCString(DISALLOW_NULLS,
ROBUST_STRING_TRAVERSAL).Detach();
cache_entry->value = c_name;
} else {
DeleteArray(c_name);
}
cached_resource_name = reinterpret_cast<const char*>(cache_entry->value);
return reinterpret_cast<const char*>(cache_entry->value);
} else {
return "";
}
}
CodeEntry* entry = new ManagedNameCodeEntry(tag,
name,
cached_resource_name,
line_number);
code_entries_.Add(entry);
return entry;
const char* CpuProfilesCollection::GetName(int args_count) {
ASSERT(args_count >= 0);
if (args_count_names_.length() <= args_count) {
args_count_names_.AddBlock(
NULL, args_count - args_count_names_.length() + 1);
}
if (args_count_names_[args_count] == NULL) {
const int kMaximumNameLength = 32;
char* name = NewArray<char>(kMaximumNameLength);
OS::SNPrintF(Vector<char>(name, kMaximumNameLength),
"args_count: %d", args_count);
args_count_names_[args_count] = name;
}
return args_count_names_[args_count];
}
CodeEntry* ProfileGenerator::NewCodeEntry(
Logger::LogEventsAndTags tag,
const char* name) {
CodeEntry* entry = new StaticNameCodeEntry(tag, name);
code_entries_.Add(entry);
return entry;
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
: profiles_(profiles) {
}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// Allocate space for stack frames + pc + function.
ScopedVector<CodeEntry*> entries(sample.frames_count + 2);
CodeEntry** entry = entries.start();
*entry++ = code_map_.FindEntry(sample.pc);
if (sample.function != NULL) {
*entry = code_map_.FindEntry(sample.function);
if (*entry != NULL && !(*entry)->is_js_function()) {
*entry = NULL;
} else {
CodeEntry* pc_entry = *entries.start();
if (pc_entry == NULL || pc_entry->is_js_function())
*entry = NULL;
}
entry++;
} else {
*entry++ = NULL;
}
for (const Address *stack_pos = sample.stack,
*stack_end = stack_pos + sample.frames_count;
stack_pos != stack_end;
++stack_pos) {
*entry++ = code_map_.FindEntry(*stack_pos);
}
profile()->AddPath(entries);
}
} } // namespace v8::internal
......@@ -36,50 +36,22 @@ namespace internal {
class CodeEntry {
public:
virtual ~CodeEntry() { }
// CodeEntry doesn't own name strings, just references them.
INLINE(CodeEntry(Logger::LogEventsAndTags tag_,
const char* name_,
const char* resource_name_,
int line_number_));
virtual const char* name() = 0;
INLINE(bool is_js_function());
protected:
INLINE(explicit CodeEntry(Logger::LogEventsAndTags tag))
: tag_(tag) { }
INLINE(const char* name()) { return name_; }
private:
Logger::LogEventsAndTags tag_;
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
};
class StaticNameCodeEntry : public CodeEntry {
public:
INLINE(StaticNameCodeEntry(Logger::LogEventsAndTags tag,
const char* name));
INLINE(virtual const char* name()) { return name_ != NULL ? name_ : ""; }
private:
const char* name_;
DISALLOW_COPY_AND_ASSIGN(StaticNameCodeEntry);
};
class ManagedNameCodeEntry : public CodeEntry {
public:
INLINE(ManagedNameCodeEntry(Logger::LogEventsAndTags tag,
String* name,
const char* resource_name, int line_number));
INLINE(virtual const char* name()) { return !name_.is_empty() ? *name_ : ""; }
private:
SmartPointer<char> name_;
const char* resource_name_;
int line_number_;
DISALLOW_COPY_AND_ASSIGN(ManagedNameCodeEntry);
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
};
......@@ -92,17 +64,18 @@ class ProfileNode {
INLINE(void IncrementSelfTicks()) { ++self_ticks_; }
INLINE(void IncreaseTotalTicks(unsigned amount)) { total_ticks_ += amount; }
INLINE(CodeEntry* entry()) { return entry_; }
INLINE(unsigned total_ticks()) { return total_ticks_; }
INLINE(unsigned self_ticks()) { return self_ticks_; }
void Print(int indent);
private:
INLINE(static bool CodeEntriesMatch(void* key1, void* key2)) {
return key1 == key2;
INLINE(static bool CodeEntriesMatch(void* entry1, void* entry2)) {
return entry1 == entry2;
}
INLINE(static bool CodeEntryHash(CodeEntry* entry)) {
INLINE(static uint32_t CodeEntryHash(CodeEntry* entry)) {
return static_cast<int32_t>(reinterpret_cast<intptr_t>(entry));
}
......@@ -144,13 +117,16 @@ class ProfileTree BASE_EMBEDDED {
};
class CpuProfile BASE_EMBEDDED {
class CpuProfile {
public:
CpuProfile() { }
// Add pc -> ... -> main() call path to the profile.
void AddPath(const Vector<CodeEntry*>& path);
void CalculateTotalTicks();
INLINE(ProfileTree* top_down()) { return &top_down_; }
INLINE(ProfileTree* bottom_up()) { return &bottom_up_; }
void ShortPrint();
void Print();
......@@ -196,33 +172,70 @@ class CodeMap BASE_EMBEDDED {
};
class ProfileGenerator {
class CpuProfilesCollection {
public:
ProfileGenerator();
~ProfileGenerator();
CpuProfilesCollection();
~CpuProfilesCollection();
void AddProfile(unsigned uid);
CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag,
String* name, String* resource_name, int line_number);
CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag, const char* name);
CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag, int args_count);
INLINE(CpuProfile* profile()) { return &profile_; }
INLINE(CodeMap* code_map()) { return &code_map_; }
INLINE(CpuProfile* profile()) { return profiles_.last(); }
private:
const char* GetName(String* name);
const char* GetName(int args_count);
INLINE(static bool StringsMatch(void* key1, void* key2)) {
return key1 == key2;
return strcmp(reinterpret_cast<char*>(key1),
reinterpret_cast<char*>(key2)) == 0;
}
INLINE(static bool StringEntryHash(String* entry)) {
return entry->Hash();
// String::Hash -> const char*
HashMap function_and_resource_names_;
// args_count -> char*
List<char*> args_count_names_;
List<CodeEntry*> code_entries_;
List<CpuProfile*> profiles_;
DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection);
};
class ProfileGenerator {
public:
explicit ProfileGenerator(CpuProfilesCollection* profiles);
INLINE(CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag,
String* name,
String* resource_name,
int line_number)) {
return profiles_->NewCodeEntry(tag, name, resource_name, line_number);
}
CpuProfile profile_;
INLINE(CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag,
const char* name)) {
return profiles_->NewCodeEntry(tag, name);
}
INLINE(CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag,
int args_count)) {
return profiles_->NewCodeEntry(tag, args_count);
}
void RecordTickSample(const TickSample& sample);
INLINE(CodeMap* code_map()) { return &code_map_; }
private:
INLINE(CpuProfile* profile()) { return profiles_->profile(); }
CpuProfilesCollection* profiles_;
CodeMap code_map_;
typedef List<CodeEntry*> CodeEntryList;
CodeEntryList code_entries_;
// String::Hash -> const char*
HashMap resource_names_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};
......
......@@ -10,25 +10,27 @@ namespace i = v8::internal;
using i::CodeEntry;
using i::CodeMap;
using i::CpuProfilesCollection;
using i::ProfileNode;
using i::ProfileTree;
using i::StaticNameCodeEntry;
using i::ProfileGenerator;
using i::TickSample;
using i::Vector;
TEST(ProfileNodeFindOrAddChild) {
ProfileNode node(NULL);
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
ProfileNode* childNode1 = node.FindOrAddChild(&entry1);
CHECK_NE(NULL, childNode1);
CHECK_EQ(childNode1, node.FindOrAddChild(&entry1));
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
ProfileNode* childNode2 = node.FindOrAddChild(&entry2);
CHECK_NE(NULL, childNode2);
CHECK_NE(childNode1, childNode2);
CHECK_EQ(childNode1, node.FindOrAddChild(&entry1));
CHECK_EQ(childNode2, node.FindOrAddChild(&entry2));
StaticNameCodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc", "", 0);
ProfileNode* childNode3 = node.FindOrAddChild(&entry3);
CHECK_NE(NULL, childNode3);
CHECK_NE(childNode1, childNode3);
......@@ -69,9 +71,9 @@ class ProfileTreeTestHelper {
} // namespace
TEST(ProfileTreeAddPathFromStart) {
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
StaticNameCodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc", "", 0);
ProfileTree tree;
ProfileTreeTestHelper helper(&tree);
CHECK_EQ(NULL, helper.Walk(&entry1));
......@@ -136,9 +138,9 @@ TEST(ProfileTreeAddPathFromStart) {
TEST(ProfileTreeAddPathFromEnd) {
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
StaticNameCodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc", "", 0);
ProfileTree tree;
ProfileTreeTestHelper helper(&tree);
CHECK_EQ(NULL, helper.Walk(&entry1));
......@@ -216,8 +218,8 @@ TEST(ProfileTreeCalculateTotalTicks) {
CHECK_EQ(1, empty_tree.root()->total_ticks());
CHECK_EQ(1, empty_tree.root()->self_ticks());
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
CodeEntry* e1_path[] = {&entry1};
Vector<CodeEntry*> e1_path_vec(
e1_path, sizeof(e1_path) / sizeof(e1_path[0]));
......@@ -255,7 +257,7 @@ TEST(ProfileTreeCalculateTotalTicks) {
CodeEntry* e2_path[] = {&entry2};
Vector<CodeEntry*> e2_path_vec(
e2_path, sizeof(e2_path) / sizeof(e2_path[0]));
StaticNameCodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc", "", 0);
CodeEntry* e3_path[] = {&entry3};
Vector<CodeEntry*> e3_path_vec(
e3_path, sizeof(e3_path) / sizeof(e3_path[0]));
......@@ -316,10 +318,10 @@ static inline i::Address ToAddress(int n) {
TEST(CodeMapAddCode) {
CodeMap code_map;
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
StaticNameCodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
StaticNameCodeEntry entry4(i::Logger::FUNCTION_TAG, "ddd");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc", "", 0);
CodeEntry entry4(i::Logger::FUNCTION_TAG, "ddd", "", 0);
code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
code_map.AddCode(ToAddress(0x1900), &entry3, 0x50);
......@@ -346,8 +348,8 @@ TEST(CodeMapAddCode) {
TEST(CodeMapMoveAndDeleteCode) {
CodeMap code_map;
StaticNameCodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
StaticNameCodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa", "", 0);
CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb", "", 0);
code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500)));
......@@ -360,3 +362,60 @@ TEST(CodeMapMoveAndDeleteCode) {
CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1700)));
CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1800)));
}
TEST(RecordTickSample) {
CpuProfilesCollection profiles;
profiles.AddProfile(0);
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);
ProfileTreeTestHelper top_down_test_helper(profiles.profile()->top_down());
CHECK_EQ(NULL, top_down_test_helper.Walk(entry1));
CHECK_EQ(NULL, top_down_test_helper.Walk(entry2));
CHECK_EQ(NULL, top_down_test_helper.Walk(entry3));
// We are building the following calls tree:
// -> aaa - sample1
// aaa -> bbb -> ccc - sample2
// -> ccc -> aaa - sample3
TickSample sample1;
sample1.pc = ToAddress(0x1600);
sample1.function = ToAddress(0x1500);
sample1.stack[0] = ToAddress(0x1510);
sample1.frames_count = 1;
generator.RecordTickSample(sample1);
TickSample sample2;
sample2.pc = ToAddress(0x1925);
sample2.function = ToAddress(0x1900);
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.function = ToAddress(0x1500);
sample3.stack[0] = ToAddress(0x1910);
sample3.stack[1] = ToAddress(0x1610);
sample3.frames_count = 2;
generator.RecordTickSample(sample3);
ProfileNode* node1 = top_down_test_helper.Walk(entry1);
CHECK_NE(NULL, node1);
CHECK_EQ(entry1, node1->entry());
ProfileNode* node2 = top_down_test_helper.Walk(entry1, entry1);
CHECK_NE(NULL, node2);
CHECK_EQ(entry1, node2->entry());
// ProfileNode* node3 = top_down_test_helper.Walk(entry1, entry2, entry3);
// CHECK_NE(NULL, node3);
// CHECK_EQ(entry2, node3->entry());
ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1);
CHECK_NE(NULL, node4);
CHECK_EQ(entry1, node4->entry());
}
......@@ -253,6 +253,9 @@
'../../src/counters.cc',
'../../src/counters.h',
'../../src/cpu.h',
'../../src/cpu-profiler-inl.h',
'../../src/cpu-profiler.cc',
'../../src/cpu-profiler.h',
'../../src/data-flow.cc',
'../../src/data-flow.h',
'../../src/dateparser.cc',
......
......@@ -212,6 +212,8 @@
9F11D9A1105AF0A300EBE5B2 /* heap-profiler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F11D99E105AF0A300EBE5B2 /* heap-profiler.cc */; };
9F2B3711114FF62D007CDAF4 /* circular-queue.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B370F114FF62D007CDAF4 /* circular-queue.cc */; };
9F2B3712114FF62D007CDAF4 /* circular-queue.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B370F114FF62D007CDAF4 /* circular-queue.cc */; };
9F2B37261152CEA0007CDAF4 /* cpu-profiler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B37241152CEA0007CDAF4 /* cpu-profiler.cc */; };
9F2B37271152CEA0007CDAF4 /* cpu-profiler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B37241152CEA0007CDAF4 /* cpu-profiler.cc */; };
9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
9F73E3B1114E61A100F84A5A /* profile-generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F73E3AF114E61A100F84A5A /* profile-generator.cc */; };
......@@ -557,6 +559,9 @@
9F2B370E114FF62D007CDAF4 /* circular-queue-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "circular-queue-inl.h"; sourceTree = "<group>"; };
9F2B370F114FF62D007CDAF4 /* circular-queue.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "circular-queue.cc"; sourceTree = "<group>"; };
9F2B3710114FF62D007CDAF4 /* circular-queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "circular-queue.h"; sourceTree = "<group>"; };
9F2B37231152CEA0007CDAF4 /* cpu-profiler-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "cpu-profiler-inl.h"; sourceTree = "<group>"; };
9F2B37241152CEA0007CDAF4 /* cpu-profiler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "cpu-profiler.cc"; sourceTree = "<group>"; };
9F2B37251152CEA0007CDAF4 /* cpu-profiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "cpu-profiler.h"; sourceTree = "<group>"; };
9F4B7B870FCC877A00DC4117 /* log-utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "log-utils.cc"; sourceTree = "<group>"; };
9F4B7B880FCC877A00DC4117 /* log-utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "log-utils.h"; sourceTree = "<group>"; };
9F73E3AE114E61A100F84A5A /* profile-generator-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "profile-generator-inl.h"; sourceTree = "<group>"; };
......@@ -717,6 +722,9 @@
897FF1230E719B8F00D62E90 /* cpu-arm.cc */,
897FF1240E719B8F00D62E90 /* cpu-ia32.cc */,
897FF1250E719B8F00D62E90 /* cpu.h */,
9F2B37231152CEA0007CDAF4 /* cpu-profiler-inl.h */,
9F2B37241152CEA0007CDAF4 /* cpu-profiler.cc */,
9F2B37251152CEA0007CDAF4 /* cpu-profiler.h */,
893A722A0F7B4A3200303DD2 /* dateparser-inl.h */,
897FF1260E719B8F00D62E90 /* dateparser.cc */,
897FF1270E719B8F00D62E90 /* dateparser.h */,
......@@ -1257,6 +1265,7 @@
9FBE03E210BD40EA00F8BFBA /* fast-codegen-ia32.cc in Sources */,
9F73E3B2114E61A100F84A5A /* profile-generator.cc in Sources */,
9F2B3712114FF62D007CDAF4 /* circular-queue.cc in Sources */,
9F2B37271152CEA0007CDAF4 /* cpu-profiler.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -1368,6 +1377,7 @@
9FBE03E510BD412600F8BFBA /* fast-codegen-arm.cc in Sources */,
9F73E3B1114E61A100F84A5A /* profile-generator.cc in Sources */,
9F2B3711114FF62D007CDAF4 /* circular-queue.cc in Sources */,
9F2B37261152CEA0007CDAF4 /* cpu-profiler.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -348,6 +348,18 @@
RelativePath="..\..\src\cpu.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.cc"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler-inl.h"
>
</File>
<File
RelativePath="..\..\src\data-flow.cc"
>
......
......@@ -356,6 +356,18 @@
RelativePath="..\..\src\cpu.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.cc"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler-inl.h"
>
</File>
<File
RelativePath="..\..\src\data-flow.cc"
>
......
......@@ -348,6 +348,18 @@
RelativePath="..\..\src\cpu.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.cc"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler.h"
>
</File>
<File
RelativePath="..\..\src\cpu-profiler-inl.h"
>
</File>
<File
RelativePath="..\..\src\data-flow.cc"
>
......
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