#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:
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);
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);
}
stack_trace[topmost_frame_index].line_and_column = entry_pos;
} else {
stack_trace.push_back({entry, entry_pos});
}
}
}
Symbolizer::SymbolizedSample Symbolizer::SymbolizeTickSample(
const TickSample& sample) {
ProfileStackTrace stack_trace;
stack_trace.reserve(sample.frames_count + 3);
CodeEntryAndPosition first_js_frame{nullptr, {}};
if (sample.pc != nullptr) {
if (sample.has_external_callback && IsExternal(sample.state)) {
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 (!pc_entry && !sample.has_external_callback) {
attributed_pc = reinterpret_cast<Address>(sample.tos);
pc_entry = FindEntry(attributed_pc, &pc_entry_instruction_start);
}
if (pc_entry) {
int pc_offset =
static_cast<int>(attributed_pc - pc_entry_instruction_start);
UnwindInlineStack(pc_entry, pc_offset, stack_trace, first_js_frame);
if (pc_entry->builtin() == Builtin::kFunctionPrototypeApply ||
pc_entry->builtin() == Builtin::kFunctionPrototypeCall) {
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);
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_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{}});
}
}
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};
}
}
}