Commit aa6ce3ee authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

[log][api] introduce public CodeEventListener API

Introduce a new public API called CodeEventListener to allow embedders
to better support external profilers and other diagnostic tools without
relying on unsupported methods like --perf-basic-prof.

Bug: v8:7694
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I063cc965394d59401358757634c9ea84c11517e9
Co-authored-by: 's avatarDaniel Beckert <daniel@sthima.com.br>
Reviewed-on: https://chromium-review.googlesource.com/1028770
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53382}
parent 7633479c
......@@ -1009,6 +1009,76 @@ struct HeapStatsUpdate {
uint32_t size; // New value of size field for the interval with this index.
};
#define CODE_EVENTS_LIST(V) \
V(Builtin) \
V(Callback) \
V(Eval) \
V(Function) \
V(InterpretedFunction) \
V(Handler) \
V(BytecodeHandler) \
V(LazyCompile) \
V(RegExp) \
V(Script) \
V(Stub)
/**
* Note that this enum may be extended in the future. Please include a default
* case if this enum is used in a switch statement.
*/
enum CodeEventType {
kUnknownType = 0
#define V(Name) , k##Name##Type
CODE_EVENTS_LIST(V)
#undef V
};
/**
* Representation of a code creation event
*/
class V8_EXPORT CodeEvent {
public:
uintptr_t GetCodeStartAddress();
size_t GetCodeSize();
Local<String> GetFunctionName();
Local<String> GetScriptName();
int GetScriptLine();
int GetScriptColumn();
/**
* NOTE (mmarchini): We can't allocate objects in the heap when we collect
* existing code, and both the code type and the comment are not stored in the
* heap, so we return those as const char*.
*/
CodeEventType GetCodeType();
const char* GetComment();
static const char* GetCodeEventTypeName(CodeEventType code_event_type);
};
/**
* Interface to listen to code creation events.
*/
class V8_EXPORT CodeEventHandler {
public:
/**
* Creates a new listener for the |isolate|. The isolate must be initialized.
* The listener object must be disposed after use by calling |Dispose| method.
* Multiple listeners can be created for the same isolate.
*/
explicit CodeEventHandler(Isolate* isolate);
virtual ~CodeEventHandler();
virtual void Handle(CodeEvent* code_event) = 0;
void Enable();
void Disable();
private:
CodeEventHandler();
CodeEventHandler(const CodeEventHandler&);
CodeEventHandler& operator=(const CodeEventHandler&);
void* internal_listener_;
};
} // namespace v8
......
......@@ -10162,6 +10162,70 @@ void CpuProfiler::SetIdle(bool is_idle) {
isolate->SetIdle(is_idle);
}
uintptr_t CodeEvent::GetCodeStartAddress() {
return reinterpret_cast<i::CodeEvent*>(this)->code_start_address;
}
size_t CodeEvent::GetCodeSize() {
return reinterpret_cast<i::CodeEvent*>(this)->code_size;
}
Local<String> CodeEvent::GetFunctionName() {
return ToApiHandle<String>(
reinterpret_cast<i::CodeEvent*>(this)->function_name);
}
Local<String> CodeEvent::GetScriptName() {
return ToApiHandle<String>(
reinterpret_cast<i::CodeEvent*>(this)->script_name);
}
int CodeEvent::GetScriptLine() {
return reinterpret_cast<i::CodeEvent*>(this)->script_line;
}
int CodeEvent::GetScriptColumn() {
return reinterpret_cast<i::CodeEvent*>(this)->script_column;
}
CodeEventType CodeEvent::GetCodeType() {
return reinterpret_cast<i::CodeEvent*>(this)->code_type;
}
const char* CodeEvent::GetComment() {
return reinterpret_cast<i::CodeEvent*>(this)->comment;
}
const char* CodeEvent::GetCodeEventTypeName(CodeEventType code_event_type) {
switch (code_event_type) {
case kUnknownType:
return "Unknown";
#define V(Name) \
case k##Name##Type: \
return #Name;
CODE_EVENTS_LIST(V)
#undef V
}
}
CodeEventHandler::CodeEventHandler(Isolate* isolate) {
internal_listener_ =
new i::ExternalCodeEventListener(reinterpret_cast<i::Isolate*>(isolate));
}
CodeEventHandler::~CodeEventHandler() {
delete reinterpret_cast<i::ExternalCodeEventListener*>(internal_listener_);
}
void CodeEventHandler::Enable() {
reinterpret_cast<i::ExternalCodeEventListener*>(internal_listener_)
->StartListening(this);
}
void CodeEventHandler::Disable() {
reinterpret_cast<i::ExternalCodeEventListener*>(internal_listener_)
->StopListening();
}
static i::HeapGraphEdge* ToInternal(const HeapGraphEdge* edge) {
return const_cast<i::HeapGraphEdge*>(
......
......@@ -24,32 +24,38 @@ class WasmCode;
using WasmName = Vector<const char>;
} // namespace wasm
#define LOG_EVENTS_AND_TAGS_LIST(V) \
V(CODE_CREATION_EVENT, "code-creation") \
V(CODE_DISABLE_OPT_EVENT, "code-disable-optimization") \
V(CODE_MOVE_EVENT, "code-move") \
V(CODE_DELETE_EVENT, "code-delete") \
V(CODE_MOVING_GC, "code-moving-gc") \
V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
V(SNAPSHOT_CODE_NAME_EVENT, "snapshot-code-name") \
V(TICK_EVENT, "tick") \
V(BUILTIN_TAG, "Builtin") \
V(CALLBACK_TAG, "Callback") \
V(EVAL_TAG, "Eval") \
V(FUNCTION_TAG, "Function") \
V(INTERPRETED_FUNCTION_TAG, "InterpretedFunction") \
V(HANDLER_TAG, "Handler") \
V(BYTECODE_HANDLER_TAG, "BytecodeHandler") \
V(LAZY_COMPILE_TAG, "LazyCompile") \
V(REG_EXP_TAG, "RegExp") \
V(SCRIPT_TAG, "Script") \
V(STUB_TAG, "Stub") \
V(NATIVE_FUNCTION_TAG, "Function") \
V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile") \
V(NATIVE_SCRIPT_TAG, "Script")
#define LOG_EVENTS_LIST(V) \
V(CODE_CREATION_EVENT, code-creation) \
V(CODE_DISABLE_OPT_EVENT, code-disable-optimization) \
V(CODE_MOVE_EVENT, code-move) \
V(CODE_DELETE_EVENT, code-delete) \
V(CODE_MOVING_GC, code-moving-gc) \
V(SHARED_FUNC_MOVE_EVENT, sfi-move) \
V(SNAPSHOT_CODE_NAME_EVENT, snapshot-code-name) \
V(TICK_EVENT, tick)
#define TAGS_LIST(V) \
V(BUILTIN_TAG, Builtin) \
V(CALLBACK_TAG, Callback) \
V(EVAL_TAG, Eval) \
V(FUNCTION_TAG, Function) \
V(INTERPRETED_FUNCTION_TAG, InterpretedFunction) \
V(HANDLER_TAG, Handler) \
V(BYTECODE_HANDLER_TAG, BytecodeHandler) \
V(LAZY_COMPILE_TAG, LazyCompile) \
V(REG_EXP_TAG, RegExp) \
V(SCRIPT_TAG, Script) \
V(STUB_TAG, Stub) \
V(NATIVE_FUNCTION_TAG, Function) \
V(NATIVE_LAZY_COMPILE_TAG, LazyCompile) \
V(NATIVE_SCRIPT_TAG, Script)
// Note that 'NATIVE_' cases for functions and scripts are mapped onto
// original tags when writing to the log.
#define LOG_EVENTS_AND_TAGS_LIST(V) \
LOG_EVENTS_LIST(V) \
TAGS_LIST(V)
#define PROFILE(the_isolate, Call) (the_isolate)->code_event_dispatcher()->Call;
class CodeEventListener {
......@@ -85,6 +91,8 @@ class CodeEventListener {
enum DeoptKind { kSoft, kLazy, kEager };
virtual void CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
int fp_to_sp_delta) = 0;
virtual bool is_listening_to_code_events() { return false; }
};
class CodeEventDispatcher {
......@@ -101,6 +109,14 @@ class CodeEventDispatcher {
base::LockGuard<base::Mutex> guard(&mutex_);
listeners_.erase(listener);
}
bool IsListeningToCodeEvents() {
for (auto it : listeners_) {
if (it->is_listening_to_code_events()) {
return true;
}
}
return false;
}
#define CODE_EVENT_DISPATCH(code) \
base::LockGuard<base::Mutex> guard(&mutex_); \
......
......@@ -84,8 +84,9 @@ void LogFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
// Log the code generation. If source information is available include
// script name and line number. Check explicitly whether logging is
// enabled as finding the line number is not free.
if (!isolate->logger()->is_logging_code_events() &&
!isolate->is_profiling() && !FLAG_log_function_events) {
if (!isolate->logger()->is_listening_to_code_events() &&
!isolate->is_profiling() && !FLAG_log_function_events &&
!isolate->code_event_dispatcher()->IsListeningToCodeEvents()) {
return;
}
......
......@@ -3948,7 +3948,8 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
namespace {
bool must_record_function_compilation(Isolate* isolate) {
return isolate->logger()->is_logging_code_events() || isolate->is_profiling();
return isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling();
}
PRINTF_FORMAT(4, 5)
......
......@@ -2414,7 +2414,7 @@ void MarkCompactCollectorBase::CreateAndExecuteEvacuationTasks(
const bool profiling =
heap()->isolate()->is_profiling() ||
heap()->isolate()->logger()->is_logging_code_events() ||
heap()->isolate()->logger()->is_listening_to_code_events() ||
heap()->isolate()->heap_profiler()->is_tracking_object_moves() ||
heap()->has_heap_object_allocation_tracker();
ProfilingMigrationObserver profiling_observer(heap());
......
......@@ -2893,7 +2893,7 @@ void CreateOffHeapTrampolines(Isolate* isolate) {
// thus collected by the GC.
builtins->set_builtin(i, *trampoline);
if (isolate->logger()->is_logging_code_events() ||
if (isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) {
isolate->logger()->LogCodeObject(*trampoline);
}
......
......@@ -57,6 +57,7 @@ class BuiltinsConstantsTableBuilder;
class CallInterfaceDescriptorData;
class CancelableTaskManager;
class CodeEventDispatcher;
class ExternalCodeEventListener;
class CodeGenerator;
class CodeRange;
class CodeStubDescriptor;
......
......@@ -40,11 +40,33 @@
namespace v8 {
namespace internal {
#define DECLARE_EVENT(ignore1, name) name,
#define DECLARE_EVENT(ignore1, name) #name,
static const char* kLogEventsNames[CodeEventListener::NUMBER_OF_LOG_EVENTS] = {
LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)};
#undef DECLARE_EVENT
static v8::CodeEventType GetCodeEventTypeForTag(
CodeEventListener::LogEventsAndTags tag) {
switch (tag) {
case CodeEventListener::NUMBER_OF_LOG_EVENTS:
#define V(Event, _) case CodeEventListener::Event:
LOG_EVENTS_LIST(V)
#undef V
return v8::CodeEventType::kUnknownType;
#define V(From, To) \
case CodeEventListener::From: \
return v8::CodeEventType::k##To##Type;
TAGS_LIST(V)
#undef V
}
}
#define CALL_CODE_EVENT_HANDLER(Call) \
if (listener_) { \
listener_->Call; \
} else { \
PROFILE(isolate_, Call); \
}
static const char* ComputeMarker(SharedFunctionInfo* shared,
AbstractCode* code) {
switch (code->kind()) {
......@@ -319,9 +341,147 @@ void PerfBasicLogger::LogRecordedBuffer(const wasm::WasmCode* code,
code->instructions().length(), name, length);
}
// Low-level logging support.
#define LL_LOG(Call) if (ll_logger_) ll_logger_->Call;
// External CodeEventListener
ExternalCodeEventListener::ExternalCodeEventListener(Isolate* isolate)
: is_listening_(false), isolate_(isolate), code_event_handler_(nullptr) {}
ExternalCodeEventListener::~ExternalCodeEventListener() {
if (is_listening_) {
StopListening();
}
}
void ExternalCodeEventListener::LogExistingCode() {
HandleScope scope(isolate_);
ExistingCodeLogger logger(isolate_, this);
logger.LogCodeObjects();
logger.LogBytecodeHandlers();
logger.LogCompiledFunctions();
}
void ExternalCodeEventListener::StartListening(
CodeEventHandler* code_event_handler) {
if (is_listening_ || code_event_handler == nullptr) {
return;
}
code_event_handler_ = code_event_handler;
is_listening_ = isolate_->code_event_dispatcher()->AddListener(this);
if (is_listening_) {
LogExistingCode();
}
}
void ExternalCodeEventListener::StopListening() {
if (!is_listening_) {
return;
}
isolate_->code_event_dispatcher()->RemoveListener(this);
is_listening_ = false;
}
void ExternalCodeEventListener::CodeCreateEvent(
CodeEventListener::LogEventsAndTags tag, AbstractCode* code,
const char* comment) {
CodeEvent code_event;
code_event.code_start_address =
static_cast<uintptr_t>(code->InstructionStart());
code_event.code_size = static_cast<size_t>(code->InstructionSize());
code_event.function_name = isolate_->factory()->empty_string();
code_event.script_name = isolate_->factory()->empty_string();
code_event.script_line = 0;
code_event.script_column = 0;
code_event.code_type = GetCodeEventTypeForTag(tag);
code_event.comment = comment;
code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
}
void ExternalCodeEventListener::CodeCreateEvent(
CodeEventListener::LogEventsAndTags tag, AbstractCode* code, Name* name) {
Handle<String> name_string =
Name::ToFunctionName(Handle<Name>(name, isolate_)).ToHandleChecked();
CodeEvent code_event;
code_event.code_start_address =
static_cast<uintptr_t>(code->InstructionStart());
code_event.code_size = static_cast<size_t>(code->InstructionSize());
code_event.function_name = name_string;
code_event.script_name = isolate_->factory()->empty_string();
code_event.script_line = 0;
code_event.script_column = 0;
code_event.code_type = GetCodeEventTypeForTag(tag);
code_event.comment = "";
code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
}
void ExternalCodeEventListener::CodeCreateEvent(
CodeEventListener::LogEventsAndTags tag, AbstractCode* code,
SharedFunctionInfo* shared, Name* name) {
Handle<String> name_string =
Name::ToFunctionName(Handle<Name>(name, isolate_)).ToHandleChecked();
CodeEvent code_event;
code_event.code_start_address =
static_cast<uintptr_t>(code->InstructionStart());
code_event.code_size = static_cast<size_t>(code->InstructionSize());
code_event.function_name = name_string;
code_event.script_name = isolate_->factory()->empty_string();
code_event.script_line = 0;
code_event.script_column = 0;
code_event.code_type = GetCodeEventTypeForTag(tag);
code_event.comment = "";
code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
}
void ExternalCodeEventListener::CodeCreateEvent(
CodeEventListener::LogEventsAndTags tag, AbstractCode* code,
SharedFunctionInfo* shared, Name* source, int line, int column) {
Handle<String> name_string =
Name::ToFunctionName(Handle<Name>(shared->Name(), isolate_))
.ToHandleChecked();
Handle<String> source_string =
Name::ToFunctionName(Handle<Name>(source, isolate_)).ToHandleChecked();
CodeEvent code_event;
code_event.code_start_address =
static_cast<uintptr_t>(code->InstructionStart());
code_event.code_size = static_cast<size_t>(code->InstructionSize());
code_event.function_name = name_string;
code_event.script_name = source_string;
code_event.script_line = line;
code_event.script_column = column;
code_event.code_type = GetCodeEventTypeForTag(tag);
code_event.comment = "";
code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
}
void ExternalCodeEventListener::CodeCreateEvent(LogEventsAndTags tag,
const wasm::WasmCode* code,
wasm::WasmName name) {
// TODO(mmarchini): handle later
}
void ExternalCodeEventListener::RegExpCodeCreateEvent(AbstractCode* code,
String* source) {
CodeEvent code_event;
code_event.code_start_address =
static_cast<uintptr_t>(code->InstructionStart());
code_event.code_size = static_cast<size_t>(code->InstructionSize());
code_event.function_name = Handle<String>(source, isolate_);
code_event.script_name = isolate_->factory()->empty_string();
code_event.script_line = 0;
code_event.script_column = 0;
code_event.code_type = GetCodeEventTypeForTag(CodeEventListener::REG_EXP_TAG);
code_event.comment = "";
code_event_handler_->Handle(reinterpret_cast<v8::CodeEvent*>(&code_event));
}
// Low-level logging support.
class LowLevelLogger : public CodeEventLogger {
public:
explicit LowLevelLogger(const char* file_name);
......@@ -808,7 +968,8 @@ Logger::Logger(Isolate* isolate)
perf_jit_logger_(nullptr),
ll_logger_(nullptr),
jit_logger_(nullptr),
is_initialized_(false) {}
is_initialized_(false),
existing_code_logger_(isolate) {}
Logger::~Logger() {
delete log_;
......@@ -1077,7 +1238,7 @@ void AppendCodeCreateHeader(Log::MessageBuilder& msg,
void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, const char* comment) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
AppendCodeCreateHeader(msg, tag, code, &timer_);
......@@ -1087,7 +1248,7 @@ void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, Name* name) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
AppendCodeCreateHeader(msg, tag, code, &timer_);
......@@ -1098,7 +1259,7 @@ void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, SharedFunctionInfo* shared,
Name* name) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
if (code == AbstractCode::cast(
isolate_->builtins()->builtin(Builtins::kCompileLazy))) {
......@@ -1114,7 +1275,7 @@ void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
const wasm::WasmCode* code, wasm::WasmName name) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
AppendCodeCreateHeader(msg, tag, AbstractCode::Kind::WASM_FUNCTION,
......@@ -1142,7 +1303,7 @@ void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
AbstractCode* code, SharedFunctionInfo* shared,
Name* source, int line, int column) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
......@@ -1259,7 +1420,7 @@ void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
void Logger::CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
msg << kLogEventsNames[CodeEventListener::CODE_DISABLE_OPT_EVENT] << kNext
......@@ -1270,13 +1431,13 @@ void Logger::CodeDisableOptEvent(AbstractCode* code,
void Logger::CodeMovingGCEvent() {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!log_->IsEnabled() || !FLAG_ll_prof) return;
base::OS::SignalCodeMovingGC();
}
void Logger::RegExpCodeCreateEvent(AbstractCode* code, String* source) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
if (!FLAG_log_code || !log_->IsEnabled()) return;
Log::MessageBuilder msg(log_);
AppendCodeCreateHeader(msg, CodeEventListener::REG_EXP_TAG, code, &timer_);
......@@ -1285,7 +1446,7 @@ void Logger::RegExpCodeCreateEvent(AbstractCode* code, String* source) {
}
void Logger::CodeMoveEvent(AbstractCode* from, Address to) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
MoveEventInternal(CodeEventListener::CODE_MOVE_EVENT, from->address(), to);
}
......@@ -1334,7 +1495,7 @@ void Logger::CodeNameEvent(Address addr, int pos, const char* code_name) {
void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
if (!is_logging_code_events()) return;
if (!is_listening_to_code_events()) return;
MoveEventInternal(CodeEventListener::SHARED_FUNC_MOVE_EVENT, from, to);
}
......@@ -1624,170 +1785,28 @@ static int EnumerateWasmModules(Heap* heap,
}
void Logger::LogCodeObject(Object* object) {
AbstractCode* code_object = AbstractCode::cast(object);
CodeEventListener::LogEventsAndTags tag = CodeEventListener::STUB_TAG;
const char* description = "Unknown code from the snapshot";
switch (code_object->kind()) {
case AbstractCode::INTERPRETED_FUNCTION:
case AbstractCode::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
case AbstractCode::BYTECODE_HANDLER:
return; // We log it later by walking the dispatch table.
case AbstractCode::STUB:
description =
CodeStub::MajorName(CodeStub::GetMajorKey(code_object->GetCode()));
if (description == nullptr) description = "A stub from the snapshot";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::REGEXP:
description = "Regular expression code";
tag = CodeEventListener::REG_EXP_TAG;
break;
case AbstractCode::BUILTIN:
description =
isolate_->builtins()->name(code_object->GetCode()->builtin_index());
tag = CodeEventListener::BUILTIN_TAG;
break;
case AbstractCode::WASM_FUNCTION:
description = "A Wasm function";
tag = CodeEventListener::FUNCTION_TAG;
break;
case AbstractCode::JS_TO_WASM_FUNCTION:
description = "A JavaScript to Wasm adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::WASM_TO_JS_FUNCTION:
description = "A Wasm to JavaScript adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::WASM_INTERPRETER_ENTRY:
description = "A Wasm to Interpreter adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::C_WASM_ENTRY:
description = "A C to Wasm entry stub";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::NUMBER_OF_KINDS:
UNIMPLEMENTED();
}
PROFILE(isolate_, CodeCreateEvent(tag, code_object, description));
existing_code_logger_.LogCodeObject(object);
}
void Logger::LogCodeObjects() {
Heap* heap = isolate_->heap();
HeapIterator iterator(heap);
DisallowHeapAllocation no_gc;
for (HeapObject* obj = iterator.next(); obj != nullptr;
obj = iterator.next()) {
if (obj->IsCode()) LogCodeObject(obj);
if (obj->IsBytecodeArray()) LogCodeObject(obj);
}
}
void Logger::LogCodeObjects() { existing_code_logger_.LogCodeObjects(); }
void Logger::LogBytecodeHandler(interpreter::Bytecode bytecode,
interpreter::OperandScale operand_scale,
Code* code) {
std::string bytecode_name =
interpreter::Bytecodes::ToString(bytecode, operand_scale);
PROFILE(isolate_,
CodeCreateEvent(CodeEventListener::BYTECODE_HANDLER_TAG,
AbstractCode::cast(code), bytecode_name.c_str()));
existing_code_logger_.LogBytecodeHandler(bytecode, operand_scale, code);
}
void Logger::LogBytecodeHandlers() {
const interpreter::OperandScale kOperandScales[] = {
#define VALUE(Name, _) interpreter::OperandScale::k##Name,
OPERAND_SCALE_LIST(VALUE)
#undef VALUE
};
const int last_index = static_cast<int>(interpreter::Bytecode::kLast);
interpreter::Interpreter* interpreter = isolate_->interpreter();
for (auto operand_scale : kOperandScales) {
for (int index = 0; index <= last_index; ++index) {
interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(index);
if (interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
Code* code = interpreter->GetBytecodeHandler(bytecode, operand_scale);
if (isolate_->heap()->IsDeserializeLazyHandler(code)) continue;
LogBytecodeHandler(bytecode, operand_scale, code);
}
}
}
existing_code_logger_.LogBytecodeHandlers();
}
void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
Handle<AbstractCode> code) {
if (shared->script()->IsScript()) {
Handle<Script> script(Script::cast(shared->script()));
int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1;
int column_num =
Script::GetColumnNumber(script, shared->StartPosition()) + 1;
if (script->name()->IsString()) {
Handle<String> script_name(String::cast(script->name()));
if (line_num > 0) {
PROFILE(isolate_,
CodeCreateEvent(
Logger::ToNativeByScript(
CodeEventListener::LAZY_COMPILE_TAG, *script),
*code, *shared, *script_name, line_num, column_num));
} else {
// Can't distinguish eval and script here, so always use Script.
PROFILE(isolate_,
CodeCreateEvent(Logger::ToNativeByScript(
CodeEventListener::SCRIPT_TAG, *script),
*code, *shared, *script_name));
}
} else {
PROFILE(isolate_,
CodeCreateEvent(Logger::ToNativeByScript(
CodeEventListener::LAZY_COMPILE_TAG, *script),
*code, *shared, isolate_->heap()->empty_string(),
line_num, column_num));
}
} else if (shared->IsApiFunction()) {
// API function.
FunctionTemplateInfo* fun_data = shared->get_api_func_data();
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined(isolate_)) {
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
Object* callback_obj = call_data->callback();
Address entry_point = v8::ToCData<Address>(callback_obj);
#if USES_FUNCTION_DESCRIPTORS
entry_point = *FUNCTION_ENTRYPOINT_ADDRESS(entry_point);
#endif
PROFILE(isolate_, CallbackEvent(shared->DebugName(), entry_point));
}
} else {
PROFILE(isolate_,
CodeCreateEvent(CodeEventListener::LAZY_COMPILE_TAG, *code, *shared,
isolate_->heap()->empty_string()));
}
existing_code_logger_.LogExistingFunction(shared, code);
}
void Logger::LogCompiledFunctions() {
Heap* heap = isolate_->heap();
HandleScope scope(isolate_);
const int compiled_funcs_count =
EnumerateCompiledFunctions(heap, nullptr, nullptr);
ScopedVector<Handle<SharedFunctionInfo>> sfis(compiled_funcs_count);
ScopedVector<Handle<AbstractCode> > code_objects(compiled_funcs_count);
EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start());
// During iteration, there can be heap allocation due to
// GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) {
if (code_objects[i].is_identical_to(BUILTIN_CODE(isolate_, CompileLazy)))
continue;
LogExistingFunction(sfis[i], code_objects[i]);
}
const int compiled_wasm_modules_count = EnumerateWasmModules(heap, nullptr);
ScopedVector<Handle<WasmCompiledModule>> modules(compiled_wasm_modules_count);
EnumerateWasmModules(heap, modules.start());
for (int i = 0; i < compiled_wasm_modules_count; ++i) {
modules[i]->LogWasmCodes(isolate_);
}
existing_code_logger_.LogCompiledFunctions();
}
void Logger::LogAccessorCallbacks() {
......@@ -1991,5 +2010,172 @@ FILE* Logger::TearDown() {
return log_->Close();
}
void ExistingCodeLogger::LogCodeObject(Object* object) {
AbstractCode* code_object = AbstractCode::cast(object);
CodeEventListener::LogEventsAndTags tag = CodeEventListener::STUB_TAG;
const char* description = "Unknown code from before profiling";
switch (code_object->kind()) {
case AbstractCode::INTERPRETED_FUNCTION:
case AbstractCode::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
case AbstractCode::BYTECODE_HANDLER:
return; // We log it later by walking the dispatch table.
case AbstractCode::STUB:
description =
CodeStub::MajorName(CodeStub::GetMajorKey(code_object->GetCode()));
if (description == nullptr) description = "A stub from before profiling";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::REGEXP:
description = "Regular expression code";
tag = CodeEventListener::REG_EXP_TAG;
break;
case AbstractCode::BUILTIN:
description =
isolate_->builtins()->name(code_object->GetCode()->builtin_index());
tag = CodeEventListener::BUILTIN_TAG;
break;
case AbstractCode::WASM_FUNCTION:
description = "A Wasm function";
tag = CodeEventListener::FUNCTION_TAG;
break;
case AbstractCode::JS_TO_WASM_FUNCTION:
description = "A JavaScript to Wasm adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::WASM_TO_JS_FUNCTION:
description = "A Wasm to JavaScript adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::WASM_INTERPRETER_ENTRY:
description = "A Wasm to Interpreter adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::C_WASM_ENTRY:
description = "A C to Wasm entry stub";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::NUMBER_OF_KINDS:
UNIMPLEMENTED();
}
CALL_CODE_EVENT_HANDLER(CodeCreateEvent(tag, code_object, description))
}
void ExistingCodeLogger::LogCodeObjects() {
Heap* heap = isolate_->heap();
HeapIterator iterator(heap);
DisallowHeapAllocation no_gc;
for (HeapObject* obj = iterator.next(); obj != nullptr;
obj = iterator.next()) {
if (obj->IsCode()) LogCodeObject(obj);
if (obj->IsBytecodeArray()) LogCodeObject(obj);
}
}
void ExistingCodeLogger::LogCompiledFunctions() {
Heap* heap = isolate_->heap();
HandleScope scope(isolate_);
const int compiled_funcs_count =
EnumerateCompiledFunctions(heap, nullptr, nullptr);
ScopedVector<Handle<SharedFunctionInfo>> sfis(compiled_funcs_count);
ScopedVector<Handle<AbstractCode>> code_objects(compiled_funcs_count);
EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start());
// During iteration, there can be heap allocation due to
// GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) {
if (code_objects[i].is_identical_to(BUILTIN_CODE(isolate_, CompileLazy)))
continue;
LogExistingFunction(sfis[i], code_objects[i]);
}
const int compiled_wasm_modules_count = EnumerateWasmModules(heap, nullptr);
ScopedVector<Handle<WasmCompiledModule>> modules(compiled_wasm_modules_count);
EnumerateWasmModules(heap, modules.start());
for (int i = 0; i < compiled_wasm_modules_count; ++i) {
modules[i]->LogWasmCodes(isolate_);
}
}
void ExistingCodeLogger::LogBytecodeHandler(
interpreter::Bytecode bytecode, interpreter::OperandScale operand_scale,
Code* code) {
std::string bytecode_name =
interpreter::Bytecodes::ToString(bytecode, operand_scale);
CALL_CODE_EVENT_HANDLER(
CodeCreateEvent(CodeEventListener::BYTECODE_HANDLER_TAG,
AbstractCode::cast(code), bytecode_name.c_str()))
}
void ExistingCodeLogger::LogBytecodeHandlers() {
const interpreter::OperandScale kOperandScales[] = {
#define VALUE(Name, _) interpreter::OperandScale::k##Name,
OPERAND_SCALE_LIST(VALUE)
#undef VALUE
};
const int last_index = static_cast<int>(interpreter::Bytecode::kLast);
interpreter::Interpreter* interpreter = isolate_->interpreter();
for (auto operand_scale : kOperandScales) {
for (int index = 0; index <= last_index; ++index) {
interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(index);
if (interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
Code* code = interpreter->GetBytecodeHandler(bytecode, operand_scale);
if (isolate_->heap()->IsDeserializeLazyHandler(code)) continue;
LogBytecodeHandler(bytecode, operand_scale, code);
}
}
}
}
void ExistingCodeLogger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
Handle<AbstractCode> code) {
if (shared->script()->IsScript()) {
Handle<Script> script(Script::cast(shared->script()));
int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1;
int column_num =
Script::GetColumnNumber(script, shared->StartPosition()) + 1;
if (script->name()->IsString()) {
Handle<String> script_name(String::cast(script->name()));
if (line_num > 0) {
CALL_CODE_EVENT_HANDLER(
CodeCreateEvent(Logger::ToNativeByScript(
CodeEventListener::LAZY_COMPILE_TAG, *script),
*code, *shared, *script_name, line_num, column_num))
} else {
// Can't distinguish eval and script here, so always use Script.
CALL_CODE_EVENT_HANDLER(CodeCreateEvent(
Logger::ToNativeByScript(CodeEventListener::SCRIPT_TAG, *script),
*code, *shared, *script_name))
}
} else {
CALL_CODE_EVENT_HANDLER(
CodeCreateEvent(Logger::ToNativeByScript(
CodeEventListener::LAZY_COMPILE_TAG, *script),
*code, *shared, isolate_->heap()->empty_string(),
line_num, column_num))
}
} else if (shared->IsApiFunction()) {
// API function.
FunctionTemplateInfo* fun_data = shared->get_api_func_data();
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined(isolate_)) {
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
Object* callback_obj = call_data->callback();
Address entry_point = v8::ToCData<Address>(callback_obj);
#if USES_FUNCTION_DESCRIPTORS
entry_point = *FUNCTION_ENTRYPOINT_ADDRESS(entry_point);
#endif
CALL_CODE_EVENT_HANDLER(CallbackEvent(shared->DebugName(), entry_point))
}
} else {
CALL_CODE_EVENT_HANDLER(CodeCreateEvent(CodeEventListener::LAZY_COMPILE_TAG,
*code, *shared,
isolate_->heap()->empty_string()))
}
}
#undef CALL_CODE_EVENT_HANDLER
} // namespace internal
} // namespace v8
......@@ -8,6 +8,7 @@
#include <set>
#include <string>
#include "include/v8-profiler.h"
#include "src/allocation.h"
#include "src/base/compiler-specific.h"
#include "src/base/platform/elapsed-timer.h"
......@@ -91,12 +92,33 @@ class WasmCode;
if (logger->is_logging()) logger->Call; \
} while (false)
#define LOG_CODE_EVENT(isolate, Call) \
do { \
v8::internal::Logger* logger = (isolate)->logger(); \
if (logger->is_logging_code_events()) logger->Call; \
#define LOG_CODE_EVENT(isolate, Call) \
do { \
v8::internal::Logger* logger = (isolate)->logger(); \
if (logger->is_listening_to_code_events()) logger->Call; \
} while (false)
class ExistingCodeLogger {
public:
explicit ExistingCodeLogger(Isolate* isolate,
CodeEventListener* listener = nullptr)
: isolate_(isolate), listener_(listener) {}
void LogCodeObjects();
void LogBytecodeHandlers();
void LogCompiledFunctions();
void LogExistingFunction(Handle<SharedFunctionInfo> shared,
Handle<AbstractCode> code);
void LogCodeObject(Object* object);
void LogBytecodeHandler(interpreter::Bytecode bytecode,
interpreter::OperandScale operand_scale, Code* code);
private:
Isolate* isolate_;
CodeEventListener* listener_;
};
class Logger : public CodeEventListener {
public:
enum StartEnd { START = 0, END = 1, STAMP = 2 };
......@@ -229,7 +251,7 @@ class Logger : public CodeEventListener {
return is_logging_;
}
bool is_logging_code_events() {
bool is_listening_to_code_events() {
return is_logging() || jit_logger_ != nullptr;
}
......@@ -327,6 +349,8 @@ class Logger : public CodeEventListener {
// 'true' between SetUp() and TearDown().
bool is_initialized_;
ExistingCodeLogger existing_code_logger_;
base::ElapsedTimer timer_;
friend class CpuProfiler;
......@@ -407,6 +431,58 @@ class CodeEventLogger : public CodeEventListener {
NameBuffer* name_buffer_;
};
struct CodeEvent {
uintptr_t code_start_address;
size_t code_size;
Handle<String> function_name;
Handle<String> script_name;
int script_line;
int script_column;
CodeEventType code_type;
const char* comment;
};
class ExternalCodeEventListener : public CodeEventListener {
public:
explicit ExternalCodeEventListener(Isolate* isolate);
~ExternalCodeEventListener() 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* name) override;
void CodeCreateEvent(LogEventsAndTags tag, AbstractCode* code,
SharedFunctionInfo* shared, Name* source, int line,
int column) override;
void CodeCreateEvent(LogEventsAndTags tag, const wasm::WasmCode* code,
wasm::WasmName name) override;
void RegExpCodeCreateEvent(AbstractCode* code, String* source) override;
void CallbackEvent(Name* name, Address entry_point) override {}
void GetterCallbackEvent(Name* name, Address entry_point) override {}
void SetterCallbackEvent(Name* name, Address entry_point) override {}
void SharedFunctionInfoMoveEvent(Address from, Address to) override {}
void CodeMoveEvent(AbstractCode* from, Address to) override {}
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
void CodeMovingGCEvent() override {}
void CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
int fp_to_sp_delta) override {}
void StartListening(CodeEventHandler* code_event_handler);
void StopListening();
bool is_listening_to_code_events() override { return true; }
private:
void LogExistingCode();
bool is_listening_;
Isolate* isolate_;
v8::CodeEventHandler* code_event_handler_;
};
} // namespace internal
} // namespace v8
......
......@@ -147,7 +147,8 @@ RUNTIME_FUNCTION(Runtime_SetCode) {
// the target_shared optimized code map.
JSFunction::EnsureFeedbackVector(target);
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
if (isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) {
isolate->logger()->LogExistingFunction(
source_shared, Handle<AbstractCode>(source_shared->abstract_code()));
}
......
......@@ -287,7 +287,8 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
}
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
if (isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) {
String* name = isolate->heap()->empty_string();
if (result->script()->IsScript()) {
Script* script = Script::cast(result->script());
......
......@@ -115,7 +115,8 @@ Code* Snapshot::DeserializeBuiltin(Isolate* isolate, int builtin_id) {
Builtins::name(builtin_id), bytes, ms);
}
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
if (isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) {
isolate->logger()->LogCodeObject(code);
}
......@@ -196,7 +197,8 @@ Code* Snapshot::DeserializeHandler(Isolate* isolate,
bytes, ms);
}
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
if (isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) {
isolate->logger()->LogBytecodeHandler(bytecode, operand_scale, code);
}
......
......@@ -217,7 +217,7 @@ bool WasmCode::HasTrapHandlerIndex() const { return trap_handler_index_ >= 0; }
void WasmCode::ResetTrapHandlerIndex() { trap_handler_index_ = -1; }
bool WasmCode::ShouldBeLogged(Isolate* isolate) {
return isolate->logger()->is_logging_code_events() ||
return isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling() || FLAG_print_wasm_code || FLAG_print_code;
}
......
......@@ -35,6 +35,7 @@
#endif // __linux__
#include <unordered_set>
#include <vector>
#include "src/api.h"
#include "src/log-utils.h"
#include "src/log.h"
......@@ -251,6 +252,38 @@ class ScopedLoggerInitializer {
DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
};
class TestCodeEventHandler : public v8::CodeEventHandler {
public:
explicit TestCodeEventHandler(v8::Isolate* isolate)
: v8::CodeEventHandler(isolate) {}
const char* FindLine(const char* prefix, const char* suffix = nullptr,
const char* start = nullptr) {
if (!log_.length()) return NULL;
const char* c_log = log_.c_str();
if (start == nullptr) start = c_log;
const char* end = c_log + log_.length();
return FindLogLine(start, end, prefix, suffix);
}
void Handle(v8::CodeEvent* code_event) override {
const char* code_type =
v8::CodeEvent::GetCodeEventTypeName(code_event->GetCodeType());
char function_name[1000];
strncpy(function_name, code_type, 1000);
function_name[strlen(code_type)] = ' ';
code_event->GetFunctionName()->WriteUtf8(
function_name + strlen(code_type) + 1, 1000);
function_name[strlen(function_name) + 1] = '\0';
function_name[strlen(function_name)] = '\n';
log_ += std::string(function_name);
}
private:
std::string log_;
};
} // namespace
TEST(FindLogLine) {
......@@ -800,6 +833,48 @@ TEST(LogInterpretedFramesNativeStack) {
isolate->Dispose();
}
TEST(ExternalCodeEventListener) {
i::FLAG_log = false;
i::FLAG_prof = false;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::HandleScope scope(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
context->Enter();
TestCodeEventHandler code_event_handler(isolate);
const char* source_text_before_start =
"function testCodeEventListenerBeforeStart(a,b) { return a + b };"
"testCodeEventListenerBeforeStart('1', 1);";
CompileRun(source_text_before_start);
CHECK_NULL(code_event_handler.FindLine("LazyCompile",
"testCodeEventListenerBeforeStart"));
code_event_handler.Enable();
CHECK_NOT_NULL(code_event_handler.FindLine(
"LazyCompile", "testCodeEventListenerBeforeStart"));
const char* source_text_after_start =
"function testCodeEventListenerAfterStart(a,b) { return a + b };"
"testCodeEventListenerAfterStart('1', 1);";
CompileRun(source_text_after_start);
CHECK_NOT_NULL(code_event_handler.FindLine(
"LazyCompile", "testCodeEventListenerAfterStart"));
context->Exit();
}
isolate->Dispose();
}
TEST(TraceMaps) {
SETUP_FLAGS();
i::FLAG_trace_maps = true;
......
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