| Index: src/profiler/tick-sample.cc
|
| diff --git a/src/profiler/tick-sample.cc b/src/profiler/tick-sample.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2dc50a4aa12092fa29bd93f9a89671fd6577e288
|
| --- /dev/null
|
| +++ b/src/profiler/tick-sample.cc
|
| @@ -0,0 +1,179 @@
|
| +// Copyright 2013 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/tick-sample.h"
|
| +
|
| +#include "src/frames-inl.h"
|
| +#include "src/vm-state-inl.h"
|
| +
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +namespace {
|
| +
|
| +bool IsSamePage(byte* ptr1, byte* ptr2) {
|
| + const uint32_t kPageSize = 4096;
|
| + uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1);
|
| + return (reinterpret_cast<uintptr_t>(ptr1) & mask) ==
|
| + (reinterpret_cast<uintptr_t>(ptr2) & mask);
|
| +}
|
| +
|
| +
|
| +// Check if the code at specified address could potentially be a
|
| +// frame setup code.
|
| +bool IsNoFrameRegion(Address address) {
|
| + struct Pattern {
|
| + int bytes_count;
|
| + byte bytes[8];
|
| + int offsets[4];
|
| + };
|
| + byte* pc = reinterpret_cast<byte*>(address);
|
| + static Pattern patterns[] = {
|
| +#if V8_HOST_ARCH_IA32
|
| + // push %ebp
|
| + // mov %esp,%ebp
|
| + {3, {0x55, 0x89, 0xe5}, {0, 1, -1}},
|
| + // pop %ebp
|
| + // ret N
|
| + {2, {0x5d, 0xc2}, {0, 1, -1}},
|
| + // pop %ebp
|
| + // ret
|
| + {2, {0x5d, 0xc3}, {0, 1, -1}},
|
| +#elif V8_HOST_ARCH_X64
|
| + // pushq %rbp
|
| + // movq %rsp,%rbp
|
| + {4, {0x55, 0x48, 0x89, 0xe5}, {0, 1, -1}},
|
| + // popq %rbp
|
| + // ret N
|
| + {2, {0x5d, 0xc2}, {0, 1, -1}},
|
| + // popq %rbp
|
| + // ret
|
| + {2, {0x5d, 0xc3}, {0, 1, -1}},
|
| +#endif
|
| + {0, {}, {}}
|
| + };
|
| + for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
|
| + for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
|
| + int offset = *offset_ptr;
|
| + if (!offset || IsSamePage(pc, pc - offset)) {
|
| + MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
|
| + if (!memcmp(pc - offset, pattern->bytes, pattern->bytes_count))
|
| + return true;
|
| + } else {
|
| + // It is not safe to examine bytes on another page as it might not be
|
| + // allocated thus causing a SEGFAULT.
|
| + // Check the pattern part that's on the same page and
|
| + // pessimistically assume it could be the entire pattern match.
|
| + MSAN_MEMORY_IS_INITIALIZED(pc, pattern->bytes_count - offset);
|
| + if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset))
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +//
|
| +// StackTracer implementation
|
| +//
|
| +DISABLE_ASAN void TickSample::Init(Isolate* isolate,
|
| + const v8::RegisterState& regs,
|
| + RecordCEntryFrame record_c_entry_frame,
|
| + bool update_stats) {
|
| + timestamp = base::TimeTicks::HighResolutionNow();
|
| + pc = reinterpret_cast<Address>(regs.pc);
|
| + state = isolate->current_vm_state();
|
| + this->update_stats = update_stats;
|
| +
|
| + // Avoid collecting traces while doing GC.
|
| + if (state == GC) return;
|
| +
|
| + Address js_entry_sp = isolate->js_entry_sp();
|
| + if (js_entry_sp == 0) return; // Not executing JS now.
|
| +
|
| + if (pc && IsNoFrameRegion(pc)) {
|
| + // Can't collect stack. Mark the sample as spoiled.
|
| + timestamp = base::TimeTicks();
|
| + pc = 0;
|
| + return;
|
| + }
|
| +
|
| + ExternalCallbackScope* scope = isolate->external_callback_scope();
|
| + Address handler = 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
|
| + // is not the top function.
|
| + if (scope && scope->scope_address() < handler) {
|
| + external_callback_entry = *scope->callback_entrypoint_address();
|
| + has_external_callback = true;
|
| + } else {
|
| + // sp register may point at an arbitrary place in memory, make
|
| + // sure MSAN doesn't complain about it.
|
| + MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(Address));
|
| + // Sample potential return address value for frameless invocation of
|
| + // stubs (we'll figure out later, if this value makes sense).
|
| + tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp));
|
| + has_external_callback = false;
|
| + }
|
| +
|
| + SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
|
| + reinterpret_cast<Address>(regs.sp), js_entry_sp);
|
| + top_frame_type = it.top_frame_type();
|
| +
|
| + SampleInfo info;
|
| + GetStackSample(isolate, regs, record_c_entry_frame,
|
| + reinterpret_cast<void**>(&stack[0]), kMaxFramesCount, &info);
|
| + frames_count = static_cast<unsigned>(info.frames_count);
|
| + if (!frames_count) {
|
| + // It is executing JS but failed to collect a stack trace.
|
| + // Mark the sample as spoiled.
|
| + timestamp = base::TimeTicks();
|
| + pc = 0;
|
| + }
|
| +}
|
| +
|
| +
|
| +void TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
|
| + RecordCEntryFrame record_c_entry_frame,
|
| + void** frames, size_t frames_limit,
|
| + v8::SampleInfo* sample_info) {
|
| + sample_info->frames_count = 0;
|
| + sample_info->vm_state = isolate->current_vm_state();
|
| + if (sample_info->vm_state == GC) return;
|
| +
|
| + Address js_entry_sp = isolate->js_entry_sp();
|
| + if (js_entry_sp == 0) return; // Not executing JS now.
|
| +
|
| + SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
|
| + reinterpret_cast<Address>(regs.sp), js_entry_sp);
|
| + size_t i = 0;
|
| + if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() &&
|
| + it.top_frame_type() == StackFrame::EXIT) {
|
| + frames[i++] = isolate->c_function();
|
| + }
|
| + while (!it.done() && i < frames_limit) {
|
| + if (it.frame()->is_interpreted()) {
|
| + // For interpreted frames use the bytecode array pointer as the pc.
|
| + InterpretedFrame* frame = static_cast<InterpretedFrame*>(it.frame());
|
| + // Since the sampler can interrupt execution at any point the
|
| + // bytecode_array might be garbage, so don't dereference it.
|
| + Address bytecode_array =
|
| + reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag;
|
| + frames[i++] = bytecode_array + BytecodeArray::kHeaderSize +
|
| + frame->GetBytecodeOffset();
|
| + } else {
|
| + frames[i++] = it.frame()->pc();
|
| + }
|
| + it.Advance();
|
| + }
|
| + sample_info->frames_count = i;
|
| +}
|
| +
|
| +
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|