/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/profiler/symbolizer.cc
190 строк
7 KB
Michaël Zasso
deps: update V8 to 14.1.146.11
04 окт 2025, 19:47
Не верифицирован
04 окт 2025, 19:47
7772a2d
Код
Авторство
О чём код?
// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/profiler/symbolizer.h" #include "src/execution/vm-state.h" #include "src/profiler/profile-generator.h" #include "src/profiler/profiler-stats.h" #include "src/profiler/tick-sample.h" namespace v8 { namespace internal { Symbolizer::Symbolizer(InstructionStreamMap* instruction_stream_map) : code_map_(instruction_stream_map) {} CodeEntry* Symbolizer::FindEntry(Address address, Address* out_instruction_start) { return code_map_->FindEntry(address, out_instruction_start); } namespace { CodeEntry* EntryForVMState(StateTag tag) { switch (tag) { case GC: return CodeEntry::gc_entry(); case JS: case PARSER: case COMPILER: case BYTECODE_COMPILER: case ATOMICS_WAIT: // DOM events handlers are reported as OTHER / EXTERNAL entries. // To avoid confusing people, let's put all these entries into // one bucket. case OTHER: case IDLE_EXTERNAL: case EXTERNAL: case LOGGING: return CodeEntry::program_entry(); case IDLE: return CodeEntry::idle_entry(); } } void UnwindInlineStack(CodeEntry* entry, int pc_offset, ProfileStackTrace& stack_trace, CodeEntryAndPosition& first_js_frame) { LineAndColumn entry_pos = entry->GetSourcePosition(pc_offset); const std::vector<CodeEntryAndPosition>* inline_stack = entry->GetInlineStack(pc_offset); // Update the first JS frame if we haven't recorded a JS entry yet. if (first_js_frame.code_entry == nullptr) { first_js_frame.code_entry = entry; first_js_frame.line_and_column = entry_pos; } if (inline_stack) { DCHECK(!inline_stack->empty()); size_t topmost_frame_index = stack_trace.size(); for (auto inline_stack_entry : *inline_stack) { stack_trace.push_back(inline_stack_entry); } // This is a bit of a messy hack. The position for the most-inlined // frame (the function at the end of the chain of function calls) has // the wrong position in inline_stack. The actual position in // this function is stored in the SourcePositionTable in entry. We fix // up the position for the most-inlined frame here. // TODO(petermarshall): Remove this and use a tree with a node per // inlining_id. stack_trace[topmost_frame_index].line_and_column = entry_pos; } else { stack_trace.push_back({entry, entry_pos}); } } } // namespace Symbolizer::SymbolizedSample Symbolizer::SymbolizeTickSample( const TickSample& sample) { ProfileStackTrace stack_trace; // Conservatively reserve space for stack frames + pc + function + vm-state. // There could in fact be more of them because of inlined entries. stack_trace.reserve(sample.frames_count + 3); // The ProfileNode knows nothing about all versions of generated code for // the same JS function. The position information associated with // the latest version of generated code is used to find a source position // for a JS function. Then, the detected source position is passed to // ProfileNode to increase the tick count for this source position. CodeEntryAndPosition first_js_frame{nullptr, {}}; if (sample.pc != nullptr) { if (sample.has_external_callback && IsExternal(sample.state)) { // Don't use PC when in external callback code, as it can point // inside a callback's code, and we will erroneously report // that a callback calls itself. stack_trace.push_back( {FindEntry(reinterpret_cast<Address>(sample.external_callback_entry)), LineAndColumn{}}); } else { Address attributed_pc = reinterpret_cast<Address>(sample.pc); Address pc_entry_instruction_start = kNullAddress; CodeEntry* pc_entry = FindEntry(attributed_pc, &pc_entry_instruction_start); // If there is no pc_entry, we're likely in native code. Find out if the // top of the stack (the return address) was pointing inside a JS // function, meaning that we have encountered a frameless invocation. if (!pc_entry && !sample.has_external_callback) { attributed_pc = reinterpret_cast<Address>(sample.tos); pc_entry = FindEntry(attributed_pc, &pc_entry_instruction_start); } // If pc is in the function code before it set up stack frame or after the // frame was destroyed, StackFrameIteratorForProfiler incorrectly thinks // that ebp contains the return address of the current function and skips // the caller's frame. Check for this case and just skip such samples. if (pc_entry) { int pc_offset = static_cast<int>(attributed_pc - pc_entry_instruction_start); // TODO(petermarshall): pc_offset can still be negative in some cases. UnwindInlineStack(pc_entry, pc_offset, stack_trace, first_js_frame); if (pc_entry->builtin() == Builtin::kFunctionPrototypeApply || pc_entry->builtin() == Builtin::kFunctionPrototypeCall) { // When current function is either the Function.prototype.apply or the // Function.prototype.call builtin the top frame is either frame of // the calling JS function or internal frame. // In the latter case we know the caller for sure but in the // former case we don't so we simply replace the frame with // 'unresolved' entry. if (!sample.has_external_callback) { ProfilerStats::Instance()->AddReason( ProfilerStats::Reason::kInCallOrApply); stack_trace.push_back( {CodeEntry::unresolved_entry(), LineAndColumn{}}); } } } } for (unsigned i = 0; i < sample.frames_count; ++i) { Address stack_pos = reinterpret_cast<Address>(sample.stack[i]); Address instruction_start = kNullAddress; CodeEntry* entry = FindEntry(stack_pos, &instruction_start); if (entry) { int pc_offset = static_cast<int>(stack_pos - instruction_start); // TODO(petermarshall): pc_offset can still be negative in some cases. UnwindInlineStack(entry, pc_offset, stack_trace, first_js_frame); } else { stack_trace.push_back({entry, {}}); } } } if (v8_flags.prof_browser_mode) { bool no_symbolized_entries = true; for (auto e : stack_trace) { if (e.code_entry != nullptr) { no_symbolized_entries = false; break; } } // If no frames were symbolized, put the VM state entry in. if (no_symbolized_entries) { if (sample.pc == nullptr) { ProfilerStats::Instance()->AddReason(ProfilerStats::Reason::kNullPC); } else { ProfilerStats::Instance()->AddReason( ProfilerStats::Reason::kNoSymbolizedFrames); } stack_trace.push_back({EntryForVMState(sample.state), LineAndColumn{}}); } } // Resolve the source position for the first JS frame. LineAndColumn src_pos = first_js_frame.line_and_column; if (first_js_frame.code_entry != nullptr && src_pos.line == v8::CpuProfileNode::kNoLineNumberInfo) { src_pos = first_js_frame.code_entry->line_and_column(); } return SymbolizedSample{stack_trace, src_pos}; } } // namespace internal } // namespace v8