LCOV - code coverage report
Current view: top level - src/profiler - profile-generator.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 311 331 94.0 %
Date: 2017-10-20 Functions: 47 53 88.7 %

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

Generated by: LCOV version 1.10