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); |