Add basic C++ implementation of CPU profiler.
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Mar 2010 09:46:53 +0000 (09:46 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Mar 2010 09:46:53 +0000 (09:46 +0000)
Review URL: http://codereview.chromium.org/1079006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4189 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/SConscript
src/cpu-profiler-inl.h [new file with mode: 0644]
src/cpu-profiler.cc [new file with mode: 0644]
src/cpu-profiler.h [new file with mode: 0644]
src/platform.h
src/profile-generator-inl.h
src/profile-generator.cc
src/profile-generator.h
test/cctest/test-profile-generator.cc
tools/gyp/v8.gyp
tools/v8.xcodeproj/project.pbxproj
tools/visual_studio/v8_base.vcproj
tools/visual_studio/v8_base_arm.vcproj
tools/visual_studio/v8_base_x64.vcproj

index 3c0d61d..14b6047 100755 (executable)
@@ -51,6 +51,7 @@ SOURCES = {
     contexts.cc
     conversions.cc
     counters.cc
+    cpu-profiler.cc
     data-flow.cc
     dateparser.cc
     debug-agent.cc
diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h
new file mode 100644 (file)
index 0000000..26ab643
--- /dev/null
@@ -0,0 +1,50 @@
+// 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_
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
new file mode 100644 (file)
index 0000000..d36f511
--- /dev/null
@@ -0,0 +1,201 @@
+// 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
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
new file mode 100644 (file)
index 0000000..7c838fd
--- /dev/null
@@ -0,0 +1,180 @@
+// 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_
index 8f68766..f124cf1 100644 (file)
@@ -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.
 };
 
index 090ad8f..5e92be4 100644 (file)
@@ -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),
index 5c64d36..9d7c346 100644 (file)
@@ -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
index c3843bf..a16ea66 100644 (file)
@@ -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);
 };
index 2d673d4..fc4d603 100644 (file)
@@ -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());
+}
index e608640..1e4cd50 100644 (file)
         '../../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',
index 9ef2665..591ba4b 100644 (file)
                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;
                };
index be32bcf..1d6d605 100644 (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>
index 74a7359..9043d58 100644 (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>
index f573910..3043753 100644 (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>