[Reland] Refactor CpuProfiler.

Currently CpuProfiler is a subclass of CodeEventListener, it listens code events
from Logger, constructs and stores CodeEventsContainer. This patch is part of
the effort to split the logic of CodeEventListener as ProfilerListener out of
the profiling functionality logic in CpuProfiler. A ProfilerListener will listen
to code events, construct code event to CodeEventsContainer and pass it to code
event handler.

The reason we refactor CpuProfiler is that eventually we want to move
CpuProfiler as part of sampler library and code event listener should stay
inside V8.

Main changes:
1. Refactored CpuProfiler into two parts, the CpuProfiler with profling
functionality and the ProfilerListener listening to code events from Logger.
2. Created CodeEventObserver and made CpuProfiler inherit from it.
ProfilerListener will have a list of observers and call CodeEventHandler once a
code event is created.
3. Moved code entry list from CodeEntry to ProfilerListener.

Minor changes:
1. Moved static code entry as part of CodeEntry.
2. Added ProfilerListener to Logger.

BUG=v8:4789

Committed: https://crrev.com/cb59fc1facc9b390e2c7544b4da56a4e0a9b3222
Review-Url: https://codereview.chromium.org/2053523003
Cr-Original-Commit-Position: refs/heads/master@{#37112}
Cr-Commit-Position: refs/heads/master@{#37195}
This commit is contained in:
lpy 2016-06-22 09:43:46 -07:00 committed by Commit bot
parent c444b2b7af
commit 04f710ac20
12 changed files with 641 additions and 430 deletions

View File

@ -1331,6 +1331,8 @@ v8_source_set("v8_base") {
"src/profiler/profile-generator-inl.h",
"src/profiler/profile-generator.cc",
"src/profiler/profile-generator.h",
"src/profiler/profiler-listener.cc",
"src/profiler/profiler-listener.h",
"src/profiler/sampling-heap-profiler.cc",
"src/profiler/sampling-heap-profiler.h",
"src/profiler/strings-storage.cc",

View File

@ -22,6 +22,7 @@
#include "src/macro-assembler.h"
#include "src/perf-jit.h"
#include "src/profiler/cpu-profiler-inl.h"
#include "src/profiler/profiler-listener.h"
#include "src/runtime-profiler.h"
#include "src/string-stream.h"
#include "src/vm-state-inl.h"
@ -1789,6 +1790,8 @@ bool Logger::SetUp(Isolate* isolate) {
profiler_->Engage();
}
profiler_listener_.reset();
if (is_logging_) {
addCodeEventListener(this);
}
@ -1816,6 +1819,18 @@ void Logger::SetCodeEventHandler(uint32_t options,
}
}
void Logger::SetUpProfilerListener() {
if (!is_initialized_) return;
if (profiler_listener_.get() == nullptr) {
profiler_listener_.reset(new ProfilerListener(isolate_));
}
addCodeEventListener(profiler_listener_.get());
}
void Logger::TearDownProfilerListener() {
if (profiler_listener_->HasObservers()) return;
removeCodeEventListener(profiler_listener_.get());
}
sampler::Sampler* Logger::sampler() {
return ticker_;
@ -1860,6 +1875,10 @@ FILE* Logger::TearDown() {
jit_logger_ = NULL;
}
if (profiler_listener_.get() != nullptr) {
removeCodeEventListener(profiler_listener_.get());
}
return log_->Close();
}

View File

@ -89,6 +89,7 @@ class JitLogger;
class PerfBasicLogger;
class LowLevelLogger;
class PerfJitLogger;
class ProfilerListener;
class Logger : public CodeEventListener {
public:
@ -101,8 +102,16 @@ class Logger : public CodeEventListener {
void SetCodeEventHandler(uint32_t options,
JitCodeEventHandler event_handler);
// Sets up ProfilerListener.
void SetUpProfilerListener();
// Tear down ProfilerListener if it has no observers.
void TearDownProfilerListener();
sampler::Sampler* sampler();
ProfilerListener* profiler_listener() { return profiler_listener_.get(); }
// Frees resources acquired in SetUp.
// When a temporary file is used for the log, returns its stream descriptor,
// leaving the file open.
@ -332,6 +341,7 @@ class Logger : public CodeEventListener {
PerfJitLogger* perf_jit_logger_;
LowLevelLogger* ll_logger_;
JitLogger* jit_logger_;
std::unique_ptr<ProfilerListener> profiler_listener_;
List<CodeEventListener*> listeners_;
// Guards against multiple calls to TearDown() that can happen in some tests.

View File

@ -199,294 +199,23 @@ void CpuProfiler::DeleteProfile(CpuProfile* profile) {
}
}
void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry = profiles_->NewCodeEntry(CodeEventListener::CALLBACK_TAG,
profiles_->GetName(name));
rec->size = 1;
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, const char* name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, Name* name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code,
SharedFunctionInfo* shared,
Name* script_name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetFunctionName(shared->DebugName()),
CodeEntry::kEmptyNamePrefix,
profiles_->GetName(InferScriptName(script_name, shared)),
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->entry->FillFunctionInfo(shared);
rec->size = code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* abstract_code,
SharedFunctionInfo* shared, Name* script_name,
int line, int column) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = abstract_code->address();
Script* script = Script::cast(shared->script());
JITLineInfoTable* line_table = NULL;
if (script) {
if (abstract_code->IsCode()) {
Code* code = abstract_code->GetCode();
int start_position = shared->start_position();
int end_position = shared->end_position();
line_table = new JITLineInfoTable();
for (RelocIterator it(code); !it.done(); it.next()) {
RelocInfo* reloc_info = it.rinfo();
if (!RelocInfo::IsPosition(reloc_info->rmode())) continue;
int position = static_cast<int>(reloc_info->data());
// TODO(alph): in case of inlining the position may correspond
// to an inlined function source code. Do not collect positions
// that fall beyond the function source code. There's however a
// chance the inlined function has similar positions but in another
// script. So the proper fix is to store script_id in some form
// along with the inlined function positions.
if (position < start_position || position >= end_position) continue;
int pc_offset = static_cast<int>(reloc_info->pc() - code->address());
int line_number = script->GetLineNumber(position) + 1;
line_table->SetPosition(pc_offset, line_number);
}
} else {
BytecodeArray* bytecode = abstract_code->GetBytecodeArray();
line_table = new JITLineInfoTable();
interpreter::SourcePositionTableIterator it(
bytecode->source_position_table());
for (; !it.done(); it.Advance()) {
int line_number = script->GetLineNumber(it.source_position()) + 1;
int pc_offset = it.bytecode_offset() + BytecodeArray::kHeaderSize;
line_table->SetPosition(pc_offset, line_number);
}
}
}
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetFunctionName(shared->DebugName()),
CodeEntry::kEmptyNamePrefix,
profiles_->GetName(InferScriptName(script_name, shared)), line, column,
line_table, abstract_code->instruction_start());
RecordInliningInfo(rec->entry, abstract_code);
RecordDeoptInlinedFrames(rec->entry, abstract_code);
rec->entry->FillFunctionInfo(shared);
rec->size = abstract_code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, int args_count) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = profiles_->NewCodeEntry(
tag, profiles_->GetName(args_count), "args_count: ",
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeMoveEvent(AbstractCode* from, Address to) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
rec->from = from->address();
rec->to = to;
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
rec->start = code->address();
rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
processor_->Enqueue(evt_rec);
}
void CpuProfiler::CodeDeoptEvent(Code* code, Address pc, int fp_to_sp_delta) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
rec->start = code->address();
rec->deopt_reason = Deoptimizer::GetDeoptReason(info.deopt_reason);
rec->position = info.position;
rec->deopt_id = info.deopt_id;
processor_->Enqueue(evt_rec);
processor_->AddDeoptStack(isolate_, pc, fp_to_sp_delta);
}
void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry = profiles_->NewCodeEntry(CodeEventListener::CALLBACK_TAG,
profiles_->GetName(name), "get ");
rec->size = 1;
processor_->Enqueue(evt_rec);
}
void CpuProfiler::RegExpCodeCreateEvent(AbstractCode* code, String* source) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = profiles_->NewCodeEntry(
CodeEventListener::REG_EXP_TAG, profiles_->GetName(source), "RegExp: ",
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
rec->size = code->ExecutableSize();
processor_->Enqueue(evt_rec);
}
void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry = profiles_->NewCodeEntry(CodeEventListener::CALLBACK_TAG,
profiles_->GetName(name), "set ");
rec->size = 1;
processor_->Enqueue(evt_rec);
}
Name* CpuProfiler::InferScriptName(Name* name, SharedFunctionInfo* info) {
if (name->IsString() && String::cast(name)->length()) return name;
if (!info->script()->IsScript()) return name;
Object* source_url = Script::cast(info->script())->source_url();
return source_url->IsName() ? Name::cast(source_url) : name;
}
void CpuProfiler::RecordInliningInfo(CodeEntry* entry,
AbstractCode* abstract_code) {
if (!abstract_code->IsCode()) return;
Code* code = abstract_code->GetCode();
if (code->kind() != Code::OPTIMIZED_FUNCTION) return;
DeoptimizationInputData* deopt_input_data =
DeoptimizationInputData::cast(code->deoptimization_data());
int deopt_count = deopt_input_data->DeoptCount();
for (int i = 0; i < deopt_count; i++) {
int pc_offset = deopt_input_data->Pc(i)->value();
if (pc_offset == -1) continue;
int translation_index = deopt_input_data->TranslationIndex(i)->value();
TranslationIterator it(deopt_input_data->TranslationByteArray(),
translation_index);
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
DCHECK_EQ(Translation::BEGIN, opcode);
it.Skip(Translation::NumberOfOperandsFor(opcode));
int depth = 0;
std::vector<CodeEntry*> inline_stack;
while (it.HasNext() &&
Translation::BEGIN !=
(opcode = static_cast<Translation::Opcode>(it.Next()))) {
if (opcode != Translation::JS_FRAME &&
opcode != Translation::INTERPRETED_FRAME) {
it.Skip(Translation::NumberOfOperandsFor(opcode));
continue;
}
it.Next(); // Skip ast_id
int shared_info_id = it.Next();
it.Next(); // Skip height
SharedFunctionInfo* shared_info = SharedFunctionInfo::cast(
deopt_input_data->LiteralArray()->get(shared_info_id));
if (!depth++) continue; // Skip the current function itself.
CodeEntry* inline_entry = new CodeEntry(
entry->tag(), profiles_->GetFunctionName(shared_info->DebugName()),
CodeEntry::kEmptyNamePrefix, entry->resource_name(),
CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
inline_entry->FillFunctionInfo(shared_info);
inline_stack.push_back(inline_entry);
}
if (!inline_stack.empty()) {
entry->AddInlineStack(pc_offset, inline_stack);
DCHECK(inline_stack.empty());
}
}
}
void CpuProfiler::RecordDeoptInlinedFrames(CodeEntry* entry,
AbstractCode* abstract_code) {
if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
Code* code = abstract_code->GetCode();
DeoptimizationInputData* deopt_input_data =
DeoptimizationInputData::cast(code->deoptimization_data());
int const mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID);
for (RelocIterator rit(code, mask); !rit.done(); rit.next()) {
RelocInfo* reloc_info = rit.rinfo();
DCHECK(RelocInfo::IsDeoptId(reloc_info->rmode()));
int deopt_id = static_cast<int>(reloc_info->data());
int translation_index =
deopt_input_data->TranslationIndex(deopt_id)->value();
TranslationIterator it(deopt_input_data->TranslationByteArray(),
translation_index);
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
DCHECK_EQ(Translation::BEGIN, opcode);
it.Skip(Translation::NumberOfOperandsFor(opcode));
std::vector<CodeEntry::DeoptInlinedFrame> inlined_frames;
while (it.HasNext() &&
Translation::BEGIN !=
(opcode = static_cast<Translation::Opcode>(it.Next()))) {
if (opcode != Translation::JS_FRAME &&
opcode != Translation::INTERPRETED_FRAME) {
it.Skip(Translation::NumberOfOperandsFor(opcode));
continue;
}
BailoutId ast_id = BailoutId(it.Next());
int shared_info_id = it.Next();
it.Next(); // Skip height
SharedFunctionInfo* shared = SharedFunctionInfo::cast(
deopt_input_data->LiteralArray()->get(shared_info_id));
int source_position = Deoptimizer::ComputeSourcePosition(shared, ast_id);
int script_id = v8::UnboundScript::kNoScriptId;
if (shared->script()->IsScript()) {
Script* script = Script::cast(shared->script());
script_id = script->id();
}
CodeEntry::DeoptInlinedFrame frame = {source_position, script_id};
inlined_frames.push_back(frame);
}
if (!inlined_frames.empty() && !entry->HasDeoptInlinedFramesFor(deopt_id)) {
entry->AddDeoptInlinedFrames(deopt_id, inlined_frames);
DCHECK(inlined_frames.empty());
void CpuProfiler::CodeEventHandler(const CodeEventsContainer& evt_rec) {
switch (evt_rec.generic.type) {
case CodeEventRecord::CODE_CREATION:
case CodeEventRecord::CODE_MOVE:
case CodeEventRecord::CODE_DISABLE_OPT:
processor_->Enqueue(evt_rec);
break;
case CodeEventRecord::CODE_DEOPT: {
const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
Address pc = reinterpret_cast<Address>(rec->pc);
int fp_to_sp_delta = rec->fp_to_sp_delta;
processor_->Enqueue(evt_rec);
processor_->AddDeoptStack(isolate_, pc, fp_to_sp_delta);
break;
}
default:
UNREACHABLE();
}
}
@ -558,11 +287,13 @@ void CpuProfiler::StartProcessorIfNotStarted() {
generator_.reset(new ProfileGenerator(profiles_.get()));
processor_.reset(new ProfilerEventsProcessor(generator_.get(), sampler,
sampling_interval_));
logger->SetUpProfilerListener();
ProfilerListener* profiler_listener = logger->profiler_listener();
profiler_listener->AddObserver(this);
is_profiling_ = true;
isolate_->set_is_profiling(true);
// Enumerate stuff we already have in the heap.
DCHECK(isolate_->heap()->HasBeenSetUp());
isolate_->code_event_dispatcher()->AddListener(this);
if (!FLAG_prof_browser_mode) {
logger->LogCodeObjects();
}
@ -609,8 +340,10 @@ void CpuProfiler::StopProcessor() {
reinterpret_cast<sampler::Sampler*>(logger->ticker_);
is_profiling_ = false;
isolate_->set_is_profiling(false);
isolate_->code_event_dispatcher()->RemoveListener(this);
ProfilerListener* profiler_listener = logger->profiler_listener();
profiler_listener->RemoveObserver(this);
processor_->StopSynchronously();
logger->TearDownProfilerListener();
processor_.reset();
generator_.reset();
sampler->SetHasProcessingThread(false);
@ -632,6 +365,5 @@ void CpuProfiler::LogBuiltins() {
}
}
} // namespace internal
} // namespace v8

View File

@ -16,6 +16,7 @@
#include "src/libsampler/v8-sampler.h"
#include "src/locked-queue.h"
#include "src/profiler/circular-queue.h"
#include "src/profiler/profiler-listener.h"
#include "src/profiler/tick-sample.h"
namespace v8 {
@ -85,6 +86,8 @@ class CodeDeoptEventRecord : public CodeEventRecord {
const char* deopt_reason;
SourcePosition position;
int deopt_id;
void* pc;
int fp_to_sp_delta;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
@ -183,7 +186,7 @@ class ProfilerEventsProcessor : public base::Thread {
unsigned last_processed_code_event_id_;
};
class CpuProfiler : public CodeEventListener {
class CpuProfiler : public CodeEventObserver {
public:
explicit CpuProfiler(Isolate* isolate);
@ -204,34 +207,12 @@ class CpuProfiler : public CodeEventListener {
void DeleteAllProfiles();
void DeleteProfile(CpuProfile* profile);
void CodeEventHandler(const CodeEventsContainer& evt_rec) override;
// Invoked from stack sampler (thread or signal handler.)
inline TickSample* StartTickSample();
inline void FinishTickSample();
// Must be called via PROFILE macro, otherwise will crash when
// profiling is not enabled.
void CallbackEvent(Name* name, Address entry_point) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
const char* comment) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
Name* name) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
SharedFunctionInfo* shared, Name* script_name) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
SharedFunctionInfo* shared, Name* script_name, int line,
int column) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
int args_count) override;
void CodeMovingGCEvent() override {}
void CodeMoveEvent(AbstractCode* from, Address to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override;
void CodeDeoptEvent(Code* code, Address pc, int fp_to_sp_delta) override;
void GetterCallbackEvent(Name* name, Address entry_point) override;
void RegExpCodeCreateEvent(AbstractCode* code, String* source) override;
void SetterCallbackEvent(Name* name, Address entry_point) override;
void SharedFunctionInfoMoveEvent(Address from, Address to) override {}
bool is_profiling() const { return is_profiling_; }
ProfileGenerator* generator() const { return generator_.get(); }
@ -244,9 +225,6 @@ class CpuProfiler : public CodeEventListener {
void StopProcessor();
void ResetProfiles();
void LogBuiltins();
void RecordInliningInfo(CodeEntry* entry, AbstractCode* abstract_code);
void RecordDeoptInlinedFrames(CodeEntry* entry, AbstractCode* abstract_code);
Name* InferScriptName(Name* name, SharedFunctionInfo* info);
Isolate* const isolate_;
base::TimeDelta sampling_interval_;

View File

@ -48,6 +48,41 @@ const char* const CodeEntry::kEmptyResourceName = "";
const char* const CodeEntry::kEmptyBailoutReason = "";
const char* const CodeEntry::kNoDeoptReason = "";
const char* const CodeEntry::kProgramEntryName = "(program)";
const char* const CodeEntry::kIdleEntryName = "(idle)";
const char* const CodeEntry::kGarbageCollectorEntryName = "(garbage collector)";
const char* const CodeEntry::kUnresolvedFunctionName = "(unresolved function)";
base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type
CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type
CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type
CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
base::LazyDynamicInstance<CodeEntry,
CodeEntry::UnresolvedEntryCreateTrait>::type
CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() {
return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName);
}
CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() {
return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName);
}
CodeEntry* CodeEntry::GCEntryCreateTrait::Create() {
return new CodeEntry(Logger::BUILTIN_TAG,
CodeEntry::kGarbageCollectorEntryName);
}
CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() {
return new CodeEntry(Logger::FUNCTION_TAG,
CodeEntry::kUnresolvedFunctionName);
}
CodeEntry::~CodeEntry() {
delete line_info_;
@ -433,15 +468,10 @@ void CodeMap::Print() {
}
CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
: function_and_resource_names_(isolate->heap()),
: resource_names_(isolate->heap()),
profiler_(nullptr),
current_profiles_semaphore_(1) {}
static void DeleteCodeEntry(CodeEntry** entry_ptr) {
delete *entry_ptr;
}
static void DeleteCpuProfile(CpuProfile** profile_ptr) {
delete *profile_ptr;
}
@ -450,7 +480,6 @@ static void DeleteCpuProfile(CpuProfile** profile_ptr) {
CpuProfilesCollection::~CpuProfilesCollection() {
finished_profiles_.Iterate(DeleteCpuProfile);
current_profiles_.Iterate(DeleteCpuProfile);
code_entries_.Iterate(DeleteCodeEntry);
}
@ -527,37 +556,8 @@ void CpuProfilesCollection::AddPathToCurrentProfiles(
current_profiles_semaphore_.Signal();
}
CodeEntry* CpuProfilesCollection::NewCodeEntry(
CodeEventListener::LogEventsAndTags tag, const char* name,
const char* name_prefix, const char* resource_name, int line_number,
int column_number, JITLineInfoTable* line_info, Address instruction_start) {
CodeEntry* code_entry =
new CodeEntry(tag, name, name_prefix, resource_name, line_number,
column_number, line_info, instruction_start);
code_entries_.Add(code_entry);
return code_entry;
}
const char* const ProfileGenerator::kProgramEntryName =
"(program)";
const char* const ProfileGenerator::kIdleEntryName =
"(idle)";
const char* const ProfileGenerator::kGarbageCollectorEntryName =
"(garbage collector)";
const char* const ProfileGenerator::kUnresolvedFunctionName =
"(unresolved function)";
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
: profiles_(profiles),
program_entry_(profiles->NewCodeEntry(CodeEventListener::FUNCTION_TAG,
kProgramEntryName)),
idle_entry_(profiles->NewCodeEntry(CodeEventListener::FUNCTION_TAG,
kIdleEntryName)),
gc_entry_(profiles->NewCodeEntry(CodeEventListener::BUILTIN_TAG,
kGarbageCollectorEntryName)),
unresolved_entry_(profiles->NewCodeEntry(CodeEventListener::FUNCTION_TAG,
kUnresolvedFunctionName)) {}
: profiles_(profiles) {}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
std::vector<CodeEntry*> entries;
@ -610,7 +610,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// former case we don't so we simply replace the frame with
// 'unresolved' entry.
if (!sample.has_external_callback) {
entries.push_back(unresolved_entry_);
entries.push_back(CodeEntry::unresolved_entry());
}
}
}
@ -667,7 +667,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
switch (tag) {
case GC:
return gc_entry_;
return CodeEntry::gc_entry();
case JS:
case COMPILER:
// DOM events handlers are reported as OTHER / EXTERNAL entries.
@ -675,9 +675,9 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
// one bucket.
case OTHER:
case EXTERNAL:
return program_entry_;
return CodeEntry::program_entry();
case IDLE:
return idle_entry_;
return CodeEntry::idle_entry();
default: return NULL;
}
}

View File

@ -114,7 +114,45 @@ class CodeEntry {
static const char* const kEmptyBailoutReason;
static const char* const kNoDeoptReason;
static const char* const kProgramEntryName;
static const char* const kIdleEntryName;
static const char* const kGarbageCollectorEntryName;
// Used to represent frames for which we have no reliable way to
// detect function.
static const char* const kUnresolvedFunctionName;
V8_INLINE static CodeEntry* program_entry() {
return kProgramEntry.Pointer();
}
V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); }
V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); }
V8_INLINE static CodeEntry* unresolved_entry() {
return kUnresolvedEntry.Pointer();
}
private:
struct ProgramEntryCreateTrait {
static CodeEntry* Create();
};
struct IdleEntryCreateTrait {
static CodeEntry* Create();
};
struct GCEntryCreateTrait {
static CodeEntry* Create();
};
struct UnresolvedEntryCreateTrait {
static CodeEntry* Create();
};
static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type
kProgramEntry;
static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type
kIdleEntry;
static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type
kGCEntry;
static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type
kUnresolvedEntry;
class TagField : public BitField<Logger::LogEventsAndTags, 0, 8> {};
class BuiltinIdField : public BitField<Builtins::Name, 8, 24> {};
@ -300,29 +338,10 @@ class CpuProfilesCollection {
bool StartProfiling(const char* title, bool record_samples);
CpuProfile* StopProfiling(const char* title);
List<CpuProfile*>* profiles() { return &finished_profiles_; }
const char* GetName(Name* name) {
return function_and_resource_names_.GetName(name);
}
const char* GetName(int args_count) {
return function_and_resource_names_.GetName(args_count);
}
const char* GetFunctionName(Name* name) {
return function_and_resource_names_.GetFunctionName(name);
}
const char* GetFunctionName(const char* name) {
return function_and_resource_names_.GetFunctionName(name);
}
const char* GetName(Name* name) { return resource_names_.GetName(name); }
bool IsLastProfile(const char* title);
void RemoveProfile(CpuProfile* profile);
CodeEntry* NewCodeEntry(
CodeEventListener::LogEventsAndTags tag, const char* name,
const char* name_prefix = CodeEntry::kEmptyNamePrefix,
const char* resource_name = CodeEntry::kEmptyResourceName,
int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
JITLineInfoTable* line_info = NULL, Address instruction_start = NULL);
// Called from profile generator thread.
void AddPathToCurrentProfiles(base::TimeTicks timestamp,
const std::vector<CodeEntry*>& path,
@ -332,8 +351,7 @@ class CpuProfilesCollection {
static const int kMaxSimultaneousProfiles = 100;
private:
StringsStorage function_and_resource_names_;
List<CodeEntry*> code_entries_;
StringsStorage resource_names_;
List<CpuProfile*> finished_profiles_;
CpuProfiler* profiler_;
@ -353,22 +371,11 @@ class ProfileGenerator {
CodeMap* code_map() { return &code_map_; }
static const char* const kProgramEntryName;
static const char* const kIdleEntryName;
static const char* const kGarbageCollectorEntryName;
// Used to represent frames for which we have no reliable way to
// detect function.
static const char* const kUnresolvedFunctionName;
private:
CodeEntry* EntryForVMState(StateTag tag);
CpuProfilesCollection* profiles_;
CodeMap code_map_;
CodeEntry* program_entry_;
CodeEntry* idle_entry_;
CodeEntry* gc_entry_;
CodeEntry* unresolved_entry_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};

View File

@ -0,0 +1,339 @@
// Copyright 2016 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/profiler-listener.h"
#include "src/deoptimizer.h"
#include "src/interpreter/source-position-table.h"
#include "src/profiler/cpu-profiler.h"
#include "src/profiler/profile-generator-inl.h"
namespace v8 {
namespace internal {
ProfilerListener::ProfilerListener(Isolate* isolate)
: function_and_resource_names_(isolate->heap()) {}
ProfilerListener::~ProfilerListener() {
for (auto code_entry : code_entries_) {
delete code_entry;
}
}
void ProfilerListener::CallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
rec->size = 1;
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, const char* name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = NewCodeEntry(
tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, Name* name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = NewCodeEntry(
tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code,
SharedFunctionInfo* shared,
Name* script_name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = NewCodeEntry(
tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
GetName(InferScriptName(script_name, shared)),
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->entry->FillFunctionInfo(shared);
rec->size = code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* abstract_code,
SharedFunctionInfo* shared,
Name* script_name, int line,
int column) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = abstract_code->address();
Script* script = Script::cast(shared->script());
JITLineInfoTable* line_table = NULL;
if (script) {
if (abstract_code->IsCode()) {
Code* code = abstract_code->GetCode();
int start_position = shared->start_position();
int end_position = shared->end_position();
line_table = new JITLineInfoTable();
for (RelocIterator it(code); !it.done(); it.next()) {
RelocInfo* reloc_info = it.rinfo();
if (!RelocInfo::IsPosition(reloc_info->rmode())) continue;
int position = static_cast<int>(reloc_info->data());
// TODO(alph): in case of inlining the position may correspond
// to an inlined function source code. Do not collect positions
// that fall beyond the function source code. There's however a
// chance the inlined function has similar positions but in another
// script. So the proper fix is to store script_id in some form
// along with the inlined function positions.
if (position < start_position || position >= end_position) continue;
int pc_offset = static_cast<int>(reloc_info->pc() - code->address());
int line_number = script->GetLineNumber(position) + 1;
line_table->SetPosition(pc_offset, line_number);
}
} else {
BytecodeArray* bytecode = abstract_code->GetBytecodeArray();
line_table = new JITLineInfoTable();
interpreter::SourcePositionTableIterator it(
bytecode->source_position_table());
for (; !it.done(); it.Advance()) {
int line_number = script->GetLineNumber(it.source_position()) + 1;
int pc_offset = it.bytecode_offset() + BytecodeArray::kHeaderSize;
line_table->SetPosition(pc_offset, line_number);
}
}
}
rec->entry = NewCodeEntry(
tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
GetName(InferScriptName(script_name, shared)), line, column, line_table,
abstract_code->instruction_start());
RecordInliningInfo(rec->entry, abstract_code);
RecordDeoptInlinedFrames(rec->entry, abstract_code);
rec->entry->FillFunctionInfo(shared);
rec->size = abstract_code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, int args_count) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = NewCodeEntry(
tag, GetName(args_count), "args_count: ", CodeEntry::kEmptyResourceName,
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
NULL, code->instruction_start());
RecordInliningInfo(rec->entry, code);
rec->size = code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeMoveEvent(AbstractCode* from, Address to) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
rec->from = from->address();
rec->to = to;
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
rec->start = code->address();
rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::CodeDeoptEvent(Code* code, Address pc,
int fp_to_sp_delta) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
rec->start = code->address();
rec->deopt_reason = Deoptimizer::GetDeoptReason(info.deopt_reason);
rec->position = info.position;
rec->deopt_id = info.deopt_id;
rec->pc = reinterpret_cast<void*>(pc);
rec->fp_to_sp_delta = fp_to_sp_delta;
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::GetterCallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry =
NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "get ");
rec->size = 1;
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::RegExpCodeCreateEvent(AbstractCode* code,
String* source) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = code->address();
rec->entry = NewCodeEntry(
CodeEventListener::REG_EXP_TAG, GetName(source), "RegExp: ",
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
rec->size = code->ExecutableSize();
DispatchCodeEvent(evt_rec);
}
void ProfilerListener::SetterCallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
rec->start = entry_point;
rec->entry =
NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "set ");
rec->size = 1;
DispatchCodeEvent(evt_rec);
}
Name* ProfilerListener::InferScriptName(Name* name, SharedFunctionInfo* info) {
if (name->IsString() && String::cast(name)->length()) return name;
if (!info->script()->IsScript()) return name;
Object* source_url = Script::cast(info->script())->source_url();
return source_url->IsName() ? Name::cast(source_url) : name;
}
void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
AbstractCode* abstract_code) {
if (!abstract_code->IsCode()) return;
Code* code = abstract_code->GetCode();
if (code->kind() != Code::OPTIMIZED_FUNCTION) return;
DeoptimizationInputData* deopt_input_data =
DeoptimizationInputData::cast(code->deoptimization_data());
int deopt_count = deopt_input_data->DeoptCount();
for (int i = 0; i < deopt_count; i++) {
int pc_offset = deopt_input_data->Pc(i)->value();
if (pc_offset == -1) continue;
int translation_index = deopt_input_data->TranslationIndex(i)->value();
TranslationIterator it(deopt_input_data->TranslationByteArray(),
translation_index);
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
DCHECK_EQ(Translation::BEGIN, opcode);
it.Skip(Translation::NumberOfOperandsFor(opcode));
int depth = 0;
std::vector<CodeEntry*> inline_stack;
while (it.HasNext() &&
Translation::BEGIN !=
(opcode = static_cast<Translation::Opcode>(it.Next()))) {
if (opcode != Translation::JS_FRAME &&
opcode != Translation::INTERPRETED_FRAME) {
it.Skip(Translation::NumberOfOperandsFor(opcode));
continue;
}
it.Next(); // Skip ast_id
int shared_info_id = it.Next();
it.Next(); // Skip height
SharedFunctionInfo* shared_info = SharedFunctionInfo::cast(
deopt_input_data->LiteralArray()->get(shared_info_id));
if (!depth++) continue; // Skip the current function itself.
CodeEntry* inline_entry = new CodeEntry(
entry->tag(), GetFunctionName(shared_info->DebugName()),
CodeEntry::kEmptyNamePrefix, entry->resource_name(),
CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
inline_entry->FillFunctionInfo(shared_info);
inline_stack.push_back(inline_entry);
}
if (!inline_stack.empty()) {
entry->AddInlineStack(pc_offset, inline_stack);
DCHECK(inline_stack.empty());
}
}
}
void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
AbstractCode* abstract_code) {
if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
Code* code = abstract_code->GetCode();
DeoptimizationInputData* deopt_input_data =
DeoptimizationInputData::cast(code->deoptimization_data());
int const mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID);
for (RelocIterator rit(code, mask); !rit.done(); rit.next()) {
RelocInfo* reloc_info = rit.rinfo();
DCHECK(RelocInfo::IsDeoptId(reloc_info->rmode()));
int deopt_id = static_cast<int>(reloc_info->data());
int translation_index =
deopt_input_data->TranslationIndex(deopt_id)->value();
TranslationIterator it(deopt_input_data->TranslationByteArray(),
translation_index);
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
DCHECK_EQ(Translation::BEGIN, opcode);
it.Skip(Translation::NumberOfOperandsFor(opcode));
std::vector<CodeEntry::DeoptInlinedFrame> inlined_frames;
while (it.HasNext() &&
Translation::BEGIN !=
(opcode = static_cast<Translation::Opcode>(it.Next()))) {
if (opcode != Translation::JS_FRAME &&
opcode != Translation::INTERPRETED_FRAME) {
it.Skip(Translation::NumberOfOperandsFor(opcode));
continue;
}
BailoutId ast_id = BailoutId(it.Next());
int shared_info_id = it.Next();
it.Next(); // Skip height
SharedFunctionInfo* shared = SharedFunctionInfo::cast(
deopt_input_data->LiteralArray()->get(shared_info_id));
int source_position = Deoptimizer::ComputeSourcePosition(shared, ast_id);
int script_id = v8::UnboundScript::kNoScriptId;
if (shared->script()->IsScript()) {
Script* script = Script::cast(shared->script());
script_id = script->id();
}
CodeEntry::DeoptInlinedFrame frame = {source_position, script_id};
inlined_frames.push_back(frame);
}
if (!inlined_frames.empty() && !entry->HasDeoptInlinedFramesFor(deopt_id)) {
entry->AddDeoptInlinedFrames(deopt_id, inlined_frames);
DCHECK(inlined_frames.empty());
}
}
}
CodeEntry* ProfilerListener::NewCodeEntry(
CodeEventListener::LogEventsAndTags tag, const char* name,
const char* name_prefix, const char* resource_name, int line_number,
int column_number, JITLineInfoTable* line_info, Address instruction_start) {
CodeEntry* code_entry =
new CodeEntry(tag, name, name_prefix, resource_name, line_number,
column_number, line_info, instruction_start);
code_entries_.push_back(code_entry);
return code_entry;
}
void ProfilerListener::AddObserver(CodeEventObserver* observer) {
if (std::find(observers_.begin(), observers_.end(), observer) !=
observers_.end())
return;
observers_.push_back(observer);
}
void ProfilerListener::RemoveObserver(CodeEventObserver* observer) {
auto it = std::find(observers_.begin(), observers_.end(), observer);
if (it == observers_.end()) return;
observers_.erase(it);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,97 @@
// Copyright 2016 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_PROFILER_LISTENER_H_
#define V8_PROFILER_PROFILER_LISTENER_H_
#include <vector>
#include "src/code-events.h"
#include "src/profiler/profile-generator.h"
namespace v8 {
namespace internal {
class CodeEventsContainer;
class CodeEventObserver {
public:
virtual void CodeEventHandler(const CodeEventsContainer& evt_rec) = 0;
virtual ~CodeEventObserver() {}
};
class ProfilerListener : public CodeEventListener {
public:
explicit ProfilerListener(Isolate* isolate);
~ProfilerListener() override;
void CallbackEvent(Name* name, Address entry_point) override;
void CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, const char* comment) override;
void CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, Name* name) override;
void CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, SharedFunctionInfo* shared,
Name* script_name) override;
void CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, SharedFunctionInfo* shared,
Name* script_name, int line, int column) override;
void CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, int args_count) override;
void CodeMovingGCEvent() override {}
void CodeMoveEvent(AbstractCode* from, Address to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override;
void CodeDeoptEvent(Code* code, Address pc, int fp_to_sp_delta) override;
void GetterCallbackEvent(Name* name, Address entry_point) override;
void RegExpCodeCreateEvent(AbstractCode* code, String* source) override;
void SetterCallbackEvent(Name* name, Address entry_point) override;
void SharedFunctionInfoMoveEvent(Address from, Address to) override {}
CodeEntry* NewCodeEntry(
CodeEventListener::LogEventsAndTags tag, const char* name,
const char* name_prefix = CodeEntry::kEmptyNamePrefix,
const char* resource_name = CodeEntry::kEmptyResourceName,
int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
JITLineInfoTable* line_info = NULL, Address instruction_start = NULL);
void AddObserver(CodeEventObserver* observer);
void RemoveObserver(CodeEventObserver* observer);
V8_INLINE bool HasObservers() { return !observers_.empty(); }
const char* GetName(Name* name) {
return function_and_resource_names_.GetName(name);
}
const char* GetName(int args_count) {
return function_and_resource_names_.GetName(args_count);
}
const char* GetFunctionName(Name* name) {
return function_and_resource_names_.GetFunctionName(name);
}
const char* GetFunctionName(const char* name) {
return function_and_resource_names_.GetFunctionName(name);
}
private:
void RecordInliningInfo(CodeEntry* entry, AbstractCode* abstract_code);
void RecordDeoptInlinedFrames(CodeEntry* entry, AbstractCode* abstract_code);
Name* InferScriptName(Name* name, SharedFunctionInfo* info);
V8_INLINE void DispatchCodeEvent(const CodeEventsContainer& evt_rec) {
for (auto observer : observers_) {
observer->CodeEventHandler(evt_rec);
}
}
StringsStorage function_and_resource_names_;
std::vector<CodeEntry*> code_entries_;
std::vector<CodeEventObserver*> observers_;
DISALLOW_COPY_AND_ASSIGN(ProfilerListener);
};
} // namespace internal
} // namespace v8
#endif // V8_PROFILER_PROFILER_LISTENER_H_

View File

@ -1004,6 +1004,8 @@
'profiler/heap-snapshot-generator-inl.h',
'profiler/heap-snapshot-generator.cc',
'profiler/heap-snapshot-generator.h',
'profiler/profiler-listener.cc',
'profiler/profiler-listener.h',
'profiler/profile-generator-inl.h',
'profiler/profile-generator.cc',
'profiler/profile-generator.h',

View File

@ -33,6 +33,7 @@
#include "src/base/platform/platform.h"
#include "src/deoptimizer.h"
#include "src/profiler/cpu-profiler-inl.h"
#include "src/profiler/profiler-listener.h"
#include "src/utils.h"
#include "test/cctest/cctest.h"
#include "test/cctest/profiler-extension.h"
@ -45,6 +46,7 @@ using i::Heap;
using i::ProfileGenerator;
using i::ProfileNode;
using i::ProfilerEventsProcessor;
using i::ProfilerListener;
using i::ScopedVector;
using i::Vector;
@ -158,24 +160,29 @@ TEST(CodeEvents) {
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
processor->Start();
ProfilerListener profiler_listener(isolate);
isolate->code_event_dispatcher()->AddListener(&profiler_listener);
profiler_listener.AddObserver(&profiler);
// Enqueue code creation events.
const char* aaa_str = "aaa";
i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
profiler.CodeCreateEvent(i::CodeEventListener::FUNCTION_TAG, aaa_code,
*aaa_name);
profiler.CodeCreateEvent(i::CodeEventListener::BUILTIN_TAG, comment_code,
"comment");
profiler.CodeCreateEvent(i::CodeEventListener::STUB_TAG, args5_code, 5);
profiler.CodeCreateEvent(i::CodeEventListener::BUILTIN_TAG, comment2_code,
"comment2");
profiler.CodeMoveEvent(comment2_code, moved_code->address());
profiler.CodeCreateEvent(i::CodeEventListener::STUB_TAG, args3_code, 3);
profiler.CodeCreateEvent(i::CodeEventListener::STUB_TAG, args4_code, 4);
profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code,
*aaa_name);
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code,
"comment");
profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code,
"comment2");
profiler_listener.CodeMoveEvent(comment2_code, moved_code->address());
profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
// Enqueue a tick event to enable code events processing.
EnqueueTickSampleEvent(processor, aaa_code->address());
profiler_listener.RemoveObserver(&profiler);
isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
processor->StopSynchronously();
// Check the state of profile generator.
@ -221,12 +228,13 @@ TEST(TickEvents) {
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
processor->Start();
ProfilerListener profiler_listener(isolate);
isolate->code_event_dispatcher()->AddListener(&profiler_listener);
profiler_listener.AddObserver(&profiler);
profiler.CodeCreateEvent(i::CodeEventListener::BUILTIN_TAG, frame1_code,
"bbb");
profiler.CodeCreateEvent(i::CodeEventListener::STUB_TAG, frame2_code, 5);
profiler.CodeCreateEvent(i::CodeEventListener::BUILTIN_TAG, frame3_code,
"ddd");
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, 5);
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
EnqueueTickSampleEvent(processor, frame1_code->instruction_start());
EnqueueTickSampleEvent(
@ -237,6 +245,8 @@ TEST(TickEvents) {
frame2_code->instruction_end() - 1,
frame1_code->instruction_end() - 1);
profiler_listener.RemoveObserver(&profiler);
isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
processor->StopSynchronously();
CpuProfile* profile = profiles->StopProfiling("");
CHECK(profile);
@ -257,6 +267,8 @@ TEST(TickEvents) {
const i::List<ProfileNode*>* top_down_ddd_children =
top_down_stub_children->last()->children();
CHECK_EQ(0, top_down_ddd_children->length());
isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
}
// http://crbug/51594
@ -288,8 +300,11 @@ TEST(Issue1398) {
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
processor->Start();
ProfilerListener profiler_listener(isolate);
isolate->code_event_dispatcher()->AddListener(&profiler_listener);
profiler_listener.AddObserver(&profiler);
profiler.CodeCreateEvent(i::CodeEventListener::BUILTIN_TAG, code, "bbb");
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
i::TickSample* sample = processor->StartTickSample();
sample->pc = code->address();
@ -300,6 +315,8 @@ TEST(Issue1398) {
}
processor->FinishTickSample();
profiler_listener.RemoveObserver(&profiler);
isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
processor->StopSynchronously();
CpuProfile* profile = profiles->StopProfiling("");
CHECK(profile);
@ -1026,17 +1043,22 @@ static void TickLines(bool optimize) {
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
processor->Start();
ProfilerListener profiler_listener(isolate);
isolate->code_event_dispatcher()->AddListener(&profiler_listener);
profiler_listener.AddObserver(&profiler);
// Enqueue code creation events.
i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
int line = 1;
int column = 1;
profiler.CodeCreateEvent(i::CodeEventListener::FUNCTION_TAG, code,
func->shared(), *str, line, column);
profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, code,
func->shared(), *str, line, column);
// Enqueue a tick event to enable code events processing.
EnqueueTickSampleEvent(processor, code_address);
profiler_listener.RemoveObserver(&profiler);
isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
processor->StopSynchronously();
CpuProfile* profile = profiles->StopProfiling("");
@ -1129,8 +1151,8 @@ TEST(FunctionCallSample) {
const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
GetChild(env.local(), start_node, "bar");
const v8::CpuProfileNode* unresolved_node = FindChild(
env.local(), root, i::ProfileGenerator::kUnresolvedFunctionName);
const v8::CpuProfileNode* unresolved_node =
FindChild(env.local(), root, i::CodeEntry::kUnresolvedFunctionName);
CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "call"));
profile->Delete();
@ -1185,8 +1207,8 @@ TEST(FunctionApplySample) {
GetChild(env.local(), start_node, "test");
GetChild(env.local(), test_node, "bar");
const v8::CpuProfileNode* unresolved_node = FindChild(
env.local(), start_node, ProfileGenerator::kUnresolvedFunctionName);
const v8::CpuProfileNode* unresolved_node =
FindChild(env.local(), start_node, CodeEntry::kUnresolvedFunctionName);
CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "apply"));
profile->Delete();
@ -1593,12 +1615,12 @@ TEST(IdleTime) {
const v8::CpuProfileNode* root = profile->GetTopDownRoot();
const v8::CpuProfileNode* program_node =
GetChild(env.local(), root, ProfileGenerator::kProgramEntryName);
GetChild(env.local(), root, CodeEntry::kProgramEntryName);
CHECK_EQ(0, program_node->GetChildrenCount());
CHECK_GE(program_node->GetHitCount(), 2u);
const v8::CpuProfileNode* idle_node =
GetChild(env.local(), root, ProfileGenerator::kIdleEntryName);
GetChild(env.local(), root, CodeEntry::kIdleEntryName);
CHECK_EQ(0, idle_node->GetChildrenCount());
CHECK_GE(idle_node->GetHitCount(), 3u);

View File

@ -348,12 +348,9 @@ TEST(RecordTickSample) {
profiles.set_cpu_profiler(CcTest::i_isolate()->cpu_profiler());
profiles.StartProfiling("", false);
ProfileGenerator generator(&profiles);
CodeEntry* entry1 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
CodeEntry* entry2 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "bbb");
CodeEntry* entry3 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
CodeEntry* entry2 = new CodeEntry(i::Logger::FUNCTION_TAG, "bbb");
CodeEntry* entry3 = new CodeEntry(i::Logger::FUNCTION_TAG, "ccc");
generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
@ -401,6 +398,10 @@ TEST(RecordTickSample) {
ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1);
CHECK(node4);
CHECK_EQ(entry1, node4->entry());
delete entry1;
delete entry2;
delete entry3;
}
@ -418,12 +419,9 @@ TEST(SampleIds) {
profiles.set_cpu_profiler(CcTest::i_isolate()->cpu_profiler());
profiles.StartProfiling("", true);
ProfileGenerator generator(&profiles);
CodeEntry* entry1 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
CodeEntry* entry2 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "bbb");
CodeEntry* entry3 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
CodeEntry* entry2 = new CodeEntry(i::Logger::FUNCTION_TAG, "bbb");
CodeEntry* entry3 = new CodeEntry(i::Logger::FUNCTION_TAG, "ccc");
generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
@ -464,6 +462,10 @@ TEST(SampleIds) {
for (int i = 0; i < 3; i++) {
CHECK_EQ(expected_id[i], profile->sample(i)->id());
}
delete entry1;
delete entry2;
delete entry3;
}
@ -473,8 +475,7 @@ TEST(NoSamples) {
profiles.set_cpu_profiler(CcTest::i_isolate()->cpu_profiler());
profiles.StartProfiling("", false);
ProfileGenerator generator(&profiles);
CodeEntry* entry1 =
profiles.NewCodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
// We are building the following calls tree:
@ -491,6 +492,8 @@ TEST(NoSamples) {
CHECK_EQ(3u, nodeId - 1);
CHECK_EQ(0, profile->samples_count());
delete entry1;
}