contexts.cc
conversions.cc
counters.cc
+ cpu-profiler.cc
data-flow.cc
dateparser.cc
debug-agent.cc
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
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.
};
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
}
-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),
}
-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
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);
};
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));
}
};
-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();
};
-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);
};
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);
} // 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));
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));
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]));
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]));
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);
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)));
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());
+}
'../../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',
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 */; };
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>"; };
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 */,
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;
};
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;
};
>
</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"
>
</File>
>
</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"
>
</File>
>
</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"
>
</File>