5 #include "src/profiler/profile-generator.h" 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" 21 void SourcePositionTable::SetPosition(
int pc_offset,
int line) {
22 DCHECK_GE(pc_offset, 0);
26 DCHECK(pc_offsets_to_lines_.empty() ||
27 pc_offsets_to_lines_.back().pc_offset < pc_offset);
28 if (pc_offsets_to_lines_.empty() ||
29 pc_offsets_to_lines_.back().line_number != line) {
30 pc_offsets_to_lines_.push_back({pc_offset, line});
34 int SourcePositionTable::GetSourceLineNumber(
int pc_offset)
const {
35 if (pc_offsets_to_lines_.empty()) {
36 return v8::CpuProfileNode::kNoLineNumberInfo;
39 std::upper_bound(pc_offsets_to_lines_.begin(), pc_offsets_to_lines_.end(),
40 PCOffsetAndLineNumber{pc_offset, 0});
41 if (it != pc_offsets_to_lines_.begin()) --it;
42 return it->line_number;
45 const char*
const CodeEntry::kWasmResourceNamePrefix =
"wasm ";
46 const char*
const CodeEntry::kEmptyResourceName =
"";
47 const char*
const CodeEntry::kEmptyBailoutReason =
"";
48 const char*
const CodeEntry::kNoDeoptReason =
"";
50 const char*
const CodeEntry::kProgramEntryName =
"(program)";
51 const char*
const CodeEntry::kIdleEntryName =
"(idle)";
52 const char*
const CodeEntry::kGarbageCollectorEntryName =
"(garbage collector)";
53 const char*
const CodeEntry::kUnresolvedFunctionName =
"(unresolved function)";
55 base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type
56 CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
58 base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type
59 CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
61 base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type
62 CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
64 base::LazyDynamicInstance<CodeEntry,
65 CodeEntry::UnresolvedEntryCreateTrait>::type
66 CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
68 CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() {
69 return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName);
72 CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() {
73 return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName);
76 CodeEntry* CodeEntry::GCEntryCreateTrait::Create() {
77 return new CodeEntry(Logger::BUILTIN_TAG,
78 CodeEntry::kGarbageCollectorEntryName);
81 CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() {
82 return new CodeEntry(Logger::FUNCTION_TAG,
83 CodeEntry::kUnresolvedFunctionName);
86 uint32_t CodeEntry::GetHash()
const {
87 uint32_t hash = ComputeUnseededHash(tag());
88 if (script_id_ != v8::UnboundScript::kNoScriptId) {
89 hash ^= ComputeUnseededHash(static_cast<uint32_t>(script_id_));
90 hash ^= ComputeUnseededHash(static_cast<uint32_t>(position_));
92 hash ^= ComputeUnseededHash(
93 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
94 hash ^= ComputeUnseededHash(
95 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
96 hash ^= ComputeUnseededHash(line_number_);
101 bool CodeEntry::IsSameFunctionAs(
const CodeEntry* entry)
const {
102 if (
this == entry)
return true;
103 if (script_id_ != v8::UnboundScript::kNoScriptId) {
104 return script_id_ == entry->script_id_ && position_ == entry->position_;
106 return name_ == entry->name_ && resource_name_ == entry->resource_name_ &&
107 line_number_ == entry->line_number_;
111 void CodeEntry::SetBuiltinId(Builtins::Name
id) {
112 bit_field_ = TagField::update(bit_field_, CodeEventListener::BUILTIN_TAG);
113 bit_field_ = BuiltinIdField::update(bit_field_,
id);
117 int CodeEntry::GetSourceLine(
int pc_offset)
const {
118 if (line_info_)
return line_info_->GetSourceLineNumber(pc_offset);
119 return v8::CpuProfileNode::kNoLineNumberInfo;
122 void CodeEntry::AddInlineStack(
123 int pc_offset, std::vector<std::unique_ptr<CodeEntry>> inline_stack) {
124 EnsureRareData()->inline_locations_.insert(
125 std::make_pair(pc_offset, std::move(inline_stack)));
128 const std::vector<std::unique_ptr<CodeEntry>>* CodeEntry::GetInlineStack(
129 int pc_offset)
const {
130 if (!rare_data_)
return nullptr;
131 auto it = rare_data_->inline_locations_.find(pc_offset);
132 return it != rare_data_->inline_locations_.end() ? &it->second :
nullptr;
135 void CodeEntry::set_deopt_info(
136 const char* deopt_reason,
int deopt_id,
137 std::vector<CpuProfileDeoptFrame> inlined_frames) {
138 DCHECK(!has_deopt_info());
139 RareData* rare_data = EnsureRareData();
140 rare_data->deopt_reason_ = deopt_reason;
141 rare_data->deopt_id_ = deopt_id;
142 rare_data->deopt_inlined_frames_ = std::move(inlined_frames);
145 void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
146 if (!shared->script()->IsScript())
return;
147 Script* script = Script::cast(shared->script());
148 set_script_id(script->id());
149 set_position(shared->StartPosition());
150 if (shared->optimization_disabled()) {
151 set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason()));
155 CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
156 DCHECK(has_deopt_info());
158 CpuProfileDeoptInfo info;
159 info.deopt_reason = rare_data_->deopt_reason_;
160 DCHECK_NE(kNoDeoptimizationId, rare_data_->deopt_id_);
161 if (rare_data_->deopt_inlined_frames_.empty()) {
162 info.stack.push_back(CpuProfileDeoptFrame(
163 {script_id_,
static_cast<size_t>(std::max(0, position()))}));
165 info.stack = rare_data_->deopt_inlined_frames_;
170 CodeEntry::RareData* CodeEntry::EnsureRareData() {
172 rare_data_.reset(
new RareData());
174 return rare_data_.get();
177 void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
178 deopt_infos_.push_back(entry->GetDeoptInfo());
179 entry->clear_deopt_info();
182 ProfileNode* ProfileNode::FindChild(CodeEntry* entry,
int line_number) {
183 auto map_entry = children_.find({entry, line_number});
184 return map_entry != children_.end() ? map_entry->second :
nullptr;
187 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry,
int line_number) {
188 auto map_entry = children_.find({entry, line_number});
189 if (map_entry == children_.end()) {
190 ProfileNode* node =
new ProfileNode(tree_, entry,
this, line_number);
191 children_[{entry, line_number}] = node;
192 children_list_.push_back(node);
195 return map_entry->second;
200 void ProfileNode::IncrementLineTicks(
int src_line) {
201 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo)
return;
204 auto map_entry = line_ticks_.find(src_line);
205 if (map_entry == line_ticks_.end()) {
206 line_ticks_[src_line] = 1;
208 line_ticks_[src_line]++;
214 unsigned int length)
const {
215 if (entries ==
nullptr || length == 0)
return false;
217 unsigned line_count =
static_cast<unsigned>(line_ticks_.size());
219 if (line_count == 0)
return true;
220 if (length < line_count)
return false;
224 for (
auto p = line_ticks_.begin(); p != line_ticks_.end(); p++, entry++) {
225 entry->
line = p->first;
233 void ProfileNode::Print(
int indent) {
234 int line_number = line_number_ != 0 ? line_number_ : entry_->line_number();
235 base::OS::Print(
"%5u %*s %s:%d %d #%d", self_ticks_, indent,
"",
236 entry_->name(), line_number, entry_->script_id(), id());
237 if (entry_->resource_name()[0] !=
'\0')
238 base::OS::Print(
" %s:%d", entry_->resource_name(), entry_->line_number());
239 base::OS::Print(
"\n");
240 for (
size_t i = 0;
i < deopt_infos_.size(); ++
i) {
241 CpuProfileDeoptInfo& info = deopt_infos_[
i];
242 base::OS::Print(
"%*s;;; deopted at script_id: %d position: %" PRIuS
243 " with reason '%s'.\n",
244 indent + 10,
"", info.stack[0].script_id,
245 info.stack[0].position, info.deopt_reason);
246 for (
size_t index = 1; index < info.stack.size(); ++index) {
247 base::OS::Print(
"%*s;;; Inline point: script_id %d position: %" PRIuS
249 indent + 10,
"", info.stack[index].script_id,
250 info.stack[index].position);
253 const char* bailout_reason = entry_->bailout_reason();
254 if (bailout_reason != GetBailoutReason(BailoutReason::kNoReason) &&
255 bailout_reason != CodeEntry::kEmptyBailoutReason) {
256 base::OS::Print(
"%*s bailed out due to '%s'\n", indent + 10,
"",
259 for (
auto child : children_) {
260 child.second->Print(indent + 2);
269 void AfterAllChildrenTraversed(
ProfileNode* node) {
276 ProfileTree::ProfileTree(
Isolate* isolate)
279 root_(new
ProfileNode(this, &root_entry_, nullptr)),
281 next_function_id_(1) {}
283 ProfileTree::~ProfileTree() {
284 DeleteNodesCallback cb;
285 TraverseDepthFirst(&cb);
289 unsigned ProfileTree::GetFunctionId(
const ProfileNode* node) {
290 CodeEntry* code_entry = node->entry();
291 auto map_entry = function_ids_.find(code_entry);
292 if (map_entry == function_ids_.end()) {
293 return function_ids_[code_entry] = next_function_id_++;
295 return function_ids_[code_entry];
298 ProfileNode* ProfileTree::AddPathFromEnd(
const std::vector<CodeEntry*>& path,
299 int src_line,
bool update_stats) {
300 ProfileNode* node = root_;
301 CodeEntry* last_entry =
nullptr;
302 for (
auto it = path.rbegin(); it != path.rend(); ++it) {
303 if (*it ==
nullptr)
continue;
305 node = node->FindOrAddChild(*it, v8::CpuProfileNode::kNoLineNumberInfo);
307 if (last_entry && last_entry->has_deopt_info()) {
308 node->CollectDeoptInfo(last_entry);
311 node->IncrementSelfTicks();
312 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
313 node->IncrementLineTicks(src_line);
319 ProfileNode* ProfileTree::AddPathFromEnd(
const ProfileStackTrace& path,
320 int src_line,
bool update_stats,
321 ProfilingMode mode) {
322 ProfileNode* node = root_;
323 CodeEntry* last_entry =
nullptr;
324 int parent_line_number = v8::CpuProfileNode::kNoLineNumberInfo;
325 for (
auto it = path.rbegin(); it != path.rend(); ++it) {
326 if ((*it).code_entry ==
nullptr)
continue;
327 last_entry = (*it).code_entry;
328 node = node->FindOrAddChild((*it).code_entry, parent_line_number);
329 parent_line_number = mode == ProfilingMode::kCallerLineNumbers
331 : v8::CpuProfileNode::kNoLineNumberInfo;
333 if (last_entry && last_entry->has_deopt_info()) {
334 node->CollectDeoptInfo(last_entry);
337 node->IncrementSelfTicks();
338 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
339 node->IncrementLineTicks(src_line);
349 : node(node), child_idx_(0) { }
351 return node->children()->at(child_idx_);
353 V8_INLINE
bool has_current_child() {
354 return child_idx_ < static_cast<int>(node->children()->size());
356 V8_INLINE
void next_child() { ++child_idx_; }
365 template <
typename Callback>
366 void ProfileTree::TraverseDepthFirst(Callback* callback) {
367 std::vector<Position> stack;
368 stack.emplace_back(root_);
369 while (stack.size() > 0) {
371 if (current.has_current_child()) {
372 callback->BeforeTraversingChild(current.node, current.current_child());
373 stack.emplace_back(current.current_child());
375 callback->AfterAllChildrenTraversed(current.node);
376 if (stack.size() > 1) {
377 Position& parent = stack[stack.size() - 2];
378 callback->AfterChildTraversed(parent.node, current.node);
389 std::atomic<uint32_t> CpuProfile::last_id_;
391 CpuProfile::CpuProfile(CpuProfiler* profiler,
const char* title,
392 bool record_samples, ProfilingMode mode)
394 record_samples_(record_samples),
396 start_time_(base::TimeTicks::HighResolutionNow()),
397 top_down_(profiler->isolate()),
399 streaming_next_sample_(0),
401 auto value = TracedValue::Create();
402 value->SetDouble(
"startTime",
403 (start_time_ - base::TimeTicks()).InMicroseconds());
404 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT(
"v8.cpu_profiler"),
405 "Profile", id_,
"data", std::move(value));
408 void CpuProfile::AddPath(base::TimeTicks timestamp,
409 const ProfileStackTrace& path,
int src_line,
411 ProfileNode* top_frame_node =
412 top_down_.AddPathFromEnd(path, src_line, update_stats, mode_);
414 if (record_samples_ && !timestamp.IsNull()) {
415 timestamps_.push_back(timestamp);
416 samples_.push_back(top_frame_node);
419 const int kSamplesFlushCount = 100;
420 const int kNodesFlushCount = 10;
421 if (samples_.size() - streaming_next_sample_ >= kSamplesFlushCount ||
422 top_down_.pending_nodes_count() >= kNodesFlushCount) {
423 StreamPendingTraceEvents();
429 void BuildNodeValue(
const ProfileNode* node, TracedValue* value) {
430 const CodeEntry* entry = node->entry();
431 value->BeginDictionary(
"callFrame");
432 value->SetString(
"functionName", entry->name());
433 if (*entry->resource_name()) {
434 value->SetString(
"url", entry->resource_name());
436 value->SetInteger(
"scriptId", entry->script_id());
437 if (entry->line_number()) {
438 value->SetInteger(
"lineNumber", entry->line_number() - 1);
440 if (entry->column_number()) {
441 value->SetInteger(
"columnNumber", entry->column_number() - 1);
443 value->EndDictionary();
444 value->SetInteger(
"id", node->id());
445 if (node->parent()) {
446 value->SetInteger(
"parent", node->parent()->id());
448 const char* deopt_reason = entry->bailout_reason();
449 if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason,
"no reason")) {
450 value->SetString(
"deoptReason", deopt_reason);
456 void CpuProfile::StreamPendingTraceEvents() {
457 std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes();
458 if (pending_nodes.empty() && samples_.empty())
return;
459 auto value = TracedValue::Create();
461 if (!pending_nodes.empty() || streaming_next_sample_ != samples_.size()) {
462 value->BeginDictionary(
"cpuProfile");
463 if (!pending_nodes.empty()) {
464 value->BeginArray(
"nodes");
465 for (
auto node : pending_nodes) {
466 value->BeginDictionary();
467 BuildNodeValue(node, value.get());
468 value->EndDictionary();
472 if (streaming_next_sample_ != samples_.size()) {
473 value->BeginArray(
"samples");
474 for (
size_t i = streaming_next_sample_;
i < samples_.size(); ++
i) {
475 value->AppendInteger(samples_[
i]->
id());
479 value->EndDictionary();
481 if (streaming_next_sample_ != samples_.size()) {
482 value->BeginArray(
"timeDeltas");
483 base::TimeTicks lastTimestamp =
484 streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1]
486 for (
size_t i = streaming_next_sample_;
i < timestamps_.size(); ++
i) {
487 value->AppendInteger(
488 static_cast<int>((timestamps_[
i] - lastTimestamp).InMicroseconds()));
489 lastTimestamp = timestamps_[
i];
492 DCHECK_EQ(samples_.size(), timestamps_.size());
493 streaming_next_sample_ = samples_.size();
496 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT(
"v8.cpu_profiler"),
497 "ProfileChunk", id_,
"data", std::move(value));
500 void CpuProfile::FinishProfile() {
501 end_time_ = base::TimeTicks::HighResolutionNow();
502 StreamPendingTraceEvents();
503 auto value = TracedValue::Create();
504 value->SetDouble(
"endTime", (end_time_ - base::TimeTicks()).InMicroseconds());
505 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT(
"v8.cpu_profiler"),
506 "ProfileChunk", id_,
"data", std::move(value));
509 void CpuProfile::Print() {
510 base::OS::Print(
"[Top down]:\n");
514 CodeMap::CodeMap() =
default;
516 CodeMap::~CodeMap() {
519 unsigned free_slot = free_list_head_;
520 while (free_slot != kNoFreeSlot) {
521 unsigned next_slot = code_entries_[free_slot].next_free_slot;
522 code_entries_[free_slot].entry =
nullptr;
523 free_slot = next_slot;
525 for (
auto slot : code_entries_)
delete slot.entry;
528 void CodeMap::AddCode(Address addr, CodeEntry* entry,
unsigned size) {
529 ClearCodesInRange(addr, addr + size);
530 unsigned index = AddCodeEntry(addr, entry);
531 code_map_.emplace(addr, CodeEntryMapInfo{index, size});
532 DCHECK(entry->instruction_start() == kNullAddress ||
533 addr == entry->instruction_start());
536 void CodeMap::ClearCodesInRange(Address start, Address end) {
537 auto left = code_map_.upper_bound(start);
538 if (left != code_map_.begin()) {
540 if (left->first + left->second.size <= start) ++left;
543 for (; right != code_map_.end() && right->first < end; ++right) {
544 if (!entry(right->second.index)->used()) {
545 DeleteCodeEntry(right->second.index);
548 code_map_.erase(left, right);
551 CodeEntry* CodeMap::FindEntry(Address addr) {
552 auto it = code_map_.upper_bound(addr);
553 if (it == code_map_.begin())
return nullptr;
555 Address start_address = it->first;
556 Address end_address = start_address + it->second.size;
557 CodeEntry* ret = addr < end_address ? entry(it->second.index) : nullptr;
558 if (ret && ret->instruction_start() != kNullAddress) {
559 DCHECK_EQ(start_address, ret->instruction_start());
560 DCHECK(addr >= start_address && addr < end_address);
565 void CodeMap::MoveCode(Address from, Address to) {
566 if (from == to)
return;
567 auto it = code_map_.find(from);
568 if (it == code_map_.end())
return;
569 CodeEntryMapInfo info = it->second;
571 DCHECK(from + info.size <= to || to + info.size <= from);
572 ClearCodesInRange(to, to + info.size);
573 code_map_.emplace(to, info);
575 CodeEntry* entry = code_entries_[info.index].entry;
576 entry->set_instruction_start(to);
579 unsigned CodeMap::AddCodeEntry(Address start, CodeEntry* entry) {
580 if (free_list_head_ == kNoFreeSlot) {
581 code_entries_.push_back(CodeEntrySlotInfo{entry});
582 return static_cast<unsigned>(code_entries_.size()) - 1;
584 unsigned index = free_list_head_;
585 free_list_head_ = code_entries_[index].next_free_slot;
586 code_entries_[index].entry = entry;
590 void CodeMap::DeleteCodeEntry(
unsigned index) {
591 delete code_entries_[index].entry;
592 code_entries_[index].next_free_slot = free_list_head_;
593 free_list_head_ = index;
596 void CodeMap::Print() {
597 for (
const auto& pair : code_map_) {
598 base::OS::Print(
"%p %5d %s\n", reinterpret_cast<void*>(pair.first),
599 pair.second.size, entry(pair.second.index)->name());
603 CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
604 : profiler_(nullptr), current_profiles_semaphore_(1) {}
606 bool CpuProfilesCollection::StartProfiling(
const char* title,
608 ProfilingMode mode) {
609 current_profiles_semaphore_.Wait();
610 if (static_cast<int>(current_profiles_.size()) >= kMaxSimultaneousProfiles) {
611 current_profiles_semaphore_.Signal();
614 for (
const std::unique_ptr<CpuProfile>& profile : current_profiles_) {
615 if (strcmp(profile->title(), title) == 0) {
617 current_profiles_semaphore_.Signal();
622 current_profiles_.emplace_back(
623 new CpuProfile(profiler_, title, record_samples, mode));
624 current_profiles_semaphore_.Signal();
629 CpuProfile* CpuProfilesCollection::StopProfiling(
const char* title) {
630 const int title_len = StrLength(title);
631 CpuProfile* profile =
nullptr;
632 current_profiles_semaphore_.Wait();
635 std::find_if(current_profiles_.rbegin(), current_profiles_.rend(),
636 [&](
const std::unique_ptr<CpuProfile>& p) {
637 return title_len == 0 || strcmp(p->title(), title) == 0;
640 if (it != current_profiles_.rend()) {
641 (*it)->FinishProfile();
643 finished_profiles_.push_back(std::move(*it));
645 current_profiles_.erase(--(it.base()));
648 current_profiles_semaphore_.Signal();
653 bool CpuProfilesCollection::IsLastProfile(
const char* title) {
656 if (current_profiles_.size() != 1)
return false;
657 return StrLength(title) == 0
658 || strcmp(current_profiles_[0]->title(), title) == 0;
662 void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
665 std::find_if(finished_profiles_.begin(), finished_profiles_.end(),
666 [&](
const std::unique_ptr<CpuProfile>& finished_profile) {
667 return finished_profile.get() == profile;
669 DCHECK(pos != finished_profiles_.end());
670 finished_profiles_.erase(pos);
673 void CpuProfilesCollection::AddPathToCurrentProfiles(
674 base::TimeTicks timestamp,
const ProfileStackTrace& path,
int src_line,
679 current_profiles_semaphore_.Wait();
680 for (
const std::unique_ptr<CpuProfile>& profile : current_profiles_) {
681 profile->AddPath(timestamp, path, src_line, update_stats);
683 current_profiles_semaphore_.Signal();
686 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
687 : profiles_(profiles) {}
689 void ProfileGenerator::RecordTickSample(
const TickSample& sample) {
690 ProfileStackTrace stack_trace;
693 stack_trace.reserve(sample.frames_count + 3);
700 const int no_line_info = v8::CpuProfileNode::kNoLineNumberInfo;
701 int src_line = no_line_info;
702 bool src_line_not_found =
true;
704 if (sample.pc !=
nullptr) {
705 if (sample.has_external_callback && sample.state == EXTERNAL) {
709 stack_trace.push_back(
710 {FindEntry(reinterpret_cast<Address>(sample.external_callback_entry)),
713 Address attributed_pc =
reinterpret_cast<Address
>(sample.pc);
714 CodeEntry* pc_entry = FindEntry(attributed_pc);
718 if (!pc_entry && !sample.has_external_callback) {
719 attributed_pc =
reinterpret_cast<Address
>(sample.tos);
720 pc_entry = FindEntry(attributed_pc);
728 static_cast<int>(attributed_pc - pc_entry->instruction_start());
730 src_line = pc_entry->GetSourceLine(pc_offset);
731 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
732 src_line = pc_entry->line_number();
734 src_line_not_found =
false;
735 stack_trace.push_back({pc_entry, src_line});
737 if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
738 pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
745 if (!sample.has_external_callback) {
746 stack_trace.push_back(
747 {CodeEntry::unresolved_entry(), no_line_info});
753 for (
unsigned i = 0;
i < sample.frames_count; ++
i) {
754 Address stack_pos =
reinterpret_cast<Address
>(sample.stack[
i]);
755 CodeEntry* entry = FindEntry(stack_pos);
756 int line_number = no_line_info;
760 static_cast<int>(stack_pos - entry->instruction_start());
762 const std::vector<std::unique_ptr<CodeEntry>>* inline_stack =
763 entry->GetInlineStack(pc_offset);
766 inline_stack->rbegin(), inline_stack->rend(),
767 std::back_inserter(stack_trace),
768 [=](
const std::unique_ptr<CodeEntry>& ptr) {
769 return CodeEntryAndLineNumber{ptr.get(), no_line_info};
774 if (src_line_not_found) {
775 src_line = entry->GetSourceLine(pc_offset);
776 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
777 src_line = entry->line_number();
779 src_line_not_found =
false;
781 line_number = entry->GetSourceLine(pc_offset);
783 stack_trace.push_back({entry, line_number});
787 if (FLAG_prof_browser_mode) {
788 bool no_symbolized_entries =
true;
789 for (
auto e : stack_trace) {
790 if (e.code_entry !=
nullptr) {
791 no_symbolized_entries =
false;
796 if (no_symbolized_entries) {
797 stack_trace.push_back({EntryForVMState(sample.state), no_line_info});
801 profiles_->AddPathToCurrentProfiles(sample.timestamp, stack_trace, src_line,
802 sample.update_stats);
805 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
808 return CodeEntry::gc_entry();
812 case BYTECODE_COMPILER:
818 return CodeEntry::program_entry();
820 return CodeEntry::idle_entry();