Commit 3172f6a9 authored by lpy's avatar lpy Committed by Commit bot

Expose TickSample and its APIs in v8-profiler.h

We want to eventually move the profiling functionality out of V8 as library,
this patch exposes TickSample and its APIs in v8-profiler.h so that when
embedders use library, they can have more details.

Minor change: Rename tick-sample.[h|cc] to simulator-helper.[h|cc].

BUG=v8:4789
LOG=N

Review-Url: https://codereview.chromium.org/2105943002
Cr-Commit-Position: refs/heads/master@{#37564}
parent 96ebd756
......@@ -46,6 +46,40 @@ template class V8_EXPORT std::vector<v8::CpuProfileDeoptInfo>;
namespace v8 {
// 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(nullptr),
external_callback_entry(nullptr),
frames_count(0),
has_external_callback(false),
update_stats(true) {}
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,
void** frames, size_t frames_limit,
v8::SampleInfo* sample_info);
StateTag state; // The state of the VM.
void* pc; // Instruction pointer.
union {
void* tos; // Top stack value (*sp).
void* external_callback_entry;
};
static const unsigned kMaxFramesCountLog2 = 8;
static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1;
void* stack[kMaxFramesCount]; // Call stack.
unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
bool has_external_callback : 1;
bool update_stats : 1; // Whether the sample should update aggregated stats.
};
/**
* CpuProfileNode represents a node in a call graph.
*/
......
......@@ -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/globals.h"
......@@ -7579,12 +7580,9 @@ 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)
RegisterState regs;
regs.pc = state.pc;
regs.sp = state.sp;
regs.fp = state.fp;
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
if (!i::SimulatorHelper::FillRegisters(isolate, &regs)) {
sample_info->frames_count = 0;
sample_info->vm_state = OTHER;
......@@ -7594,8 +7592,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() {
......
......@@ -23,6 +23,7 @@
#include "src/perf-jit.h"
#include "src/profiler/cpu-profiler-inl.h"
#include "src/profiler/profiler-listener.h"
#include "src/profiler/tick-sample.h"
#include "src/runtime-profiler.h"
#include "src/string-stream.h"
#include "src/vm-state-inl.h"
......@@ -646,14 +647,14 @@ class Ticker: public sampler::Sampler {
void SampleStack(const v8::RegisterState& state) override {
if (!profiler_) return;
v8::Isolate* v8_isolate = isolate();
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
Isolate* i_isolate = reinterpret_cast<Isolate*>(v8_isolate);
#if defined(USE_SIMULATOR)
if (!SimulatorHelper::FillRegisters(isolate,
if (!SimulatorHelper::FillRegisters(i_isolate,
const_cast<v8::RegisterState*>(&state)))
return;
#endif
TickSample sample;
sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true);
sample.Init(i_isolate, state, TickSample::kIncludeCEntryFrame, true);
profiler_->Insert(&sample);
}
......@@ -1373,14 +1374,15 @@ void Logger::TickEvent(TickSample* sample, bool overflow) {
}
Log::MessageBuilder msg(log_);
msg.Append("%s,", kLogEventsNames[CodeEventListener::TICK_EVENT]);
msg.AppendAddress(sample->pc);
msg.AppendAddress(reinterpret_cast<Address>(sample->pc));
msg.Append(",%d", static_cast<int>(timer_.Elapsed().InMicroseconds()));
if (sample->has_external_callback) {
msg.Append(",1,");
msg.AppendAddress(sample->external_callback_entry);
msg.AppendAddress(
reinterpret_cast<Address>(sample->external_callback_entry));
} else {
msg.Append(",0,");
msg.AppendAddress(sample->tos);
msg.AppendAddress(reinterpret_cast<Address>(sample->tos));
}
msg.Append(",%d", static_cast<int>(sample->state));
if (overflow) {
......@@ -1388,7 +1390,7 @@ void Logger::TickEvent(TickSample* sample, bool overflow) {
}
for (unsigned i = 0; i < sample->frames_count; ++i) {
msg.Append(',');
msg.AppendAddress(sample->stack[i]);
msg.AppendAddress(reinterpret_cast<Address>(sample->stack[i]));
}
msg.WriteToLogFile();
}
......
......@@ -68,8 +68,8 @@ class Isolate;
class Log;
class Profiler;
class Ticker;
struct TickSample;
class RuntimeCallTimer;
struct TickSample;
#undef LOG
#define LOG(isolate, Call) \
......
......@@ -12,8 +12,6 @@
#include "src/profiler/cpu-profiler-inl.h"
#include "src/vm-state-inl.h"
#include "include/v8-profiler.h"
namespace v8 {
namespace internal {
......
......@@ -11,7 +11,6 @@
#include "src/global-handles.h"
#include "src/profiler/cpu-profiler.h"
#include "src/profiler/profile-generator-inl.h"
#include "src/profiler/tick-sample.h"
#include "src/unicode.h"
namespace v8 {
......@@ -578,22 +577,24 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// Don't use PC when in external callback code, as it can point
// inside callback's code, and we will erroneously report
// that a callback calls itself.
entries.push_back(code_map_.FindEntry(sample.external_callback_entry));
entries.push_back(code_map_.FindEntry(
reinterpret_cast<Address>(sample.external_callback_entry)));
} else {
CodeEntry* pc_entry = code_map_.FindEntry(sample.pc);
CodeEntry* pc_entry =
code_map_.FindEntry(reinterpret_cast<Address>(sample.pc));
// If there is no pc_entry we're likely in native code.
// Find out, if top of stack was pointing inside a JS function
// meaning that we have encountered a frameless invocation.
if (!pc_entry && !sample.has_external_callback) {
pc_entry = code_map_.FindEntry(sample.tos);
pc_entry = code_map_.FindEntry(reinterpret_cast<Address>(sample.tos));
}
// If pc is in the function code before it set up stack frame or after the
// frame was destroyed SafeStackFrameIterator incorrectly thinks that
// ebp contains return address of the current function and skips caller's
// frame. Check for this case and just skip such samples.
if (pc_entry) {
int pc_offset =
static_cast<int>(sample.pc - pc_entry->instruction_start());
int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) -
pc_entry->instruction_start());
src_line = pc_entry->GetSourceLine(pc_offset);
if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
src_line = pc_entry->line_number();
......@@ -616,15 +617,14 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
}
}
for (const Address *stack_pos = sample.stack,
*stack_end = stack_pos + sample.frames_count;
stack_pos != stack_end; ++stack_pos) {
CodeEntry* entry = code_map_.FindEntry(*stack_pos);
for (unsigned i = 0; i < sample.frames_count; ++i) {
Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
CodeEntry* entry = code_map_.FindEntry(stack_pos);
if (entry) {
// Find out if the entry has an inlining stack associated.
int pc_offset =
static_cast<int>(*stack_pos - entry->instruction_start());
static_cast<int>(stack_pos - entry->instruction_start());
const std::vector<CodeEntry*>* inline_stack =
entry->GetInlineStack(pc_offset);
if (inline_stack) {
......
......@@ -6,13 +6,15 @@
#define V8_PROFILER_PROFILE_GENERATOR_H_
#include <map>
#include "include/v8-profiler.h"
#include "src/allocation.h"
#include "src/base/hashmap.h"
#include "src/compiler.h"
#include "src/profiler/strings-storage.h"
namespace v8 {
struct TickSample;
namespace internal {
// Provides a mapping from the offsets within generated code to
......
......@@ -4,16 +4,17 @@
#include "src/profiler/tick-sample.h"
#include "include/v8-profiler.h"
#include "src/frames-inl.h"
#include "src/msan.h"
#include "src/simulator.h"
#include "src/vm-state-inl.h"
namespace v8 {
namespace internal {
namespace {
bool IsSamePage(byte* ptr1, byte* ptr2) {
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) ==
......@@ -22,13 +23,13 @@ bool IsSamePage(byte* ptr1, byte* ptr2) {
// Check if the code at specified address could potentially be a
// frame setup code.
bool IsNoFrameRegion(Address address) {
bool IsNoFrameRegion(i::Address address) {
struct Pattern {
int bytes_count;
byte bytes[8];
i::byte bytes[8];
int offsets[4];
};
byte* pc = reinterpret_cast<byte*>(address);
i::byte* pc = reinterpret_cast<i::byte*>(address);
static Pattern patterns[] = {
#if V8_HOST_ARCH_IA32
// push %ebp
......@@ -79,62 +80,60 @@ bool IsNoFrameRegion(Address address) {
//
// StackTracer implementation
//
DISABLE_ASAN void TickSample::Init(Isolate* isolate,
const v8::RegisterState& regs,
DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
const RegisterState& regs,
RecordCEntryFrame record_c_entry_frame,
bool update_stats) {
timestamp = base::TimeTicks::HighResolutionNow();
this->update_stats = update_stats;
SampleInfo info;
if (GetStackSample(isolate, regs, record_c_entry_frame,
reinterpret_cast<void**>(&stack[0]), kMaxFramesCount,
&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 = static_cast<Address>(regs.pc);
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 =
static_cast<Address>(info.external_callback_entry);
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(Address));
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 = Memory::Address_at(reinterpret_cast<Address>(regs.sp));
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.
timestamp = base::TimeTicks();
pc = nullptr;
}
}
bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
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;
Address js_entry_sp = isolate->js_entry_sp();
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<Address>(regs.pc))) {
if (regs.pc && IsNoFrameRegion(static_cast<i::Address>(regs.pc))) {
// Can't collect stack.
return false;
}
ExternalCallbackScope* scope = isolate->external_callback_scope();
Address handler = Isolate::handler(isolate->thread_local_top());
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.
......@@ -143,23 +142,26 @@ bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
*scope->callback_entrypoint_address();
}
SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
reinterpret_cast<Address>(regs.sp), js_entry_sp);
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() == StackFrame::EXIT ||
it.top_frame_type() == StackFrame::BUILTIN_EXIT)) {
(it.top_frame_type() == internal::StackFrame::EXIT ||
it.top_frame_type() == internal::StackFrame::BUILTIN_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());
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.
Address bytecode_array =
reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag;
frames[i++] = bytecode_array + BytecodeArray::kHeaderSize +
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();
......@@ -170,10 +172,21 @@ bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
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();
}
#if defined(USE_SIMULATOR)
bool SimulatorHelper::FillRegisters(Isolate* isolate,
v8::RegisterState* state) {
Simulator *simulator = isolate->thread_local_top()->simulator_;
Simulator* simulator = isolate->thread_local_top()->simulator_;
// Check if there is active simulator.
if (simulator == NULL) return false;
#if V8_TARGET_ARCH_ARM
......@@ -181,8 +194,8 @@ bool SimulatorHelper::FillRegisters(Isolate* isolate,
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));
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());
......
......@@ -5,10 +5,8 @@
#ifndef V8_PROFILER_TICK_SAMPLE_H_
#define V8_PROFILER_TICK_SAMPLE_H_
#include "include/v8.h"
#include "include/v8-profiler.h"
#include "src/base/platform/time.h"
#include "src/frames.h"
#include "src/globals.h"
namespace v8 {
......@@ -16,49 +14,13 @@ 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) {}
struct TickSample : public v8::TickSample {
TickSample() : v8::TickSample() {}
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,
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.
};
#if defined(USE_SIMULATOR)
class SimulatorHelper {
public:
......
......@@ -81,7 +81,7 @@ static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
i::Address frame1,
i::Address frame2 = NULL,
i::Address frame3 = NULL) {
i::TickSample* sample = proc->StartTickSample();
v8::TickSample* sample = proc->StartTickSample();
sample->pc = frame1;
sample->tos = frame1;
sample->frames_count = 0;
......@@ -309,10 +309,10 @@ TEST(Issue1398) {
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
i::TickSample* sample = processor->StartTickSample();
v8::TickSample* sample = processor->StartTickSample();
sample->pc = code->address();
sample->tos = 0;
sample->frames_count = i::TickSample::kMaxFramesCount;
sample->frames_count = v8::TickSample::kMaxFramesCount;
for (unsigned i = 0; i < sample->frames_count; ++i) {
sample->stack[i] = code->address();
}
......@@ -331,7 +331,7 @@ TEST(Issue1398) {
++actual_depth;
}
CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
CHECK_EQ(1 + v8::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
}
TEST(DeleteAllCpuProfiles) {
......
......@@ -29,14 +29,13 @@
#include <stdlib.h>
#include "src/v8.h"
#include "include/v8-profiler.h"
#include "src/api.h"
#include "src/codegen.h"
#include "src/disassembler.h"
#include "src/isolate.h"
#include "src/log.h"
#include "src/profiler/tick-sample.h"
#include "src/v8.h"
#include "src/vm-state-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/trace-extension.h"
......@@ -46,6 +45,7 @@ using v8::Local;
using v8::Object;
using v8::Script;
using v8::String;
using v8::TickSample;
using v8::Value;
using v8::internal::byte;
......@@ -53,18 +53,15 @@ using v8::internal::Address;
using v8::internal::Handle;
using v8::internal::Isolate;
using v8::internal::JSFunction;
using v8::internal::TickSample;
static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
static bool IsAddressWithinFuncCode(JSFunction* function, void* addr) {
Address address = reinterpret_cast<Address>(addr);
i::AbstractCode* code = function->abstract_code();
return code->contains(addr);
return code->contains(address);
}
static bool IsAddressWithinFuncCode(v8::Local<v8::Context> context,
const char* func_name,
Address addr) {
const char* func_name, void* addr) {
v8::Local<v8::Value> func =
context->Global()->Get(context, v8_str(func_name)).ToLocalChecked();
CHECK(func->IsFunction());
......
......@@ -27,7 +27,7 @@
#include "test/cctest/trace-extension.h"
#include "src/profiler/tick-sample.h"
#include "include/v8-profiler.h"
#include "src/vm-state-inl.h"
#include "test/cctest/cctest.h"
......@@ -90,13 +90,9 @@ Address TraceExtension::GetFP(const v8::FunctionCallbackInfo<v8::Value>& args) {
return fp;
}
static struct { v8::TickSample* sample; } trace_env = {nullptr};
static struct {
TickSample* sample;
} trace_env = { NULL };
void TraceExtension::InitTraceEnv(TickSample* sample) {
void TraceExtension::InitTraceEnv(v8::TickSample* sample) {
trace_env.sample = sample;
}
......@@ -107,8 +103,8 @@ void TraceExtension::DoTrace(Address fp) {
// sp is only used to define stack high bound
regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240;
trace_env.sample->Init(CcTest::i_isolate(), regs,
TickSample::kSkipCEntryFrame, true);
trace_env.sample->Init(CcTest::isolate(), regs,
v8::TickSample::kSkipCEntryFrame, true);
}
......
......@@ -31,9 +31,8 @@
#include "src/v8.h"
namespace v8 {
namespace internal {
struct TickSample;
namespace internal {
class TraceExtension : public v8::Extension {
public:
......@@ -45,7 +44,7 @@ class TraceExtension : public v8::Extension {
static void JSEntrySP(const v8::FunctionCallbackInfo<v8::Value>& args);
static void JSEntrySPLevel2(const v8::FunctionCallbackInfo<v8::Value>& args);
static Address GetJsEntrySp();
static void InitTraceEnv(TickSample* sample);
static void InitTraceEnv(v8::TickSample* sample);
static void DoTrace(Address fp);
private:
static Address GetFP(const v8::FunctionCallbackInfo<v8::Value>& args);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment