Split TickSample and Sampler.
Since we are going to move Sampler as library, we creates tick-sample.[h|cc] for TickSample, in order to maintain legacy code. BUG=v8:4994 LOG=n Review-Url: https://codereview.chromium.org/1952393002 Cr-Commit-Position: refs/heads/master@{#36267}
This commit is contained in:
parent
39c7d3083e
commit
96aba388a1
2
BUILD.gn
2
BUILD.gn
@ -1350,6 +1350,8 @@ v8_source_set("v8_base") {
|
|||||||
"src/profiler/sampling-heap-profiler.h",
|
"src/profiler/sampling-heap-profiler.h",
|
||||||
"src/profiler/strings-storage.cc",
|
"src/profiler/strings-storage.cc",
|
||||||
"src/profiler/strings-storage.h",
|
"src/profiler/strings-storage.h",
|
||||||
|
"src/profiler/tick-sample.cc",
|
||||||
|
"src/profiler/tick-sample.h",
|
||||||
"src/profiler/unbound-queue-inl.h",
|
"src/profiler/unbound-queue-inl.h",
|
||||||
"src/profiler/unbound-queue.h",
|
"src/profiler/unbound-queue.h",
|
||||||
"src/property-descriptor.cc",
|
"src/property-descriptor.cc",
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
#include "src/profiler/heap-profiler.h"
|
#include "src/profiler/heap-profiler.h"
|
||||||
#include "src/profiler/heap-snapshot-generator-inl.h"
|
#include "src/profiler/heap-snapshot-generator-inl.h"
|
||||||
#include "src/profiler/profile-generator-inl.h"
|
#include "src/profiler/profile-generator-inl.h"
|
||||||
#include "src/profiler/sampler.h"
|
#include "src/profiler/tick-sample.h"
|
||||||
#include "src/property-descriptor.h"
|
#include "src/property-descriptor.h"
|
||||||
#include "src/property-details.h"
|
#include "src/property-details.h"
|
||||||
#include "src/property.h"
|
#include "src/property.h"
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/locked-queue.h"
|
#include "src/locked-queue.h"
|
||||||
#include "src/profiler/circular-queue.h"
|
#include "src/profiler/circular-queue.h"
|
||||||
#include "src/profiler/sampler.h"
|
#include "src/profiler/sampler.h"
|
||||||
|
#include "src/profiler/tick-sample.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "src/deoptimizer.h"
|
#include "src/deoptimizer.h"
|
||||||
#include "src/global-handles.h"
|
#include "src/global-handles.h"
|
||||||
#include "src/profiler/profile-generator-inl.h"
|
#include "src/profiler/profile-generator-inl.h"
|
||||||
#include "src/profiler/sampler.h"
|
#include "src/profiler/tick-sample.h"
|
||||||
#include "src/splay-tree-inl.h"
|
#include "src/splay-tree-inl.h"
|
||||||
#include "src/unicode.h"
|
#include "src/unicode.h"
|
||||||
|
|
||||||
|
@ -44,13 +44,10 @@
|
|||||||
|
|
||||||
#include "src/base/atomic-utils.h"
|
#include "src/base/atomic-utils.h"
|
||||||
#include "src/base/platform/platform.h"
|
#include "src/base/platform/platform.h"
|
||||||
#include "src/flags.h"
|
|
||||||
#include "src/frames-inl.h"
|
|
||||||
#include "src/log.h"
|
|
||||||
#include "src/profiler/cpu-profiler-inl.h"
|
#include "src/profiler/cpu-profiler-inl.h"
|
||||||
|
#include "src/profiler/tick-sample.h"
|
||||||
#include "src/simulator.h"
|
#include "src/simulator.h"
|
||||||
#include "src/v8threads.h"
|
#include "src/v8threads.h"
|
||||||
#include "src/vm-state-inl.h"
|
|
||||||
|
|
||||||
|
|
||||||
#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)
|
#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)
|
||||||
@ -175,68 +172,6 @@ class PlatformDataCommon : public Malloced {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef List<Sampler*> SamplerList;
|
typedef List<Sampler*> SamplerList;
|
||||||
|
|
||||||
#if defined(USE_SIGNALS)
|
#if defined(USE_SIGNALS)
|
||||||
@ -331,61 +266,6 @@ class Sampler::PlatformData : public PlatformDataCommon {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined(USE_SIMULATOR)
|
|
||||||
bool SimulatorHelper::FillRegisters(Isolate* isolate,
|
|
||||||
v8::RegisterState* state) {
|
|
||||||
Simulator *simulator = isolate->thread_local_top()->simulator_;
|
|
||||||
// Check if there is active simulator.
|
|
||||||
if (simulator == NULL) return false;
|
|
||||||
#if V8_TARGET_ARCH_ARM
|
|
||||||
if (!simulator->has_bad_pc()) {
|
|
||||||
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
|
||||||
}
|
|
||||||
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
|
||||||
state->fp = reinterpret_cast<Address>(simulator->get_register(
|
|
||||||
Simulator::r11));
|
|
||||||
#elif V8_TARGET_ARCH_ARM64
|
|
||||||
state->pc = reinterpret_cast<Address>(simulator->pc());
|
|
||||||
state->sp = reinterpret_cast<Address>(simulator->sp());
|
|
||||||
state->fp = reinterpret_cast<Address>(simulator->fp());
|
|
||||||
#elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
|
|
||||||
if (!simulator->has_bad_pc()) {
|
|
||||||
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
|
||||||
}
|
|
||||||
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
|
||||||
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
|
||||||
#elif V8_TARGET_ARCH_PPC
|
|
||||||
if (!simulator->has_bad_pc()) {
|
|
||||||
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
|
||||||
}
|
|
||||||
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
|
||||||
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
|
||||||
#elif V8_TARGET_ARCH_S390
|
|
||||||
if (!simulator->has_bad_pc()) {
|
|
||||||
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
|
||||||
}
|
|
||||||
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
|
||||||
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
|
||||||
#endif
|
|
||||||
if (state->sp == 0 || state->fp == 0) {
|
|
||||||
// It possible that the simulator is interrupted while it is updating
|
|
||||||
// the sp or fp register. ARM64 simulator does this in two steps:
|
|
||||||
// first setting it to zero and then setting it to the new value.
|
|
||||||
// Bailout if sp/fp doesn't contain the new value.
|
|
||||||
//
|
|
||||||
// FIXME: The above doesn't really solve the issue.
|
|
||||||
// If a 64-bit target is executed on a 32-bit host even the final
|
|
||||||
// write is non-atomic, so it might obtain a half of the result.
|
|
||||||
// Moreover as long as the register set code uses memcpy (as of now),
|
|
||||||
// it is not guaranteed to be atomic even when both host and target
|
|
||||||
// are of same bitness.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif // USE_SIMULATOR
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(USE_SIGNALS)
|
#if defined(USE_SIGNALS)
|
||||||
|
|
||||||
class SignalHandler : public AllStatic {
|
class SignalHandler : public AllStatic {
|
||||||
@ -811,103 +691,6 @@ void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
|
|||||||
#endif // USE_SIGNALs
|
#endif // USE_SIGNALs
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Sampler::SetUp() {
|
void Sampler::SetUp() {
|
||||||
#if defined(USE_SIGNALS)
|
#if defined(USE_SIGNALS)
|
||||||
SignalHandler::SetUp();
|
SignalHandler::SetUp();
|
||||||
|
@ -8,14 +8,13 @@
|
|||||||
#include "include/v8.h"
|
#include "include/v8.h"
|
||||||
|
|
||||||
#include "src/base/atomicops.h"
|
#include "src/base/atomicops.h"
|
||||||
#include "src/base/platform/time.h"
|
#include "src/base/macros.h"
|
||||||
#include "src/frames.h"
|
|
||||||
#include "src/globals.h"
|
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class Isolate;
|
class Isolate;
|
||||||
|
struct TickSample;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Sampler
|
// Sampler
|
||||||
@ -24,43 +23,6 @@ class Isolate;
|
|||||||
// (if used for profiling) the program counter and stack pointer for
|
// (if used for profiling) the program counter and stack pointer for
|
||||||
// the thread that created it.
|
// the thread that created it.
|
||||||
|
|
||||||
// TickSample captures the information collected for each sample.
|
|
||||||
struct TickSample {
|
|
||||||
// Internal profiling (with --prof + tools/$OS-tick-processor) wants to
|
|
||||||
// include the runtime function we're calling. Externally exposed tick
|
|
||||||
// samples don't care.
|
|
||||||
enum RecordCEntryFrame { kIncludeCEntryFrame, kSkipCEntryFrame };
|
|
||||||
|
|
||||||
TickSample()
|
|
||||||
: state(OTHER),
|
|
||||||
pc(NULL),
|
|
||||||
external_callback_entry(NULL),
|
|
||||||
frames_count(0),
|
|
||||||
has_external_callback(false),
|
|
||||||
update_stats(true),
|
|
||||||
top_frame_type(StackFrame::NONE) {}
|
|
||||||
void Init(Isolate* isolate, const v8::RegisterState& state,
|
|
||||||
RecordCEntryFrame record_c_entry_frame, bool update_stats);
|
|
||||||
static void GetStackSample(Isolate* isolate, const v8::RegisterState& state,
|
|
||||||
RecordCEntryFrame record_c_entry_frame,
|
|
||||||
void** frames, size_t frames_limit,
|
|
||||||
v8::SampleInfo* sample_info);
|
|
||||||
StateTag state; // The state of the VM.
|
|
||||||
Address pc; // Instruction pointer.
|
|
||||||
union {
|
|
||||||
Address tos; // Top stack value (*sp).
|
|
||||||
Address external_callback_entry;
|
|
||||||
};
|
|
||||||
static const unsigned kMaxFramesCountLog2 = 8;
|
|
||||||
static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1;
|
|
||||||
Address stack[kMaxFramesCount]; // Call stack.
|
|
||||||
base::TimeTicks timestamp;
|
|
||||||
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
|
|
||||||
bool has_external_callback : 1;
|
|
||||||
bool update_stats : 1; // Whether the sample should update aggregated stats.
|
|
||||||
StackFrame::Type top_frame_type : 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sampler {
|
class Sampler {
|
||||||
public:
|
public:
|
||||||
// Initializes the Sampler support. Called once at VM startup.
|
// Initializes the Sampler support. Called once at VM startup.
|
||||||
@ -140,17 +102,6 @@ class Sampler {
|
|||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if defined(USE_SIMULATOR)
|
|
||||||
class SimulatorHelper : AllStatic {
|
|
||||||
public:
|
|
||||||
// Returns true if register values were successfully retrieved
|
|
||||||
// from the simulator, otherwise returns false.
|
|
||||||
static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
|
|
||||||
};
|
|
||||||
#endif // USE_SIMULATOR
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
233
src/profiler/tick-sample.cc
Normal file
233
src/profiler/tick-sample.cc
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(USE_SIMULATOR)
|
||||||
|
bool SimulatorHelper::FillRegisters(Isolate* isolate,
|
||||||
|
v8::RegisterState* state) {
|
||||||
|
Simulator *simulator = isolate->thread_local_top()->simulator_;
|
||||||
|
// Check if there is active simulator.
|
||||||
|
if (simulator == NULL) return false;
|
||||||
|
#if V8_TARGET_ARCH_ARM
|
||||||
|
if (!simulator->has_bad_pc()) {
|
||||||
|
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
||||||
|
}
|
||||||
|
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
||||||
|
state->fp = reinterpret_cast<Address>(simulator->get_register(
|
||||||
|
Simulator::r11));
|
||||||
|
#elif V8_TARGET_ARCH_ARM64
|
||||||
|
state->pc = reinterpret_cast<Address>(simulator->pc());
|
||||||
|
state->sp = reinterpret_cast<Address>(simulator->sp());
|
||||||
|
state->fp = reinterpret_cast<Address>(simulator->fp());
|
||||||
|
#elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
|
||||||
|
if (!simulator->has_bad_pc()) {
|
||||||
|
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
||||||
|
}
|
||||||
|
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
||||||
|
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
||||||
|
#elif V8_TARGET_ARCH_PPC
|
||||||
|
if (!simulator->has_bad_pc()) {
|
||||||
|
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
||||||
|
}
|
||||||
|
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
||||||
|
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
||||||
|
#elif V8_TARGET_ARCH_S390
|
||||||
|
if (!simulator->has_bad_pc()) {
|
||||||
|
state->pc = reinterpret_cast<Address>(simulator->get_pc());
|
||||||
|
}
|
||||||
|
state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
|
||||||
|
state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
|
||||||
|
#endif
|
||||||
|
if (state->sp == 0 || state->fp == 0) {
|
||||||
|
// It possible that the simulator is interrupted while it is updating
|
||||||
|
// the sp or fp register. ARM64 simulator does this in two steps:
|
||||||
|
// first setting it to zero and then setting it to the new value.
|
||||||
|
// Bailout if sp/fp doesn't contain the new value.
|
||||||
|
//
|
||||||
|
// FIXME: The above doesn't really solve the issue.
|
||||||
|
// If a 64-bit target is executed on a 32-bit host even the final
|
||||||
|
// write is non-atomic, so it might obtain a half of the result.
|
||||||
|
// Moreover as long as the register set code uses memcpy (as of now),
|
||||||
|
// it is not guaranteed to be atomic even when both host and target
|
||||||
|
// are of same bitness.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // USE_SIMULATOR
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
76
src/profiler/tick-sample.h
Normal file
76
src/profiler/tick-sample.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef V8_PROFILER_TICK_SAMPLE_H_
|
||||||
|
#define V8_PROFILER_TICK_SAMPLE_H_
|
||||||
|
|
||||||
|
#include "include/v8.h"
|
||||||
|
|
||||||
|
#include "src/base/platform/time.h"
|
||||||
|
#include "src/frames.h"
|
||||||
|
#include "src/globals.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class Isolate;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Sampler
|
||||||
|
//
|
||||||
|
// A sampler periodically samples the state of the VM and optionally
|
||||||
|
// (if used for profiling) the program counter and stack pointer for
|
||||||
|
// the thread that created it.
|
||||||
|
|
||||||
|
// TickSample captures the information collected for each sample.
|
||||||
|
struct TickSample {
|
||||||
|
// Internal profiling (with --prof + tools/$OS-tick-processor) wants to
|
||||||
|
// include the runtime function we're calling. Externally exposed tick
|
||||||
|
// samples don't care.
|
||||||
|
enum RecordCEntryFrame { kIncludeCEntryFrame, kSkipCEntryFrame };
|
||||||
|
|
||||||
|
TickSample()
|
||||||
|
: state(OTHER),
|
||||||
|
pc(NULL),
|
||||||
|
external_callback_entry(NULL),
|
||||||
|
frames_count(0),
|
||||||
|
has_external_callback(false),
|
||||||
|
update_stats(true),
|
||||||
|
top_frame_type(StackFrame::NONE) {}
|
||||||
|
void Init(Isolate* isolate, const v8::RegisterState& state,
|
||||||
|
RecordCEntryFrame record_c_entry_frame, bool update_stats);
|
||||||
|
static void GetStackSample(Isolate* isolate, const v8::RegisterState& state,
|
||||||
|
RecordCEntryFrame record_c_entry_frame,
|
||||||
|
void** frames, size_t frames_limit,
|
||||||
|
v8::SampleInfo* sample_info);
|
||||||
|
StateTag state; // The state of the VM.
|
||||||
|
Address pc; // Instruction pointer.
|
||||||
|
union {
|
||||||
|
Address tos; // Top stack value (*sp).
|
||||||
|
Address external_callback_entry;
|
||||||
|
};
|
||||||
|
static const unsigned kMaxFramesCountLog2 = 8;
|
||||||
|
static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1;
|
||||||
|
Address stack[kMaxFramesCount]; // Call stack.
|
||||||
|
base::TimeTicks timestamp;
|
||||||
|
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
|
||||||
|
bool has_external_callback : 1;
|
||||||
|
bool update_stats : 1; // Whether the sample should update aggregated stats.
|
||||||
|
StackFrame::Type top_frame_type : 5;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(USE_SIMULATOR)
|
||||||
|
class SimulatorHelper {
|
||||||
|
public:
|
||||||
|
// Returns true if register values were successfully retrieved
|
||||||
|
// from the simulator, otherwise returns false.
|
||||||
|
static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
|
||||||
|
};
|
||||||
|
#endif // USE_SIMULATOR
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // V8_PROFILER_TICK_SAMPLE_H_
|
@ -1005,6 +1005,8 @@
|
|||||||
'profiler/sampling-heap-profiler.h',
|
'profiler/sampling-heap-profiler.h',
|
||||||
'profiler/strings-storage.cc',
|
'profiler/strings-storage.cc',
|
||||||
'profiler/strings-storage.h',
|
'profiler/strings-storage.h',
|
||||||
|
'profiler/tick-sample.cc',
|
||||||
|
'profiler/tick-sample.h',
|
||||||
'profiler/unbound-queue-inl.h',
|
'profiler/unbound-queue-inl.h',
|
||||||
'profiler/unbound-queue.h',
|
'profiler/unbound-queue.h',
|
||||||
'property-descriptor.cc',
|
'property-descriptor.cc',
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "src/disassembler.h"
|
#include "src/disassembler.h"
|
||||||
#include "src/isolate.h"
|
#include "src/isolate.h"
|
||||||
#include "src/log.h"
|
#include "src/log.h"
|
||||||
#include "src/profiler/sampler.h"
|
#include "src/profiler/tick-sample.h"
|
||||||
#include "src/vm-state-inl.h"
|
#include "src/vm-state-inl.h"
|
||||||
#include "test/cctest/cctest.h"
|
#include "test/cctest/cctest.h"
|
||||||
#include "test/cctest/trace-extension.h"
|
#include "test/cctest/trace-extension.h"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "test/cctest/trace-extension.h"
|
#include "test/cctest/trace-extension.h"
|
||||||
|
|
||||||
#include "src/profiler/sampler.h"
|
#include "src/profiler/tick-sample.h"
|
||||||
#include "src/vm-state-inl.h"
|
#include "src/vm-state-inl.h"
|
||||||
#include "test/cctest/cctest.h"
|
#include "test/cctest/cctest.h"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user