Revision: 25182
Author:   [email protected]
Date:     Thu Nov  6 09:16:34 2014 UTC
Log: The idea behind of this solution is to use the existing "relocation info" instead of consumption the CodeLinePosition events emitted by the V8 compilers.
During generation code and relocation info are generated simultaneously.
When code generation is done you each code object has associated "relocation info". Relocation information lets V8 to mark interesting places in the generated code: the pointers that might need to be relocated (after garbage collection), correspondences between the machine program counter and source locations for stack walking.

This patch:
1. Add more source positions info in reloc info to make it suitable for source level mapping. The amount of data should not be increased dramatically because (1) V8 already marks interesting places in the generated code and (2) V8 does not write redundant information (it writes a pair (pc_offset, pos) only if pos is changed and skips other). I measured it on Octane benchmark - for unoptimized code the number of source positions may achieve 2x ('lin_solve' from NavierStokes benchmark).

2. When a sample happens, CPU profiler finds a code object by pc, then use its reloc info to match the sample to a source line. If a source line is found that hit counter is increased by one for this line.

3. Add a new public V8 API to get the hit source lines by CDT CPU profiler.
Note that it's expected a minor patch in Blink to pack the source level info in JSON to be shown.

4.Add a test that checks how the samples are distributed through source lines. It tests two cases: (1) relocation info created during code generation and (2) relocation info associated with precompiled function's version.

Patch from Denis Pravdin <[email protected]>;

[email protected], [email protected]

Review URL: https://codereview.chromium.org/682143003

Patch from Weiliang <[email protected]>.
https://code.google.com/p/v8/source/detail?r=25182

Modified:
 /branches/bleeding_edge/include/v8-profiler.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/cpu-profiler.cc
 /branches/bleeding_edge/src/profile-generator-inl.h
 /branches/bleeding_edge/src/profile-generator.cc
 /branches/bleeding_edge/src/profile-generator.h
 /branches/bleeding_edge/test/cctest/test-cpu-profiler.cc

=======================================
--- /branches/bleeding_edge/include/v8-profiler.h Thu Oct 2 11:58:21 2014 UTC +++ /branches/bleeding_edge/include/v8-profiler.h Thu Nov 6 09:16:34 2014 UTC
@@ -22,6 +22,14 @@
  */
 class V8_EXPORT CpuProfileNode {
  public:
+  struct LineTick {
+ /** The 1-based number of the source line where the function originates. */
+    int line;
+
+    /** The count of samples associated with the source line. */
+    unsigned int hit_count;
+  };
+
   /** Returns function name (empty string for anonymous functions.) */
   Handle<String> GetFunctionName() const;

@@ -43,6 +51,18 @@
    */
   int GetColumnNumber() const;

+  /**
+ * Returns the number of the function's source lines that collect the samples.
+   */
+  unsigned int GetHitLineCount() const;
+
+  /** Returns the set of source lines that collect the samples.
+   *  The caller allocates buffer and responsible for releasing it.
+   *  True if all available entries are copied, otherwise false.
+   *  The function copies nothing if buffer is not large enough.
+   */
+  bool GetLineTicks(LineTick* entries, unsigned int length) const;
+
   /** Returns bailout reason for the function
     * if the optimization was disabled for it.
     */
=======================================
--- /branches/bleeding_edge/src/api.cc  Tue Nov  4 10:02:25 2014 UTC
+++ /branches/bleeding_edge/src/api.cc  Thu Nov  6 09:16:34 2014 UTC
@@ -7170,6 +7170,19 @@
   return reinterpret_cast<const i::ProfileNode*>(this)->
       entry()->column_number();
 }
+
+
+unsigned int CpuProfileNode::GetHitLineCount() const {
+ const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
+  return node->GetHitLineCount();
+}
+
+
+bool CpuProfileNode::GetLineTicks(LineTick* entries,
+                                  unsigned int length) const {
+ const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
+  return node->GetLineTicks(entries, length);
+}


 const char* CpuProfileNode::GetBailoutReason() const {
=======================================
--- /branches/bleeding_edge/src/cpu-profiler.cc Fri Oct 17 15:44:02 2014 UTC
+++ /branches/bleeding_edge/src/cpu-profiler.cc Thu Nov  6 09:16:34 2014 UTC
@@ -199,7 +199,10 @@
   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
   rec->start = code->address();
- rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
+  rec->entry = profiles_->NewCodeEntry(
+      tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
+      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
+ CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
   rec->size = code->ExecutableSize();
   rec->shared = NULL;
   processor_->Enqueue(evt_rec);
@@ -213,7 +216,10 @@
   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
   rec->start = code->address();
- rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
+  rec->entry = profiles_->NewCodeEntry(
+      tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
+      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
+ CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
   rec->size = code->ExecutableSize();
   rec->shared = NULL;
   processor_->Enqueue(evt_rec);
@@ -229,7 +235,9 @@
   rec->start = code->address();
   rec->entry = profiles_->NewCodeEntry(
       tag, profiles_->GetFunctionName(shared->DebugName()),
-      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name));
+      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name),
+ CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
+      NULL, code->instruction_start());
   if (info) {
     rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
   }
@@ -254,15 +262,29 @@
   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
   rec->start = code->address();
+  Script* script = Script::cast(shared->script());
+  JITLineInfoTable* line_table = NULL;
+  if (script) {
+    line_table = new JITLineInfoTable();
+    for (RelocIterator it(code); !it.done(); it.next()) {
+      RelocInfo::Mode mode = it.rinfo()->rmode();
+      if (RelocInfo::IsPosition(mode)) {
+        int position = static_cast<int>(it.rinfo()->data());
+        if (position >= 0) {
+ int pc_offset = static_cast<int>(it.rinfo()->pc() - code->address());
+          int line_number = script->GetLineNumber(position) + 1;
+          line_table->SetPosition(pc_offset, line_number);
+        }
+      }
+    }
+  }
   rec->entry = profiles_->NewCodeEntry(
       tag, profiles_->GetFunctionName(shared->DebugName()),
       CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
-      column);
+      column, line_table, code->instruction_start());
   if (info) {
     rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
   }
-  DCHECK(Script::cast(shared->script()));
-  Script* script = Script::cast(shared->script());
   rec->entry->set_script_id(script->id()->value());
   rec->size = code->ExecutableSize();
   rec->shared = shared->address();
@@ -280,9 +302,9 @@
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
   rec->start = code->address();
   rec->entry = profiles_->NewCodeEntry(
-      tag,
-      profiles_->GetName(args_count),
-      "args_count: ");
+      tag, profiles_->GetName(args_count), "args_count: ",
+      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
+ CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
   rec->size = code->ExecutableSize();
   rec->shared = NULL;
   processor_->Enqueue(evt_rec);
@@ -342,9 +364,9 @@
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
   rec->start = code->address();
   rec->entry = profiles_->NewCodeEntry(
-      Logger::REG_EXP_TAG,
-      profiles_->GetName(source),
-      "RegExp: ");
+      Logger::REG_EXP_TAG, profiles_->GetName(source), "RegExp: ",
+      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
+ CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
   rec->size = code->ExecutableSize();
   processor_->Enqueue(evt_rec);
 }
=======================================
--- /branches/bleeding_edge/src/profile-generator-inl.h Wed Nov 5 12:40:56 2014 UTC +++ /branches/bleeding_edge/src/profile-generator-inl.h Thu Nov 6 09:16:34 2014 UTC
@@ -12,7 +12,8 @@

 CodeEntry::CodeEntry(Logger::LogEventsAndTags tag, const char* name,
                      const char* name_prefix, const char* resource_name,
-                     int line_number, int column_number)
+                     int line_number, int column_number,
+ JITLineInfoTable* line_info, Address instruction_start)
     : bit_field_(TagField::encode(tag) |
                  BuiltinIdField::encode(Builtins::builtin_count)),
       name_prefix_(name_prefix),
@@ -23,7 +24,9 @@
       shared_id_(0),
       script_id_(v8::UnboundScript::kNoScriptId),
       no_frame_ranges_(NULL),
-      bailout_reason_(kEmptyBailoutReason) {}
+      bailout_reason_(kEmptyBailoutReason),
+      line_info_(line_info),
+      instruction_start_(instruction_start) {}


 bool CodeEntry::is_js_function_tag(Logger::LogEventsAndTags tag) {
@@ -41,8 +44,8 @@
       entry_(entry),
       self_ticks_(0),
       children_(CodeEntriesMatch),
-      id_(tree->next_node_id()) { }
-
+      id_(tree->next_node_id()),
+      line_ticks_(LineTickMatch) {}
 } }  // namespace v8::internal

 #endif  // V8_PROFILE_GENERATOR_INL_H_
=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Wed Nov 5 12:40:56 2014 UTC +++ /branches/bleeding_edge/src/profile-generator.cc Thu Nov 6 09:16:34 2014 UTC
@@ -130,6 +130,31 @@
   uint32_t hash = StringHasher::HashSequentialString(str, len, hash_seed_);
   return names_.Lookup(const_cast<char*>(str), hash, true);
 }
+
+
+JITLineInfoTable::JITLineInfoTable() {}
+
+
+JITLineInfoTable::~JITLineInfoTable() {}
+
+
+void JITLineInfoTable::SetPosition(int pc_offset, int line) {
+  DCHECK(pc_offset >= 0);
+  DCHECK(line > 0);  // The 1-based number of the source line.
+  if (GetSourceLineNumber(pc_offset) != line) {
+    pc_offset_map_.insert(std::make_pair(pc_offset, line));
+  }
+}
+
+
+int JITLineInfoTable::GetSourceLineNumber(int pc_offset) const {
+  PcOffsetMap::const_iterator it = pc_offset_map_.lower_bound(pc_offset);
+  if (it == pc_offset_map_.end()) {
+ if (pc_offset_map_.empty()) return v8::CpuProfileNode::kNoLineNumberInfo;
+    return (--pc_offset_map_.end())->second;
+  }
+  return it->second;
+}


 const char* const CodeEntry::kEmptyNamePrefix = "";
@@ -139,6 +164,7 @@

 CodeEntry::~CodeEntry() {
   delete no_frame_ranges_;
+  delete line_info_;
 }


@@ -177,6 +203,14 @@
   bit_field_ = TagField::update(bit_field_, Logger::BUILTIN_TAG);
   bit_field_ = BuiltinIdField::update(bit_field_, id);
 }
+
+
+int CodeEntry::GetSourceLine(int pc_offset) const {
+  if (line_info_ && !line_info_->empty()) {
+    return line_info_->GetSourceLineNumber(pc_offset);
+  }
+  return v8::CpuProfileNode::kNoLineNumberInfo;
+}


 ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
@@ -198,6 +232,40 @@
   }
   return reinterpret_cast<ProfileNode*>(map_entry->value);
 }
+
+
+void ProfileNode::IncrementLineTicks(int src_line) {
+  if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return;
+  // Increment a hit counter of a certain source line.
+  // Add a new source line if not found.
+  HashMap::Entry* e =
+ line_ticks_.Lookup(reinterpret_cast<void*>(src_line), src_line, true);
+  DCHECK(e);
+ e->value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(e->value) + 1);
+}
+
+
+bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
+                               unsigned int length) const {
+  if (entries == NULL || length == 0) return false;
+
+  unsigned line_count = line_ticks_.occupancy();
+
+  if (line_count == 0) return true;
+  if (length < line_count) return false;
+
+  v8::CpuProfileNode::LineTick* entry = entries;
+
+  for (HashMap::Entry* p = line_ticks_.Start(); p != NULL;
+       p = line_ticks_.Next(p), entry++) {
+    entry->line =
+        static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->key));
+    entry->hit_count =
+        static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->value));
+  }
+
+  return true;
+}


 void ProfileNode::Print(int indent) {
@@ -240,7 +308,8 @@
 }


-ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
+ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path,
+                                         int src_line) {
   ProfileNode* node = root_;
   for (CodeEntry** entry = path.start() + path.length() - 1;
        entry != path.start() - 1;
@@ -250,11 +319,15 @@
     }
   }
   node->IncrementSelfTicks();
+  if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
+    node->IncrementLineTicks(src_line);
+  }
   return node;
 }


-void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
+void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path,
+                                   int src_line) {
   ProfileNode* node = root_;
   for (CodeEntry** entry = path.start();
        entry != path.start() + path.length();
@@ -264,6 +337,9 @@
     }
   }
   node->IncrementSelfTicks();
+  if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
+    node->IncrementLineTicks(src_line);
+  }
 }


@@ -325,8 +401,8 @@


 void CpuProfile::AddPath(base::TimeTicks timestamp,
-                         const Vector<CodeEntry*>& path) {
-  ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path);
+                         const Vector<CodeEntry*>& path, int src_line) {
+  ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line);
   if (record_samples_) {
     timestamps_.Add(timestamp);
     samples_.Add(top_frame_node);
@@ -515,31 +591,25 @@


 void CpuProfilesCollection::AddPathToCurrentProfiles(
-    base::TimeTicks timestamp, const Vector<CodeEntry*>& path) {
+ base::TimeTicks timestamp, const Vector<CodeEntry*>& path, int src_line) {
   // As starting / stopping profiles is rare relatively to this
   // method, we don't bother minimizing the duration of lock holding,
   // e.g. copying contents of the list to a local vector.
   current_profiles_semaphore_.Wait();
   for (int i = 0; i < current_profiles_.length(); ++i) {
-    current_profiles_[i]->AddPath(timestamp, path);
+    current_profiles_[i]->AddPath(timestamp, path, src_line);
   }
   current_profiles_semaphore_.Signal();
 }


 CodeEntry* CpuProfilesCollection::NewCodeEntry(
-      Logger::LogEventsAndTags tag,
-      const char* name,
-      const char* name_prefix,
-      const char* resource_name,
-      int line_number,
-      int column_number) {
-  CodeEntry* code_entry = new CodeEntry(tag,
-                                        name,
-                                        name_prefix,
-                                        resource_name,
-                                        line_number,
-                                        column_number);
+ Logger::LogEventsAndTags tag, const char* name, const char* name_prefix,
+    const char* resource_name, int line_number, int column_number,
+    JITLineInfoTable* line_info, Address instruction_start) {
+  CodeEntry* code_entry =
+      new CodeEntry(tag, name, name_prefix, resource_name, line_number,
+                    column_number, line_info, instruction_start);
   code_entries_.Add(code_entry);
   return code_entry;
 }
@@ -577,6 +647,15 @@
   // entries vector with NULL values.
   CodeEntry** entry = entries.start();
   memset(entry, 0, entries.length() * sizeof(*entry));
+
+  // The ProfileNode knows nothing about all versions of generated code for
+  // the same JS function. The line number information associated with
+ // the latest version of generated code is used to find a source line number
+  // for a JS function. Then, the detected source line is passed to
+  // ProfileNode to increase the tick count for this source line.
+  int src_line = v8::CpuProfileNode::kNoLineNumberInfo;
+  bool src_line_not_found = true;
+
   if (sample.pc != NULL) {
     if (sample.has_external_callback && sample.state == EXTERNAL &&
         sample.top_frame_type == StackFrame::EXIT) {
@@ -593,10 +672,9 @@
       // frame. Check for this case and just skip such samples.
       if (pc_entry) {
         List<OffsetRange>* ranges = pc_entry->no_frame_ranges();
+        int pc_offset =
+            static_cast<int>(sample.pc - pc_entry->instruction_start());
         if (ranges) {
-          Code* code = Code::cast(HeapObject::FromAddress(start));
-          int pc_offset = static_cast<int>(
-              sample.pc - code->instruction_start());
           for (int i = 0; i < ranges->length(); i++) {
             OffsetRange& range = ranges->at(i);
             if (range.from <= pc_offset && pc_offset < range.to) {
@@ -604,6 +682,11 @@
             }
           }
         }
+        src_line = pc_entry->GetSourceLine(pc_offset);
+        if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
+          src_line = pc_entry->line_number();
+        }
+        src_line_not_found = false;
         *entry++ = pc_entry;

         if (pc_entry->builtin_id() == Builtins::kFunctionCall ||
@@ -624,7 +707,22 @@
            *stack_end = stack_pos + sample.frames_count;
          stack_pos != stack_end;
          ++stack_pos) {
-      *entry++ = code_map_.FindEntry(*stack_pos);
+      Address start = NULL;
+      *entry = code_map_.FindEntry(*stack_pos, &start);
+
+ // Skip unresolved frames (e.g. internal frame) and get source line of
+      // the first JS caller.
+      if (src_line_not_found && *entry) {
+        int pc_offset =
+            static_cast<int>(*stack_pos - (*entry)->instruction_start());
+        src_line = (*entry)->GetSourceLine(pc_offset);
+        if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
+          src_line = (*entry)->line_number();
+        }
+        src_line_not_found = false;
+      }
+
+      entry++;
     }
   }

@@ -642,7 +740,7 @@
     }
   }

-  profiles_->AddPathToCurrentProfiles(sample.timestamp, entries);
+  profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line);
 }


=======================================
--- /branches/bleeding_edge/src/profile-generator.h Wed Nov 5 12:40:56 2014 UTC +++ /branches/bleeding_edge/src/profile-generator.h Thu Nov 6 09:16:34 2014 UTC
@@ -5,6 +5,7 @@
 #ifndef V8_PROFILE_GENERATOR_H_
 #define V8_PROFILE_GENERATOR_H_

+#include <map>
 #include "include/v8-profiler.h"
 #include "src/allocation.h"
 #include "src/hashmap.h"
@@ -44,15 +45,35 @@
 };


+// Provides a mapping from the offsets within generated code to
+// the source line.
+class JITLineInfoTable : public Malloced {
+ public:
+  JITLineInfoTable();
+  ~JITLineInfoTable();
+
+  void SetPosition(int pc_offset, int line);
+  int GetSourceLineNumber(int pc_offset) const;
+
+  bool empty() const { return pc_offset_map_.empty(); }
+
+ private:
+  // pc_offset -> source line
+  typedef std::map<int, int> PcOffsetMap;
+  PcOffsetMap pc_offset_map_;
+  DISALLOW_COPY_AND_ASSIGN(JITLineInfoTable);
+};
+
 class CodeEntry {
  public:
   // CodeEntry doesn't own name strings, just references them.
-  inline CodeEntry(Logger::LogEventsAndTags tag,
-                   const char* name,
+  inline CodeEntry(Logger::LogEventsAndTags tag, const char* name,
                    const char* name_prefix = CodeEntry::kEmptyNamePrefix,
const char* resource_name = CodeEntry::kEmptyResourceName,
                    int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
- int column_number = v8::CpuProfileNode::kNoColumnNumberInfo); + int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
+                   JITLineInfoTable* line_info = NULL,
+                   Address instruction_start = NULL);
   ~CodeEntry();

   bool is_js_function() const { return is_js_function_tag(tag()); }
@@ -62,6 +83,7 @@
   const char* resource_name() const { return resource_name_; }
   int line_number() const { return line_number_; }
   int column_number() const { return column_number_; }
+  const JITLineInfoTable* line_info() const { return line_info_; }
   void set_shared_id(int shared_id) { shared_id_ = shared_id; }
   int script_id() const { return script_id_; }
   void set_script_id(int script_id) { script_id_ = script_id; }
@@ -84,6 +106,10 @@

   uint32_t GetCallUid() const;
   bool IsSameAs(CodeEntry* entry) const;
+
+  int GetSourceLine(int pc_offset) const;
+
+  Address instruction_start() const { return instruction_start_; }

   static const char* const kEmptyNamePrefix;
   static const char* const kEmptyResourceName;
@@ -104,6 +130,8 @@
   int script_id_;
   List<OffsetRange>* no_frame_ranges_;
   const char* bailout_reason_;
+  JITLineInfoTable* line_info_;
+  Address instruction_start_;

   DISALLOW_COPY_AND_ASSIGN(CodeEntry);
 };
@@ -119,11 +147,15 @@
   ProfileNode* FindOrAddChild(CodeEntry* entry);
   void IncrementSelfTicks() { ++self_ticks_; }
   void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
+  void IncrementLineTicks(int src_line);

   CodeEntry* entry() const { return entry_; }
   unsigned self_ticks() const { return self_ticks_; }
   const List<ProfileNode*>* children() const { return &children_list_; }
   unsigned id() const { return id_; }
+  unsigned int GetHitLineCount() const { return line_ticks_.occupancy(); }
+  bool GetLineTicks(v8::CpuProfileNode::LineTick* entries,
+                    unsigned int length) const;

   void Print(int indent);

@@ -136,6 +168,8 @@
   static uint32_t CodeEntryHash(CodeEntry* entry) {
     return entry->GetCallUid();
   }
+
+  static bool LineTickMatch(void* a, void* b) { return a == b; }

   ProfileTree* tree_;
   CodeEntry* entry_;
@@ -144,6 +178,7 @@
   HashMap children_;
   List<ProfileNode*> children_list_;
   unsigned id_;
+  HashMap line_ticks_;

   DISALLOW_COPY_AND_ASSIGN(ProfileNode);
 };
@@ -154,8 +189,11 @@
   ProfileTree();
   ~ProfileTree();

-  ProfileNode* AddPathFromEnd(const Vector<CodeEntry*>& path);
-  void AddPathFromStart(const Vector<CodeEntry*>& path);
+  ProfileNode* AddPathFromEnd(
+      const Vector<CodeEntry*>& path,
+      int src_line = v8::CpuProfileNode::kNoLineNumberInfo);
+  void AddPathFromStart(const Vector<CodeEntry*>& path,
+ int src_line = v8::CpuProfileNode::kNoLineNumberInfo);
   ProfileNode* root() const { return root_; }
   unsigned next_node_id() { return next_node_id_++; }

@@ -180,7 +218,8 @@
   CpuProfile(const char* title, bool record_samples);

   // Add pc -> ... -> main() call path to the profile.
-  void AddPath(base::TimeTicks timestamp, const Vector<CodeEntry*>& path);
+  void AddPath(base::TimeTicks timestamp, const Vector<CodeEntry*>& path,
+               int src_line);
   void CalculateTotalTicksAndSamplingRate();

   const char* title() const { return title_; }
@@ -282,16 +321,16 @@
   void RemoveProfile(CpuProfile* profile);

   CodeEntry* NewCodeEntry(
-      Logger::LogEventsAndTags tag,
-      const char* name,
+      Logger::LogEventsAndTags tag, const char* name,
       const char* name_prefix = CodeEntry::kEmptyNamePrefix,
       const char* resource_name = CodeEntry::kEmptyResourceName,
       int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
-      int column_number = v8::CpuProfileNode::kNoColumnNumberInfo);
+      int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
+ JITLineInfoTable* line_info = NULL, Address instruction_start = NULL);

   // Called from profile generator thread.
-  void AddPathToCurrentProfiles(
-      base::TimeTicks timestamp, const Vector<CodeEntry*>& path);
+  void AddPathToCurrentProfiles(base::TimeTicks timestamp,
+ const Vector<CodeEntry*>& path, int src_line);

   // Limits the number of profiles that can be simultaneously collected.
   static const int kMaxSimultaneousProfiles = 100;
=======================================
--- /branches/bleeding_edge/test/cctest/test-cpu-profiler.cc Thu Oct 2 11:58:21 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-cpu-profiler.cc Thu Nov 6 09:16:34 2014 UTC
@@ -1062,6 +1062,104 @@

   profile->Delete();
 }
+
+
+// This tests checks distribution of the samples through the source lines.
+TEST(TickLines) {
+  CcTest::InitializeVM();
+  LocalContext env;
+  i::FLAG_turbo_source_positions = true;
+  i::Isolate* isolate = CcTest::i_isolate();
+  i::Factory* factory = isolate->factory();
+  i::HandleScope scope(isolate);
+
+  i::EmbeddedVector<char, 512> script;
+
+  const char* func_name = "func";
+  i::SNPrintF(script,
+              "function %s() {\n"
+              "  var n = 0;\n"
+              "  var m = 100*100;\n"
+              "  while (m > 1) {\n"
+              "    m--;\n"
+              "    n += m * m * m;\n"
+              "  }\n"
+              "}\n"
+              "%s();\n",
+              func_name, func_name);
+
+  CompileRun(script.start());
+
+  i::Handle<i::JSFunction> func = v8::Utils::OpenHandle(
+ *v8::Local<v8::Function>::Cast((*env)->Global()->Get(v8_str(func_name))));
+  CHECK_NE(NULL, func->shared());
+  CHECK_NE(NULL, func->shared()->code());
+  i::Code* code = NULL;
+  if (func->code()->is_optimized_code()) {
+    code = func->code();
+  } else {
+    CHECK(func->shared()->code() == func->code() || !i::FLAG_crankshaft);
+    code = func->shared()->code();
+  }
+  CHECK_NE(NULL, code);
+  i::Address code_address = code->instruction_start();
+  CHECK_NE(NULL, code_address);
+
+ CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
+  profiles->StartProfiling("", false);
+  ProfileGenerator generator(profiles);
+  ProfilerEventsProcessor* processor = new ProfilerEventsProcessor(
+      &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100));
+  processor->Start();
+  CpuProfiler profiler(isolate, profiles, &generator, processor);
+
+  // Enqueue code creation events.
+  i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
+  int line = 1;
+  int column = 1;
+ profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, code, func->shared(), NULL,
+                           *str, line, column);
+
+  // Enqueue a tick event to enable code events processing.
+  EnqueueTickSampleEvent(processor, code_address);
+
+  processor->StopSynchronously();
+
+  CpuProfile* profile = profiles->StopProfiling("");
+  CHECK_NE(NULL, profile);
+
+  // Check the state of profile generator.
+  CodeEntry* func_entry = generator.code_map()->FindEntry(code_address);
+  CHECK_NE(NULL, func_entry);
+  CHECK_EQ(func_name, func_entry->name());
+  const i::JITLineInfoTable* line_info = func_entry->line_info();
+  CHECK_NE(NULL, line_info);
+  CHECK(!line_info->empty());
+
+  // Check the hit source lines using V8 Public APIs.
+  const i::ProfileTree* tree = profile->top_down();
+  ProfileNode* root = tree->root();
+  CHECK_NE(NULL, root);
+  ProfileNode* func_node = root->FindChild(func_entry);
+  CHECK_NE(NULL, func_node);
+
+  // Add 10 faked ticks to source line #5.
+  int hit_line = 5;
+  int hit_count = 10;
+ for (int i = 0; i < hit_count; i++) func_node->IncrementLineTicks(hit_line);
+
+  unsigned int line_count = func_node->GetHitLineCount();
+  CHECK_EQ(2, line_count);  // Expect two hit source lines - #1 and #5.
+  ScopedVector<v8::CpuProfileNode::LineTick> entries(line_count);
+  CHECK(func_node->GetLineTicks(&entries[0], line_count));
+  int value = 0;
+  for (int i = 0; i < entries.length(); i++)
+    if (entries[i].line == hit_line) {
+      value = entries[i].hit_count;
+      break;
+    }
+  CHECK_EQ(hit_count, value);
+}


static const char* call_function_test_source = "function bar(iterations) {\n"

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to