Home | History | Annotate | Download | only in profiler
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/profiler/profile-generator.h"
      6 
      7 #include "src/base/adapters.h"
      8 #include "src/debug/debug.h"
      9 #include "src/deoptimizer.h"
     10 #include "src/global-handles.h"
     11 #include "src/objects-inl.h"
     12 #include "src/profiler/cpu-profiler.h"
     13 #include "src/profiler/profile-generator-inl.h"
     14 #include "src/tracing/trace-event.h"
     15 #include "src/tracing/traced-value.h"
     16 #include "src/unicode.h"
     17 
     18 namespace v8 {
     19 namespace internal {
     20 
     21 
     22 JITLineInfoTable::JITLineInfoTable() {}
     23 
     24 
     25 JITLineInfoTable::~JITLineInfoTable() {}
     26 
     27 
     28 void JITLineInfoTable::SetPosition(int pc_offset, int line) {
     29   DCHECK(pc_offset >= 0);
     30   DCHECK(line > 0);  // The 1-based number of the source line.
     31   if (GetSourceLineNumber(pc_offset) != line) {
     32     pc_offset_map_.insert(std::make_pair(pc_offset, line));
     33   }
     34 }
     35 
     36 
     37 int JITLineInfoTable::GetSourceLineNumber(int pc_offset) const {
     38   PcOffsetMap::const_iterator it = pc_offset_map_.lower_bound(pc_offset);
     39   if (it == pc_offset_map_.end()) {
     40     if (pc_offset_map_.empty()) return v8::CpuProfileNode::kNoLineNumberInfo;
     41     return (--pc_offset_map_.end())->second;
     42   }
     43   return it->second;
     44 }
     45 
     46 
     47 const char* const CodeEntry::kEmptyNamePrefix = "";
     48 const char* const CodeEntry::kEmptyResourceName = "";
     49 const char* const CodeEntry::kEmptyBailoutReason = "";
     50 const char* const CodeEntry::kNoDeoptReason = "";
     51 
     52 const char* const CodeEntry::kProgramEntryName = "(program)";
     53 const char* const CodeEntry::kIdleEntryName = "(idle)";
     54 const char* const CodeEntry::kGarbageCollectorEntryName = "(garbage collector)";
     55 const char* const CodeEntry::kUnresolvedFunctionName = "(unresolved function)";
     56 
     57 base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type
     58     CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
     59 
     60 base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type
     61     CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
     62 
     63 base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type
     64     CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
     65 
     66 base::LazyDynamicInstance<CodeEntry,
     67                           CodeEntry::UnresolvedEntryCreateTrait>::type
     68     CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
     69 
     70 CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() {
     71   return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName);
     72 }
     73 
     74 CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() {
     75   return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName);
     76 }
     77 
     78 CodeEntry* CodeEntry::GCEntryCreateTrait::Create() {
     79   return new CodeEntry(Logger::BUILTIN_TAG,
     80                        CodeEntry::kGarbageCollectorEntryName);
     81 }
     82 
     83 CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() {
     84   return new CodeEntry(Logger::FUNCTION_TAG,
     85                        CodeEntry::kUnresolvedFunctionName);
     86 }
     87 
     88 CodeEntry::~CodeEntry() {
     89   delete line_info_;
     90   for (auto location : inline_locations_) {
     91     for (auto entry : location.second) {
     92       delete entry;
     93     }
     94   }
     95 }
     96 
     97 
     98 uint32_t CodeEntry::GetHash() const {
     99   uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed);
    100   if (script_id_ != v8::UnboundScript::kNoScriptId) {
    101     hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_),
    102                                v8::internal::kZeroHashSeed);
    103     hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_),
    104                                v8::internal::kZeroHashSeed);
    105   } else {
    106     hash ^= ComputeIntegerHash(
    107         static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
    108         v8::internal::kZeroHashSeed);
    109     hash ^= ComputeIntegerHash(
    110         static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
    111         v8::internal::kZeroHashSeed);
    112     hash ^= ComputeIntegerHash(
    113         static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
    114         v8::internal::kZeroHashSeed);
    115     hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
    116   }
    117   return hash;
    118 }
    119 
    120 
    121 bool CodeEntry::IsSameFunctionAs(CodeEntry* entry) const {
    122   if (this == entry) return true;
    123   if (script_id_ != v8::UnboundScript::kNoScriptId) {
    124     return script_id_ == entry->script_id_ && position_ == entry->position_;
    125   }
    126   return name_prefix_ == entry->name_prefix_ && name_ == entry->name_ &&
    127          resource_name_ == entry->resource_name_ &&
    128          line_number_ == entry->line_number_;
    129 }
    130 
    131 
    132 void CodeEntry::SetBuiltinId(Builtins::Name id) {
    133   bit_field_ = TagField::update(bit_field_, CodeEventListener::BUILTIN_TAG);
    134   bit_field_ = BuiltinIdField::update(bit_field_, id);
    135 }
    136 
    137 
    138 int CodeEntry::GetSourceLine(int pc_offset) const {
    139   if (line_info_ && !line_info_->empty()) {
    140     return line_info_->GetSourceLineNumber(pc_offset);
    141   }
    142   return v8::CpuProfileNode::kNoLineNumberInfo;
    143 }
    144 
    145 void CodeEntry::AddInlineStack(int pc_offset,
    146                                std::vector<CodeEntry*> inline_stack) {
    147   inline_locations_.insert(std::make_pair(pc_offset, std::move(inline_stack)));
    148 }
    149 
    150 const std::vector<CodeEntry*>* CodeEntry::GetInlineStack(int pc_offset) const {
    151   auto it = inline_locations_.find(pc_offset);
    152   return it != inline_locations_.end() ? &it->second : NULL;
    153 }
    154 
    155 void CodeEntry::AddDeoptInlinedFrames(
    156     int deopt_id, std::vector<CpuProfileDeoptFrame> inlined_frames) {
    157   deopt_inlined_frames_.insert(
    158       std::make_pair(deopt_id, std::move(inlined_frames)));
    159 }
    160 
    161 bool CodeEntry::HasDeoptInlinedFramesFor(int deopt_id) const {
    162   return deopt_inlined_frames_.find(deopt_id) != deopt_inlined_frames_.end();
    163 }
    164 
    165 void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
    166   if (!shared->script()->IsScript()) return;
    167   Script* script = Script::cast(shared->script());
    168   set_script_id(script->id());
    169   set_position(shared->start_position());
    170   set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason()));
    171 }
    172 
    173 CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
    174   DCHECK(has_deopt_info());
    175 
    176   CpuProfileDeoptInfo info;
    177   info.deopt_reason = deopt_reason_;
    178   DCHECK_NE(kNoDeoptimizationId, deopt_id_);
    179   if (deopt_inlined_frames_.find(deopt_id_) == deopt_inlined_frames_.end()) {
    180     info.stack.push_back(CpuProfileDeoptFrame(
    181         {script_id_, static_cast<size_t>(std::max(0, position()))}));
    182   } else {
    183     info.stack = deopt_inlined_frames_[deopt_id_];
    184   }
    185   return info;
    186 }
    187 
    188 
    189 void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
    190   deopt_infos_.push_back(entry->GetDeoptInfo());
    191   entry->clear_deopt_info();
    192 }
    193 
    194 
    195 ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
    196   base::HashMap::Entry* map_entry =
    197       children_.Lookup(entry, CodeEntryHash(entry));
    198   return map_entry != NULL ?
    199       reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
    200 }
    201 
    202 
    203 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
    204   base::HashMap::Entry* map_entry =
    205       children_.LookupOrInsert(entry, CodeEntryHash(entry));
    206   ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value);
    207   if (!node) {
    208     node = new ProfileNode(tree_, entry, this);
    209     map_entry->value = node;
    210     children_list_.Add(node);
    211   }
    212   return node;
    213 }
    214 
    215 
    216 void ProfileNode::IncrementLineTicks(int src_line) {
    217   if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return;
    218   // Increment a hit counter of a certain source line.
    219   // Add a new source line if not found.
    220   base::HashMap::Entry* e =
    221       line_ticks_.LookupOrInsert(reinterpret_cast<void*>(src_line), src_line);
    222   DCHECK(e);
    223   e->value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(e->value) + 1);
    224 }
    225 
    226 
    227 bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
    228                                unsigned int length) const {
    229   if (entries == NULL || length == 0) return false;
    230 
    231   unsigned line_count = line_ticks_.occupancy();
    232 
    233   if (line_count == 0) return true;
    234   if (length < line_count) return false;
    235 
    236   v8::CpuProfileNode::LineTick* entry = entries;
    237 
    238   for (base::HashMap::Entry *p = line_ticks_.Start(); p != NULL;
    239        p = line_ticks_.Next(p), entry++) {
    240     entry->line =
    241         static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->key));
    242     entry->hit_count =
    243         static_cast<unsigned int>(reinterpret_cast<uintptr_t>(p->value));
    244   }
    245 
    246   return true;
    247 }
    248 
    249 
    250 void ProfileNode::Print(int indent) {
    251   base::OS::Print("%5u %*s %s%s %d #%d", self_ticks_, indent, "",
    252                   entry_->name_prefix(), entry_->name(), entry_->script_id(),
    253                   id());
    254   if (entry_->resource_name()[0] != '\0')
    255     base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
    256   base::OS::Print("\n");
    257   for (size_t i = 0; i < deopt_infos_.size(); ++i) {
    258     CpuProfileDeoptInfo& info = deopt_infos_[i];
    259     base::OS::Print("%*s;;; deopted at script_id: %d position: %" PRIuS
    260                     " with reason '%s'.\n",
    261                     indent + 10, "", info.stack[0].script_id,
    262                     info.stack[0].position, info.deopt_reason);
    263     for (size_t index = 1; index < info.stack.size(); ++index) {
    264       base::OS::Print("%*s;;;     Inline point: script_id %d position: %" PRIuS
    265                       ".\n",
    266                       indent + 10, "", info.stack[index].script_id,
    267                       info.stack[index].position);
    268     }
    269   }
    270   const char* bailout_reason = entry_->bailout_reason();
    271   if (bailout_reason != GetBailoutReason(BailoutReason::kNoReason) &&
    272       bailout_reason != CodeEntry::kEmptyBailoutReason) {
    273     base::OS::Print("%*s bailed out due to '%s'\n", indent + 10, "",
    274                     bailout_reason);
    275   }
    276   for (base::HashMap::Entry* p = children_.Start(); p != NULL;
    277        p = children_.Next(p)) {
    278     reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
    279   }
    280 }
    281 
    282 
    283 class DeleteNodesCallback {
    284  public:
    285   void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
    286 
    287   void AfterAllChildrenTraversed(ProfileNode* node) {
    288     delete node;
    289   }
    290 
    291   void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
    292 };
    293 
    294 ProfileTree::ProfileTree(Isolate* isolate)
    295     : root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"),
    296       next_node_id_(1),
    297       root_(new ProfileNode(this, &root_entry_, nullptr)),
    298       isolate_(isolate),
    299       next_function_id_(1),
    300       function_ids_(ProfileNode::CodeEntriesMatch) {}
    301 
    302 ProfileTree::~ProfileTree() {
    303   DeleteNodesCallback cb;
    304   TraverseDepthFirst(&cb);
    305 }
    306 
    307 
    308 unsigned ProfileTree::GetFunctionId(const ProfileNode* node) {
    309   CodeEntry* code_entry = node->entry();
    310   base::HashMap::Entry* entry =
    311       function_ids_.LookupOrInsert(code_entry, code_entry->GetHash());
    312   if (!entry->value) {
    313     entry->value = reinterpret_cast<void*>(next_function_id_++);
    314   }
    315   return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
    316 }
    317 
    318 ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path,
    319                                          int src_line, bool update_stats) {
    320   ProfileNode* node = root_;
    321   CodeEntry* last_entry = NULL;
    322   for (auto it = path.rbegin(); it != path.rend(); ++it) {
    323     if (*it == NULL) continue;
    324     last_entry = *it;
    325     node = node->FindOrAddChild(*it);
    326   }
    327   if (last_entry && last_entry->has_deopt_info()) {
    328     node->CollectDeoptInfo(last_entry);
    329   }
    330   if (update_stats) {
    331     node->IncrementSelfTicks();
    332     if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
    333       node->IncrementLineTicks(src_line);
    334     }
    335   }
    336   return node;
    337 }
    338 
    339 
    340 struct NodesPair {
    341   NodesPair(ProfileNode* src, ProfileNode* dst)
    342       : src(src), dst(dst) { }
    343   ProfileNode* src;
    344   ProfileNode* dst;
    345 };
    346 
    347 
    348 class Position {
    349  public:
    350   explicit Position(ProfileNode* node)
    351       : node(node), child_idx_(0) { }
    352   INLINE(ProfileNode* current_child()) {
    353     return node->children()->at(child_idx_);
    354   }
    355   INLINE(bool has_current_child()) {
    356     return child_idx_ < node->children()->length();
    357   }
    358   INLINE(void next_child()) { ++child_idx_; }
    359 
    360   ProfileNode* node;
    361  private:
    362   int child_idx_;
    363 };
    364 
    365 
    366 // Non-recursive implementation of a depth-first post-order tree traversal.
    367 template <typename Callback>
    368 void ProfileTree::TraverseDepthFirst(Callback* callback) {
    369   List<Position> stack(10);
    370   stack.Add(Position(root_));
    371   while (stack.length() > 0) {
    372     Position& current = stack.last();
    373     if (current.has_current_child()) {
    374       callback->BeforeTraversingChild(current.node, current.current_child());
    375       stack.Add(Position(current.current_child()));
    376     } else {
    377       callback->AfterAllChildrenTraversed(current.node);
    378       if (stack.length() > 1) {
    379         Position& parent = stack[stack.length() - 2];
    380         callback->AfterChildTraversed(parent.node, current.node);
    381         parent.next_child();
    382       }
    383       // Remove child from the stack.
    384       stack.RemoveLast();
    385     }
    386   }
    387 }
    388 
    389 using v8::tracing::TracedValue;
    390 
    391 CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
    392                        bool record_samples)
    393     : title_(title),
    394       record_samples_(record_samples),
    395       start_time_(base::TimeTicks::HighResolutionNow()),
    396       top_down_(profiler->isolate()),
    397       profiler_(profiler),
    398       streaming_next_sample_(0) {
    399   auto value = TracedValue::Create();
    400   value->SetDouble("startTime",
    401                    (start_time_ - base::TimeTicks()).InMicroseconds());
    402   TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
    403                               "Profile", this, "data", std::move(value));
    404 }
    405 
    406 void CpuProfile::AddPath(base::TimeTicks timestamp,
    407                          const std::vector<CodeEntry*>& path, int src_line,
    408                          bool update_stats) {
    409   ProfileNode* top_frame_node =
    410       top_down_.AddPathFromEnd(path, src_line, update_stats);
    411   if (record_samples_ && !timestamp.IsNull()) {
    412     timestamps_.Add(timestamp);
    413     samples_.Add(top_frame_node);
    414   }
    415   const int kSamplesFlushCount = 100;
    416   const int kNodesFlushCount = 10;
    417   if (samples_.length() - streaming_next_sample_ >= kSamplesFlushCount ||
    418       top_down_.pending_nodes_count() >= kNodesFlushCount) {
    419     StreamPendingTraceEvents();
    420   }
    421 }
    422 
    423 namespace {
    424 
    425 void BuildNodeValue(const ProfileNode* node, TracedValue* value) {
    426   const CodeEntry* entry = node->entry();
    427   value->BeginDictionary("callFrame");
    428   value->SetString("functionName", entry->name());
    429   if (*entry->resource_name()) {
    430     value->SetString("url", entry->resource_name());
    431   }
    432   value->SetInteger("scriptId", entry->script_id());
    433   if (entry->line_number()) {
    434     value->SetInteger("lineNumber", entry->line_number() - 1);
    435   }
    436   if (entry->column_number()) {
    437     value->SetInteger("columnNumber", entry->column_number() - 1);
    438   }
    439   value->EndDictionary();
    440   value->SetInteger("id", node->id());
    441   if (node->parent()) {
    442     value->SetInteger("parent", node->parent()->id());
    443   }
    444   const char* deopt_reason = entry->bailout_reason();
    445   if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason, "no reason")) {
    446     value->SetString("deoptReason", deopt_reason);
    447   }
    448 }
    449 
    450 }  // namespace
    451 
    452 void CpuProfile::StreamPendingTraceEvents() {
    453   std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes();
    454   if (pending_nodes.empty() && !samples_.length()) return;
    455   auto value = TracedValue::Create();
    456 
    457   if (!pending_nodes.empty() || streaming_next_sample_ != samples_.length()) {
    458     value->BeginDictionary("cpuProfile");
    459     if (!pending_nodes.empty()) {
    460       value->BeginArray("nodes");
    461       for (auto node : pending_nodes) {
    462         value->BeginDictionary();
    463         BuildNodeValue(node, value.get());
    464         value->EndDictionary();
    465       }
    466       value->EndArray();
    467     }
    468     if (streaming_next_sample_ != samples_.length()) {
    469       value->BeginArray("samples");
    470       for (int i = streaming_next_sample_; i < samples_.length(); ++i) {
    471         value->AppendInteger(samples_[i]->id());
    472       }
    473       value->EndArray();
    474     }
    475     value->EndDictionary();
    476   }
    477   if (streaming_next_sample_ != samples_.length()) {
    478     value->BeginArray("timeDeltas");
    479     base::TimeTicks lastTimestamp =
    480         streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1]
    481                                : start_time();
    482     for (int i = streaming_next_sample_; i < timestamps_.length(); ++i) {
    483       value->AppendInteger(
    484           static_cast<int>((timestamps_[i] - lastTimestamp).InMicroseconds()));
    485       lastTimestamp = timestamps_[i];
    486     }
    487     value->EndArray();
    488     DCHECK(samples_.length() == timestamps_.length());
    489     streaming_next_sample_ = samples_.length();
    490   }
    491 
    492   TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
    493                               "ProfileChunk", this, "data", std::move(value));
    494 }
    495 
    496 void CpuProfile::FinishProfile() {
    497   end_time_ = base::TimeTicks::HighResolutionNow();
    498   StreamPendingTraceEvents();
    499   auto value = TracedValue::Create();
    500   value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds());
    501   TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
    502                               "ProfileChunk", this, "data", std::move(value));
    503 }
    504 
    505 void CpuProfile::Print() {
    506   base::OS::Print("[Top down]:\n");
    507   top_down_.Print();
    508 }
    509 
    510 void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
    511   DeleteAllCoveredCode(addr, addr + size);
    512   code_map_.insert({addr, CodeEntryInfo(entry, size)});
    513 }
    514 
    515 void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
    516   auto left = code_map_.upper_bound(start);
    517   if (left != code_map_.begin()) {
    518     --left;
    519     if (left->first + left->second.size <= start) ++left;
    520   }
    521   auto right = left;
    522   while (right != code_map_.end() && right->first < end) ++right;
    523   code_map_.erase(left, right);
    524 }
    525 
    526 CodeEntry* CodeMap::FindEntry(Address addr) {
    527   auto it = code_map_.upper_bound(addr);
    528   if (it == code_map_.begin()) return nullptr;
    529   --it;
    530   Address end_address = it->first + it->second.size;
    531   return addr < end_address ? it->second.entry : nullptr;
    532 }
    533 
    534 void CodeMap::MoveCode(Address from, Address to) {
    535   if (from == to) return;
    536   auto it = code_map_.find(from);
    537   if (it == code_map_.end()) return;
    538   CodeEntryInfo info = it->second;
    539   code_map_.erase(it);
    540   AddCode(to, info.entry, info.size);
    541 }
    542 
    543 void CodeMap::Print() {
    544   for (auto it = code_map_.begin(); it != code_map_.end(); ++it) {
    545     base::OS::Print("%p %5d %s\n", static_cast<void*>(it->first),
    546                     it->second.size, it->second.entry->name());
    547   }
    548 }
    549 
    550 CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
    551     : resource_names_(isolate->heap()),
    552       profiler_(nullptr),
    553       current_profiles_semaphore_(1) {}
    554 
    555 static void DeleteCpuProfile(CpuProfile** profile_ptr) {
    556   delete *profile_ptr;
    557 }
    558 
    559 
    560 CpuProfilesCollection::~CpuProfilesCollection() {
    561   finished_profiles_.Iterate(DeleteCpuProfile);
    562   current_profiles_.Iterate(DeleteCpuProfile);
    563 }
    564 
    565 
    566 bool CpuProfilesCollection::StartProfiling(const char* title,
    567                                            bool record_samples) {
    568   current_profiles_semaphore_.Wait();
    569   if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
    570     current_profiles_semaphore_.Signal();
    571     return false;
    572   }
    573   for (int i = 0; i < current_profiles_.length(); ++i) {
    574     if (strcmp(current_profiles_[i]->title(), title) == 0) {
    575       // Ignore attempts to start profile with the same title...
    576       current_profiles_semaphore_.Signal();
    577       // ... though return true to force it collect a sample.
    578       return true;
    579     }
    580   }
    581   current_profiles_.Add(new CpuProfile(profiler_, title, record_samples));
    582   current_profiles_semaphore_.Signal();
    583   return true;
    584 }
    585 
    586 
    587 CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
    588   const int title_len = StrLength(title);
    589   CpuProfile* profile = nullptr;
    590   current_profiles_semaphore_.Wait();
    591   for (int i = current_profiles_.length() - 1; i >= 0; --i) {
    592     if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
    593       profile = current_profiles_.Remove(i);
    594       break;
    595     }
    596   }
    597   current_profiles_semaphore_.Signal();
    598 
    599   if (!profile) return nullptr;
    600   profile->FinishProfile();
    601   finished_profiles_.Add(profile);
    602   return profile;
    603 }
    604 
    605 
    606 bool CpuProfilesCollection::IsLastProfile(const char* title) {
    607   // Called from VM thread, and only it can mutate the list,
    608   // so no locking is needed here.
    609   if (current_profiles_.length() != 1) return false;
    610   return StrLength(title) == 0
    611       || strcmp(current_profiles_[0]->title(), title) == 0;
    612 }
    613 
    614 
    615 void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
    616   // Called from VM thread for a completed profile.
    617   for (int i = 0; i < finished_profiles_.length(); i++) {
    618     if (profile == finished_profiles_[i]) {
    619       finished_profiles_.Remove(i);
    620       return;
    621     }
    622   }
    623   UNREACHABLE();
    624 }
    625 
    626 void CpuProfilesCollection::AddPathToCurrentProfiles(
    627     base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
    628     int src_line, bool update_stats) {
    629   // As starting / stopping profiles is rare relatively to this
    630   // method, we don't bother minimizing the duration of lock holding,
    631   // e.g. copying contents of the list to a local vector.
    632   current_profiles_semaphore_.Wait();
    633   for (int i = 0; i < current_profiles_.length(); ++i) {
    634     current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats);
    635   }
    636   current_profiles_semaphore_.Signal();
    637 }
    638 
    639 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
    640     : profiles_(profiles) {}
    641 
    642 void ProfileGenerator::RecordTickSample(const TickSample& sample) {
    643   std::vector<CodeEntry*> entries;
    644   // Conservatively reserve space for stack frames + pc + function + vm-state.
    645   // There could in fact be more of them because of inlined entries.
    646   entries.reserve(sample.frames_count + 3);
    647 
    648   // The ProfileNode knows nothing about all versions of generated code for
    649   // the same JS function. The line number information associated with
    650   // the latest version of generated code is used to find a source line number
    651   // for a JS function. Then, the detected source line is passed to
    652   // ProfileNode to increase the tick count for this source line.
    653   int src_line = v8::CpuProfileNode::kNoLineNumberInfo;
    654   bool src_line_not_found = true;
    655 
    656   if (sample.pc != nullptr) {
    657     if (sample.has_external_callback && sample.state == EXTERNAL) {
    658       // Don't use PC when in external callback code, as it can point
    659       // inside callback's code, and we will erroneously report
    660       // that a callback calls itself.
    661       entries.push_back(FindEntry(sample.external_callback_entry));
    662     } else {
    663       CodeEntry* pc_entry = FindEntry(sample.pc);
    664       // If there is no pc_entry we're likely in native code.
    665       // Find out, if top of stack was pointing inside a JS function
    666       // meaning that we have encountered a frameless invocation.
    667       if (!pc_entry && !sample.has_external_callback) {
    668         pc_entry = FindEntry(sample.tos);
    669       }
    670       // If pc is in the function code before it set up stack frame or after the
    671       // frame was destroyed SafeStackFrameIterator incorrectly thinks that
    672       // ebp contains return address of the current function and skips caller's
    673       // frame. Check for this case and just skip such samples.
    674       if (pc_entry) {
    675         int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) -
    676                                          pc_entry->instruction_start());
    677         src_line = pc_entry->GetSourceLine(pc_offset);
    678         if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
    679           src_line = pc_entry->line_number();
    680         }
    681         src_line_not_found = false;
    682         entries.push_back(pc_entry);
    683 
    684         if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
    685             pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
    686           // When current function is either the Function.prototype.apply or the
    687           // Function.prototype.call builtin the top frame is either frame of
    688           // the calling JS function or internal frame.
    689           // In the latter case we know the caller for sure but in the
    690           // former case we don't so we simply replace the frame with
    691           // 'unresolved' entry.
    692           if (!sample.has_external_callback) {
    693             entries.push_back(CodeEntry::unresolved_entry());
    694           }
    695         }
    696       }
    697     }
    698 
    699     for (unsigned i = 0; i < sample.frames_count; ++i) {
    700       Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
    701       CodeEntry* entry = FindEntry(stack_pos);
    702       if (entry) {
    703         // Find out if the entry has an inlining stack associated.
    704         int pc_offset =
    705             static_cast<int>(stack_pos - entry->instruction_start());
    706         const std::vector<CodeEntry*>* inline_stack =
    707             entry->GetInlineStack(pc_offset);
    708         if (inline_stack) {
    709           entries.insert(entries.end(), inline_stack->rbegin(),
    710                          inline_stack->rend());
    711         }
    712         // Skip unresolved frames (e.g. internal frame) and get source line of
    713         // the first JS caller.
    714         if (src_line_not_found) {
    715           src_line = entry->GetSourceLine(pc_offset);
    716           if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
    717             src_line = entry->line_number();
    718           }
    719           src_line_not_found = false;
    720         }
    721       }
    722       entries.push_back(entry);
    723     }
    724   }
    725 
    726   if (FLAG_prof_browser_mode) {
    727     bool no_symbolized_entries = true;
    728     for (auto e : entries) {
    729       if (e != NULL) {
    730         no_symbolized_entries = false;
    731         break;
    732       }
    733     }
    734     // If no frames were symbolized, put the VM state entry in.
    735     if (no_symbolized_entries) {
    736       entries.push_back(EntryForVMState(sample.state));
    737     }
    738   }
    739 
    740   profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line,
    741                                       sample.update_stats);
    742 }
    743 
    744 CodeEntry* ProfileGenerator::FindEntry(void* address) {
    745   return code_map_.FindEntry(reinterpret_cast<Address>(address));
    746 }
    747 
    748 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
    749   switch (tag) {
    750     case GC:
    751       return CodeEntry::gc_entry();
    752     case JS:
    753     case COMPILER:
    754     // DOM events handlers are reported as OTHER / EXTERNAL entries.
    755     // To avoid confusing people, let's put all these entries into
    756     // one bucket.
    757     case OTHER:
    758     case EXTERNAL:
    759       return CodeEntry::program_entry();
    760     case IDLE:
    761       return CodeEntry::idle_entry();
    762     default: return NULL;
    763   }
    764 }
    765 
    766 }  // namespace internal
    767 }  // namespace v8
    768