Move SimulatorHelper into V8 out of profiler clients.
This patch is based on alph's CL https://codereview.chromium.org/2128613004/. This patch makes GetStackSample propogate the register state when using simulator helper, and adds argument to avoid using register state from simulator when pass the native register state. BUG=v8:4789 LOG=N Review-Url: https://codereview.chromium.org/2189513002 Cr-Commit-Position: refs/heads/master@{#38554}
This commit is contained in:
parent
0359e1f63e
commit
c72f637c73
@ -60,12 +60,47 @@ struct TickSample {
|
||||
frames_count(0),
|
||||
has_external_callback(false),
|
||||
update_stats(true) {}
|
||||
|
||||
/**
|
||||
* Initialize a tick sample from the isolate.
|
||||
* \param isolate The isolate.
|
||||
* \param state Execution state.
|
||||
* \param record_c_entry_frame Include or skip the runtime function.
|
||||
* \param update_stats Whether update the sample to the aggregated stats.
|
||||
* \param use_simulator_reg_state When set to true and V8 is running under a
|
||||
* simulator, the method will use the simulator
|
||||
* register state rather than the one provided
|
||||
* with |state| argument. Otherwise the method
|
||||
* will use provided register |state| as is.
|
||||
*/
|
||||
void Init(Isolate* isolate, const v8::RegisterState& state,
|
||||
RecordCEntryFrame record_c_entry_frame, bool update_stats);
|
||||
static bool GetStackSample(Isolate* isolate, const v8::RegisterState& state,
|
||||
RecordCEntryFrame record_c_entry_frame, bool update_stats,
|
||||
bool use_simulator_reg_state = true);
|
||||
/**
|
||||
* Get a call stack sample from the isolate.
|
||||
* \param isolate The isolate.
|
||||
* \param state Register state.
|
||||
* \param record_c_entry_frame Include or skip the runtime function.
|
||||
* \param frames Caller allocated buffer to store stack frames.
|
||||
* \param frames_limit Maximum number of frames to capture. The buffer must
|
||||
* be large enough to hold the number of frames.
|
||||
* \param sample_info The sample info is filled up by the function
|
||||
* provides number of actual captured stack frames and
|
||||
* the current VM state.
|
||||
* \param use_simulator_reg_state When set to true and V8 is running under a
|
||||
* simulator, the method will use the simulator
|
||||
* register state rather than the one provided
|
||||
* with |state| argument. Otherwise the method
|
||||
* will use provided register |state| as is.
|
||||
* \note GetStackSample is thread and signal safe and should only be called
|
||||
* when the JS thread is paused or interrupted.
|
||||
* Otherwise the behavior is undefined.
|
||||
*/
|
||||
static bool GetStackSample(Isolate* isolate, v8::RegisterState* state,
|
||||
RecordCEntryFrame record_c_entry_frame,
|
||||
void** frames, size_t frames_limit,
|
||||
v8::SampleInfo* sample_info);
|
||||
v8::SampleInfo* sample_info,
|
||||
bool use_simulator_reg_state = true);
|
||||
StateTag state; // The state of the VM.
|
||||
void* pc; // Instruction pointer.
|
||||
union {
|
||||
|
18
src/api.cc
18
src/api.cc
@ -7672,20 +7672,14 @@ bool Isolate::GetHeapCodeAndMetadataStatistics(
|
||||
|
||||
void Isolate::GetStackSample(const RegisterState& state, void** frames,
|
||||
size_t frames_limit, SampleInfo* sample_info) {
|
||||
#if defined(USE_SIMULATOR)
|
||||
RegisterState regs;
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
if (!i::SimulatorHelper::FillRegisters(isolate, ®s)) {
|
||||
sample_info->frames_count = 0;
|
||||
sample_info->vm_state = OTHER;
|
||||
sample_info->external_callback_entry = nullptr;
|
||||
RegisterState regs = state;
|
||||
if (TickSample::GetStackSample(this, ®s, TickSample::kSkipCEntryFrame,
|
||||
frames, frames_limit, sample_info)) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
const RegisterState& regs = state;
|
||||
#endif
|
||||
TickSample::GetStackSample(this, regs, TickSample::kSkipCEntryFrame, frames,
|
||||
frames_limit, sample_info);
|
||||
sample_info->frames_count = 0;
|
||||
sample_info->vm_state = OTHER;
|
||||
sample_info->external_callback_entry = nullptr;
|
||||
}
|
||||
|
||||
size_t Isolate::NumberOfPhantomHandleResetsSinceLastCall() {
|
||||
|
12
src/log.cc
12
src/log.cc
@ -645,15 +645,9 @@ class Ticker: public sampler::Sampler {
|
||||
|
||||
void SampleStack(const v8::RegisterState& state) override {
|
||||
if (!profiler_) return;
|
||||
#if defined(USE_SIMULATOR)
|
||||
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate());
|
||||
v8::RegisterState regs;
|
||||
if (!SimulatorHelper::FillRegisters(i_isolate, ®s)) return;
|
||||
#else
|
||||
const v8::RegisterState& regs = state;
|
||||
#endif
|
||||
v8::TickSample sample;
|
||||
sample.Init(isolate(), regs, v8::TickSample::kIncludeCEntryFrame, true);
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
|
||||
TickSample sample;
|
||||
sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true);
|
||||
profiler_->Insert(&sample);
|
||||
}
|
||||
|
||||
|
@ -23,18 +23,11 @@ class CpuSampler : public sampler::Sampler {
|
||||
: sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
|
||||
processor_(processor) {}
|
||||
|
||||
void SampleStack(const v8::RegisterState& state) override {
|
||||
v8::Isolate* v8_isolate = isolate();
|
||||
Isolate* i_isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
||||
#if defined(USE_SIMULATOR)
|
||||
v8::RegisterState regs;
|
||||
if (!SimulatorHelper::FillRegisters(i_isolate, ®s)) return;
|
||||
#else
|
||||
const v8::RegisterState& regs = state;
|
||||
#endif
|
||||
void SampleStack(const v8::RegisterState& regs) override {
|
||||
TickSample* sample = processor_->StartTickSample();
|
||||
if (sample == NULL) return;
|
||||
sample->Init(i_isolate, regs, TickSample::kIncludeCEntryFrame, true);
|
||||
if (sample == nullptr) return;
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
|
||||
sample->Init(isolate, regs, TickSample::kIncludeCEntryFrame, true);
|
||||
if (is_counting_samples_ && !sample->timestamp.IsNull()) {
|
||||
if (sample->state == JS) ++js_sample_count_;
|
||||
if (sample->state == EXTERNAL) ++external_sample_count_;
|
||||
@ -77,7 +70,7 @@ void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from,
|
||||
regs.sp = fp - fp_to_sp_delta;
|
||||
regs.fp = fp;
|
||||
regs.pc = from;
|
||||
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false);
|
||||
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false, false);
|
||||
ticks_from_vm_buffer_.Enqueue(record);
|
||||
}
|
||||
|
||||
@ -92,7 +85,8 @@ void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate,
|
||||
regs.fp = frame->fp();
|
||||
regs.pc = frame->pc();
|
||||
}
|
||||
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, update_stats);
|
||||
record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, update_stats,
|
||||
false);
|
||||
ticks_from_vm_buffer_.Enqueue(record);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "src/vm-state-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsSamePage(i::byte* ptr1, i::byte* ptr2) {
|
||||
@ -77,118 +76,17 @@ bool IsNoFrameRegion(i::Address address) {
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// StackTracer implementation
|
||||
//
|
||||
DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
|
||||
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);
|
||||
|
||||
// If at this point iterator does not see any frames,
|
||||
// is usually means something is wrong with the FP,
|
||||
// e.g. it is used as a general purpose register in the function.
|
||||
// Bailout.
|
||||
if (it.done()) return false;
|
||||
|
||||
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++] = isolate->c_function();
|
||||
}
|
||||
for (; !it.done() && i < frames_limit; it.Advance()) {
|
||||
if (!it.frame()->is_interpreted()) {
|
||||
frames[i++] = it.frame()->pc();
|
||||
continue;
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
sample_info->frames_count = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
|
||||
RecordCEntryFrame record_c_entry_frame,
|
||||
bool update_stats) {
|
||||
v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
|
||||
record_c_entry_frame, update_stats);
|
||||
if (pc == nullptr) return;
|
||||
timestamp = base::TimeTicks::HighResolutionNow();
|
||||
}
|
||||
namespace {
|
||||
|
||||
#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);
|
||||
};
|
||||
|
||||
bool SimulatorHelper::FillRegisters(Isolate* isolate,
|
||||
v8::RegisterState* state) {
|
||||
Simulator* simulator = isolate->thread_local_top()->simulator_;
|
||||
@ -242,5 +140,130 @@ bool SimulatorHelper::FillRegisters(Isolate* isolate,
|
||||
}
|
||||
#endif // USE_SIMULATOR
|
||||
|
||||
} // 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) {
|
||||
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)) {
|
||||
// It is executing JS but failed to collect a stack trace.
|
||||
// Mark the sample as spoiled.
|
||||
pc = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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.
|
||||
|
||||
#if defined(USE_SIMULATOR)
|
||||
if (use_simulator_reg_state) {
|
||||
if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false;
|
||||
}
|
||||
#else
|
||||
USE(use_simulator_reg_state);
|
||||
#endif
|
||||
DCHECK(regs->sp);
|
||||
|
||||
if (regs->pc && IsNoFrameRegion(static_cast<i::Address>(regs->pc))) {
|
||||
// The frame is not setup, so it'd be hard to iterate the stack. Bailout.
|
||||
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);
|
||||
|
||||
// If at this point iterator does not see any frames,
|
||||
// is usually means something is wrong with the FP,
|
||||
// e.g. it is used as a general purpose register in the function.
|
||||
// Bailout.
|
||||
if (it.done()) return false;
|
||||
|
||||
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++] = isolate->c_function();
|
||||
}
|
||||
for (; !it.done() && i < frames_limit; it.Advance()) {
|
||||
if (!it.frame()->is_interpreted()) {
|
||||
frames[i++] = it.frame()->pc();
|
||||
continue;
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
sample_info->frames_count = i;
|
||||
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) {
|
||||
v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
|
||||
record_c_entry_frame, update_stats,
|
||||
use_simulator_reg_state);
|
||||
if (pc == nullptr) return;
|
||||
timestamp = base::TimeTicks::HighResolutionNow();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -15,21 +15,12 @@ namespace internal {
|
||||
class Isolate;
|
||||
|
||||
struct TickSample : public v8::TickSample {
|
||||
TickSample() : v8::TickSample() {}
|
||||
void Init(Isolate* isolate, const v8::RegisterState& state,
|
||||
RecordCEntryFrame record_c_entry_frame, bool update_stats);
|
||||
RecordCEntryFrame record_c_entry_frame, bool update_stats,
|
||||
bool use_simulator_reg_state = true);
|
||||
base::TimeTicks timestamp;
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
|
@ -41,10 +41,9 @@ class TestSampler : public Sampler {
|
||||
explicit TestSampler(Isolate* isolate) : Sampler(isolate) {}
|
||||
|
||||
void SampleStack(const v8::RegisterState& regs) override {
|
||||
void* frames[Sampler::kMaxFramesCount];
|
||||
void* frames[kMaxFramesCount];
|
||||
SampleInfo sample_info;
|
||||
isolate()->GetStackSample(regs, reinterpret_cast<void**>(frames),
|
||||
Sampler::kMaxFramesCount, &sample_info);
|
||||
isolate()->GetStackSample(regs, frames, kMaxFramesCount, &sample_info);
|
||||
if (is_counting_samples_) {
|
||||
if (sample_info.vm_state == JS) ++js_sample_count_;
|
||||
if (sample_info.vm_state == EXTERNAL) ++external_sample_count_;
|
||||
|
Loading…
Reference in New Issue
Block a user