diff options
Diffstat (limited to 'chromium/v8/src/profiler/tick-sample.cc')
-rw-r--r-- | chromium/v8/src/profiler/tick-sample.cc | 115 |
1 files changed, 87 insertions, 28 deletions
diff --git a/chromium/v8/src/profiler/tick-sample.cc b/chromium/v8/src/profiler/tick-sample.cc index b3ea07db34f..5c2f2d63ce3 100644 --- a/chromium/v8/src/profiler/tick-sample.cc +++ b/chromium/v8/src/profiler/tick-sample.cc @@ -16,6 +16,7 @@ #include "src/sanitizer/msan.h" namespace v8 { +namespace internal { namespace { bool IsSamePage(i::Address ptr1, i::Address ptr2) { @@ -78,11 +79,6 @@ bool IsNoFrameRegion(i::Address address) { return false; } -} // namespace - -namespace internal { -namespace { - #if defined(USE_SIMULATOR) class SimulatorHelper { public: @@ -147,22 +143,56 @@ bool SimulatorHelper::FillRegisters(Isolate* isolate, } #endif // USE_SIMULATOR +// Attempts to safely dereference the address of a native context at a given +// context's address. Returns kNullAddress on failure, in the event that the +// context is in an inconsistent state. +Address ScrapeNativeContextAddress(Heap* heap, Address context_address) { + DCHECK_EQ(heap->gc_state(), Heap::NOT_IN_GC); + + if (!HAS_STRONG_HEAP_OBJECT_TAG(context_address)) return kNullAddress; + + if (heap->memory_allocator()->IsOutsideAllocatedSpace(context_address)) + return kNullAddress; + + // Note that once a native context has been assigned to a context, the slot + // is no longer mutated except during pointer updates / evictions. Since + // pointer updates exclusively occur on the main thread, and we don't record + // TickSamples when the main thread's VM state is GC, the only other + // situation where the address here would be invalid is if it's being + // reassigned -- which isn't possible. + int native_context_offset = + i::Context::SlotOffset(i::Context::NATIVE_CONTEXT_INDEX); + i::Address native_context_slot_address = + context_address + native_context_offset; + + // By the prior hypothesis, the indirect native context address should always + // be valid. + if (heap->memory_allocator()->IsOutsideAllocatedSpace( + native_context_slot_address)) { + DCHECK(false); + return kNullAddress; + } + + i::ObjectSlot native_context_slot(native_context_slot_address); + i::Object native_context = native_context_slot.Relaxed_Load(); + + return native_context.ptr(); +} + } // namespace -} // namespace internal -// -// StackTracer implementation -// DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, const RegisterState& reg_state, RecordCEntryFrame record_c_entry_frame, bool update_stats, - bool use_simulator_reg_state) { + bool use_simulator_reg_state, + base::TimeDelta sampling_interval) { this->update_stats = update_stats; SampleInfo info; RegisterState regs = reg_state; if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack, - kMaxFramesCount, &info, use_simulator_reg_state)) { + kMaxFramesCount, &info, use_simulator_reg_state, + contexts)) { // It is executing JS but failed to collect a stack trace. // Mark the sample as spoiled. pc = nullptr; @@ -173,6 +203,7 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, pc = regs.pc; frames_count = static_cast<unsigned>(info.frames_count); has_external_callback = info.external_callback_entry != nullptr; + top_context = info.top_context; if (has_external_callback) { external_callback_entry = info.external_callback_entry; } else if (frames_count) { @@ -191,17 +222,20 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, } else { tos = nullptr; } + this->sampling_interval = sampling_interval; + timestamp = base::TimeTicks::HighResolutionNow(); } bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, RecordCEntryFrame record_c_entry_frame, void** frames, size_t frames_limit, v8::SampleInfo* sample_info, - bool use_simulator_reg_state) { + bool use_simulator_reg_state, void** contexts) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); sample_info->frames_count = 0; sample_info->vm_state = isolate->current_vm_state(); sample_info->external_callback_entry = nullptr; + sample_info->top_context = nullptr; if (sample_info->vm_state == GC) return true; i::Address js_entry_sp = isolate->js_entry_sp(); @@ -229,7 +263,7 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, i::ExternalCallbackScope* scope = isolate->external_callback_scope(); i::Address handler = i::Isolate::handler(isolate->thread_local_top()); // If there is a handler on top of the external callback scope then - // we have already entrered JavaScript again and the external callback + // we have already entered JavaScript again and the external callback // is not the top function. if (scope && scope->scope_address() < handler) { i::Address* external_callback_entry_ptr = @@ -245,23 +279,62 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, reinterpret_cast<i::Address>(regs->sp), reinterpret_cast<i::Address>(regs->lr), js_entry_sp); + + i::Address top_context_address = it.top_context_address(); + if (top_context_address != i::kNullAddress) { + sample_info->top_context = reinterpret_cast<void*>( + i::ScrapeNativeContextAddress(isolate->heap(), top_context_address)); + } else { + sample_info->top_context = nullptr; + } + if (it.done()) return true; size_t i = 0; if (record_c_entry_frame == kIncludeCEntryFrame && (it.top_frame_type() == internal::StackFrame::EXIT || it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) { - frames[i++] = reinterpret_cast<void*>(isolate->c_function()); + frames[i] = reinterpret_cast<void*>(isolate->c_function()); + if (contexts) contexts[i] = sample_info->top_context; + i++; } + + // If we couldn't get a context address from the top frame due to execution + // being in a callback, borrow it from the next context on the stack. + bool borrows_top_context = it.top_frame_type() == i::StackFrame::EXIT || + it.top_frame_type() == i::StackFrame::BUILTIN_EXIT; + i::RuntimeCallTimer* timer = isolate->counters()->runtime_call_stats()->current_timer(); for (; !it.done() && i < frames_limit; it.Advance()) { while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() && i < frames_limit) { + if (contexts) contexts[i] = nullptr; frames[i++] = reinterpret_cast<void*>(timer->counter()); timer = timer->parent(); } if (i == frames_limit) break; + + // Attempt to read the native context associated with the frame from the + // heap for standard frames. + if (it.frame()->is_standard() && (contexts || borrows_top_context)) { + i::Address context_address = base::Memory<i::Address>( + it.frame()->fp() + i::StandardFrameConstants::kContextOffset); + i::Address native_context_address = + i::ScrapeNativeContextAddress(isolate->heap(), context_address); + if (contexts) + contexts[i] = reinterpret_cast<void*>(native_context_address); + + if (borrows_top_context) { + DCHECK(!sample_info->top_context); + sample_info->top_context = + reinterpret_cast<void*>(native_context_address); + } + } else if (contexts) { + contexts[i] = nullptr; + } + borrows_top_context = false; + if (it.frame()->is_interpreted()) { // For interpreted frames use the bytecode array pointer as the pc. i::InterpretedFrame* frame = @@ -290,20 +363,6 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, return true; } -namespace internal { - -void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, - RecordCEntryFrame record_c_entry_frame, bool update_stats, - bool use_simulator_reg_state, - base::TimeDelta sampling_interval) { - v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, - record_c_entry_frame, update_stats, - use_simulator_reg_state); - this->sampling_interval = sampling_interval; - if (pc == nullptr) return; - timestamp = base::TimeTicks::HighResolutionNow(); -} - void TickSample::print() const { PrintF("TickSample: at %p\n", this); PrintF(" - state: %s\n", StateToString(state)); |