Chromium Code Reviews| Index: src/api.cc |
| diff --git a/src/api.cc b/src/api.cc |
| index 86135fd83d2ab6460a8e96665a651f8a1af42f05..69dd01f6d6bd992988a8c01175006cccad9bc093 100644 |
| --- a/src/api.cc |
| +++ b/src/api.cc |
| @@ -36,6 +36,7 @@ |
| #include "src/debug/debug.h" |
| #include "src/deoptimizer.h" |
| #include "src/execution.h" |
| +#include "src/frames-inl.h" |
| #include "src/gdb-jit.h" |
| #include "src/global-handles.h" |
| #include "src/icu_util.h" |
| @@ -50,7 +51,7 @@ |
| #include "src/profiler/heap-profiler.h" |
| #include "src/profiler/heap-snapshot-generator-inl.h" |
| #include "src/profiler/profile-generator-inl.h" |
| -#include "src/profiler/tick-sample.h" |
| +#include "src/profiler/simulator-helper.h" |
| #include "src/property-descriptor.h" |
| #include "src/property-details.h" |
| #include "src/property.h" |
| @@ -7564,8 +7565,8 @@ bool Isolate::GetHeapCodeAndMetadataStatistics( |
| void Isolate::GetStackSample(const RegisterState& state, void** frames, |
| size_t frames_limit, SampleInfo* sample_info) { |
| - i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); |
| #if defined(USE_SIMULATOR) |
| + i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); |
| RegisterState regs; |
| regs.pc = state.pc; |
| regs.sp = state.sp; |
| @@ -7579,8 +7580,8 @@ void Isolate::GetStackSample(const RegisterState& state, void** frames, |
| #else |
| const RegisterState& regs = state; |
| #endif |
| - i::TickSample::GetStackSample(isolate, regs, i::TickSample::kSkipCEntryFrame, |
| - frames, frames_limit, sample_info); |
| + TickSample::GetStackSample(this, regs, TickSample::kSkipCEntryFrame, frames, |
| + frames_limit, sample_info); |
| } |
| size_t Isolate::NumberOfPhantomHandleResetsSinceLastCall() { |
| @@ -8259,6 +8260,164 @@ MaybeLocal<Array> Debug::GetInternalProperties(Isolate* v8_isolate, |
| return Utils::ToLocal(result); |
| } |
| +namespace { |
| + |
| +bool IsSamePage(i::byte* ptr1, i::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(i::Address address) { |
| + struct Pattern { |
| + int bytes_count; |
| + i::byte bytes[8]; |
| + int offsets[4]; |
| + }; |
| + i::byte* pc = reinterpret_cast<i::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* v8_isolate, |
|
alph
2016/06/28 22:09:01
Don't move that much logic into api.cc
Keep it in
lpy
2016/06/28 23:02:53
Done.
|
| + const RegisterState& regs, |
| + RecordCEntryFrame record_c_entry_frame, |
| + bool update_stats) { |
| + this->update_stats = update_stats; |
| + |
| + SampleInfo info; |
| + if (GetStackSample(v8_isolate, const_cast<RegisterState&>(regs), |
| + record_c_entry_frame, reinterpret_cast<void**>(&stack[0]), |
| + kMaxFramesCount, &info)) { |
| + state = info.vm_state; |
| + pc = regs.pc; |
| + frames_count = static_cast<unsigned>(info.frames_count); |
| + has_external_callback = info.external_callback_entry != nullptr; |
| + if (has_external_callback) { |
| + external_callback_entry = info.external_callback_entry; |
| + } else if (frames_count) { |
| + // 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(void*)); |
| + // Sample potential return address value for frameless invocation of |
| + // stubs (we'll figure out later, if this value makes sense). |
| + tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp)); |
| + } else { |
| + tos = nullptr; |
| + } |
| + } else { |
| + // It is executing JS but failed to collect a stack trace. |
| + // Mark the sample as spoiled. |
| + pc = nullptr; |
| + } |
| +} |
| + |
| +bool TickSample::GetStackSample(Isolate* v8_isolate, const RegisterState& regs, |
| + RecordCEntryFrame record_c_entry_frame, |
| + void** frames, size_t frames_limit, |
| + v8::SampleInfo* sample_info) { |
| + 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; |
| + if (sample_info->vm_state == GC) return true; |
| + |
| + i::Address js_entry_sp = isolate->js_entry_sp(); |
| + if (js_entry_sp == nullptr) return true; // Not executing JS now. |
| + DCHECK(regs.sp); |
| + |
| + if (regs.pc && IsNoFrameRegion(static_cast<i::Address>(regs.pc))) { |
| + // Can't collect stack. |
| + return false; |
| + } |
| + |
| + 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 |
| + // is not the top function. |
| + if (scope && scope->scope_address() < handler) { |
| + sample_info->external_callback_entry = |
| + *scope->callback_entrypoint_address(); |
| + } |
| + |
| + i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs.fp), |
| + reinterpret_cast<i::Address>(regs.sp), |
| + js_entry_sp); |
| + size_t i = 0; |
| + if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() && |
| + it.top_frame_type() == i::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. |
| + i::InterpretedFrame* frame = |
| + static_cast<i::InterpretedFrame*>(it.frame()); |
| + // Since the sampler can interrupt execution at any point the |
| + // bytecode_array might be garbage, so don't dereference it. |
| + i::Address bytecode_array = |
| + reinterpret_cast<i::Address>(frame->GetBytecodeArray()) - |
| + i::kHeapObjectTag; |
| + frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize + |
| + frame->GetBytecodeOffset(); |
| + } else { |
| + frames[i++] = it.frame()->pc(); |
| + } |
| + it.Advance(); |
| + } |
| + sample_info->frames_count = i; |
| + return true; |
| +} |
| Local<String> CpuProfileNode::GetFunctionName() const { |
| const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); |