electron/patches/common/v8/node_v8_patches.patch

6741 lines
262 KiB
Diff

From 762089e4aab680cd7f6841d9b596ab9a2ffad3fc Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Mon, 18 Jun 2018 09:47:52 -0700
Subject: [PATCH 01/23] deps: cherry-pick 5dd3395 from upstream V8
Original commit message:
[log] improve --perf-basic-prof-only-functions
Change --perf-basic-prof-only-functions to also log builtin code
creation events, otherwise InterpretedFunctions generated by
--interpreted-frames-native-stack will be filtered out.
R=yangguo@google.com
Change-Id: Ib0623fca88e25c514473a43de56ebbbdcb146f97
Reviewed-on: https://chromium-review.googlesource.com/1100014
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53760}
Refs: https://github.com/v8/v8/commit/5dd33955d5cb1d84dd2509363e11d3c2a
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21386
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
src/log.cc | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/log.cc b/src/log.cc
index 953216aef7..f5e1fe6baf 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -305,6 +305,7 @@ void PerfBasicLogger::LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo*,
const char* name, int length) {
if (FLAG_perf_basic_prof_only_functions &&
(code->kind() != AbstractCode::INTERPRETED_FUNCTION &&
+ code->kind() != AbstractCode::BUILTIN &&
code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
return;
}
--
2.17.0
From bf391a14d065dcaeef410a09d300c5e91a06bf8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Thu, 5 Jul 2018 08:38:02 +0200
Subject: [PATCH 02/23] deps: cherry-pick aa6ce3e from upstream V8
Original commit message:
[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: Daniel Beckert <daniel@sthima.com.br>
Reviewed-on: https://chromium-review.googlesource.com/1028770
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53382}
Refs: https://github.com/v8/v8/commit/aa6ce3ee617b2f324bea3a5d8e3263aee4cde6d7
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21079
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
---
include/v8-profiler.h | 70 +++++
src/api.cc | 64 ++++
src/code-events.h | 62 ++--
src/compiler.cc | 5 +-
src/compiler/wasm-compiler.cc | 3 +-
src/heap/mark-compact.cc | 2 +-
src/isolate.cc | 2 +-
src/isolate.h | 1 +
src/log.cc | 510 ++++++++++++++++++++++----------
src/log.h | 86 +++++-
src/runtime/runtime-function.cc | 3 +-
src/snapshot/code-serializer.cc | 3 +-
src/snapshot/snapshot-common.cc | 6 +-
src/wasm/wasm-code-manager.cc | 2 +-
test/cctest/test-log.cc | 75 +++++
15 files changed, 694 insertions(+), 200 deletions(-)
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 34ad2b9cea..76490afe3c 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -992,6 +992,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
diff --git a/src/api.cc b/src/api.cc
index 89bcb2e4fa..f51576ed2a 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -10104,6 +10104,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*>(
diff --git a/src/code-events.h b/src/code-events.h
index 439cb54dca..caed5160f4 100644
--- a/src/code-events.h
+++ b/src/code-events.h
@@ -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_); \
diff --git a/src/compiler.cc b/src/compiler.cc
index ae6bc9c4fa..a01750b23a 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -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;
}
diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc
index daceffd331..61c3a350f7 100644
--- a/src/compiler/wasm-compiler.cc
+++ b/src/compiler/wasm-compiler.cc
@@ -3952,7 +3952,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)
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index 50be29c29a..66575f8250 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -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());
diff --git a/src/isolate.cc b/src/isolate.cc
index abd74ee40d..dd66479f73 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -2892,7 +2892,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);
}
diff --git a/src/isolate.h b/src/isolate.h
index 929403e134..69ec1f1853 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -57,6 +57,7 @@ class BuiltinsConstantsTableBuilder;
class CallInterfaceDescriptorData;
class CancelableTaskManager;
class CodeEventDispatcher;
+class ExternalCodeEventListener;
class CodeGenerator;
class CodeRange;
class CodeStubDescriptor;
diff --git a/src/log.cc b/src/log.cc
index f5e1fe6baf..20e5f2c964 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -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()) {
@@ -320,9 +342,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);
@@ -809,7 +969,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_;
@@ -1078,7 +1239,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_);
@@ -1088,7 +1249,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_);
@@ -1099,7 +1260,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))) {
@@ -1115,7 +1276,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,
@@ -1143,7 +1304,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_);
@@ -1260,7 +1421,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
@@ -1271,13 +1432,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_);
@@ -1286,7 +1447,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);
}
@@ -1335,7 +1496,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);
}
@@ -1625,170 +1786,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() {
@@ -1992,5 +2011,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
diff --git a/src/log.h b/src/log.h
index 9943d8ca9b..738aef4d73 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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
diff --git a/src/runtime/runtime-function.cc b/src/runtime/runtime-function.cc
index 5dcb4115e5..9f1cbc00e9 100644
--- a/src/runtime/runtime-function.cc
+++ b/src/runtime/runtime-function.cc
@@ -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()));
}
diff --git a/src/snapshot/code-serializer.cc b/src/snapshot/code-serializer.cc
index 8dc98d836b..0ff35abc5d 100644
--- a/src/snapshot/code-serializer.cc
+++ b/src/snapshot/code-serializer.cc
@@ -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());
diff --git a/src/snapshot/snapshot-common.cc b/src/snapshot/snapshot-common.cc
index 902bda4a0f..d2fcc3087b 100644
--- a/src/snapshot/snapshot-common.cc
+++ b/src/snapshot/snapshot-common.cc
@@ -114,7 +114,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);
}
@@ -195,7 +196,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);
}
diff --git a/src/wasm/wasm-code-manager.cc b/src/wasm/wasm-code-manager.cc
index 6d32a35467..f997ef2a8c 100644
--- a/src/wasm/wasm-code-manager.cc
+++ b/src/wasm/wasm-code-manager.cc
@@ -218,7 +218,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;
}
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index 27d363d5a4..97071a63f5 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -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;
--
2.17.0
From cc467685719aa242d7cf82dc58b7feccc3f710ce Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Mon, 11 Jun 2018 09:05:33 -0700
Subject: [PATCH 03/23] deps: cherry-pick b20faff from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Original commit message:
[log] fix ExistingCodeLogger behavior on edge case
ExistingCodeLogger was behaving incorrectly when the
CodeEventHandler API was used in combination with
--interpreted-frames-native-stack. Instead of collecting copied
trampolines as InterpretedFunction:functionName, they were being
collected as Builtin:IntepreterEntryTrampolines. This patch adds
special handling for copied trampolines when using
ExistingCodeLogger.
R=yangguo@google.com
Change-Id: I3ee4be03800122d28d53b51b20c60dcf6263e4c1
Reviewed-on: https://chromium-review.googlesource.com/1087813
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53624}
Refs: https://github.com/v8/v8/commit/b20faffb07bc97b869a00b935c639bd1c
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21126
Refs: https://github.com/v8/v8/commit/aa6ce3e
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
src/log.cc | 44 ++++++++++++++++++++++++-----------------
src/log.h | 4 +++-
test/cctest/test-log.cc | 43 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 72 insertions(+), 19 deletions(-)
diff --git a/src/log.cc b/src/log.cc
index 20e5f2c964..8ed6f43ba6 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -2012,10 +2012,10 @@ FILE* Logger::TearDown() {
}
void ExistingCodeLogger::LogCodeObject(Object* object) {
- AbstractCode* code_object = AbstractCode::cast(object);
+ AbstractCode* abstract_code = AbstractCode::cast(object);
CodeEventListener::LogEventsAndTags tag = CodeEventListener::STUB_TAG;
const char* description = "Unknown code from before profiling";
- switch (code_object->kind()) {
+ switch (abstract_code->kind()) {
case AbstractCode::INTERPRETED_FUNCTION:
case AbstractCode::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
@@ -2023,7 +2023,7 @@ void ExistingCodeLogger::LogCodeObject(Object* object) {
return; // We log it later by walking the dispatch table.
case AbstractCode::STUB:
description =
- CodeStub::MajorName(CodeStub::GetMajorKey(code_object->GetCode()));
+ CodeStub::MajorName(CodeStub::GetMajorKey(abstract_code->GetCode()));
if (description == nullptr) description = "A stub from before profiling";
tag = CodeEventListener::STUB_TAG;
break;
@@ -2032,8 +2032,13 @@ void ExistingCodeLogger::LogCodeObject(Object* object) {
tag = CodeEventListener::REG_EXP_TAG;
break;
case AbstractCode::BUILTIN:
+ if (Code::cast(object)->is_interpreter_trampoline_builtin() &&
+ Code::cast(object) ==
+ *BUILTIN_CODE(isolate_, InterpreterEntryTrampoline)) {
+ return;
+ }
description =
- isolate_->builtins()->name(code_object->GetCode()->builtin_index());
+ isolate_->builtins()->name(abstract_code->GetCode()->builtin_index());
tag = CodeEventListener::BUILTIN_TAG;
break;
case AbstractCode::WASM_FUNCTION:
@@ -2059,7 +2064,7 @@ void ExistingCodeLogger::LogCodeObject(Object* object) {
case AbstractCode::NUMBER_OF_KINDS:
UNIMPLEMENTED();
}
- CALL_CODE_EVENT_HANDLER(CodeCreateEvent(tag, code_object, description))
+ CALL_CODE_EVENT_HANDLER(CodeCreateEvent(tag, abstract_code, description))
}
void ExistingCodeLogger::LogCodeObjects() {
@@ -2085,6 +2090,12 @@ void ExistingCodeLogger::LogCompiledFunctions() {
// During iteration, there can be heap allocation due to
// GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) {
+ if (sfis[i]->function_data()->IsInterpreterData()) {
+ LogExistingFunction(sfis[i],
+ Handle<AbstractCode>(AbstractCode::cast(
+ sfis[i]->InterpreterTrampoline())),
+ CodeEventListener::INTERPRETED_FUNCTION_TAG);
+ }
if (code_objects[i].is_identical_to(BUILTIN_CODE(isolate_, CompileLazy)))
continue;
LogExistingFunction(sfis[i], code_objects[i]);
@@ -2129,8 +2140,9 @@ void ExistingCodeLogger::LogBytecodeHandlers() {
}
}
-void ExistingCodeLogger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
- Handle<AbstractCode> code) {
+void ExistingCodeLogger::LogExistingFunction(
+ Handle<SharedFunctionInfo> shared, Handle<AbstractCode> code,
+ CodeEventListener::LogEventsAndTags tag) {
if (shared->script()->IsScript()) {
Handle<Script> script(Script::cast(shared->script()));
int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1;
@@ -2140,9 +2152,8 @@ void ExistingCodeLogger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
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))
+ CodeCreateEvent(Logger::ToNativeByScript(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(
@@ -2150,11 +2161,9 @@ void ExistingCodeLogger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
*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))
+ CALL_CODE_EVENT_HANDLER(CodeCreateEvent(
+ Logger::ToNativeByScript(tag, *script), *code, *shared,
+ isolate_->heap()->empty_string(), line_num, column_num))
}
} else if (shared->IsApiFunction()) {
// API function.
@@ -2170,9 +2179,8 @@ void ExistingCodeLogger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
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()))
+ CALL_CODE_EVENT_HANDLER(
+ CodeCreateEvent(tag, *code, *shared, isolate_->heap()->empty_string()))
}
}
diff --git a/src/log.h b/src/log.h
index 738aef4d73..ad254097e6 100644
--- a/src/log.h
+++ b/src/log.h
@@ -109,7 +109,9 @@ class ExistingCodeLogger {
void LogCompiledFunctions();
void LogExistingFunction(Handle<SharedFunctionInfo> shared,
- Handle<AbstractCode> code);
+ Handle<AbstractCode> code,
+ CodeEventListener::LogEventsAndTags tag =
+ CodeEventListener::LAZY_COMPILE_TAG);
void LogCodeObject(Object* object);
void LogBytecodeHandler(interpreter::Bytecode bytecode,
interpreter::OperandScale operand_scale, Code* code);
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index 97071a63f5..c7864034c9 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -875,6 +875,49 @@ TEST(ExternalCodeEventListener) {
isolate->Dispose();
}
+TEST(ExternalCodeEventListenerWithInterpretedFramesNativeStack) {
+ i::FLAG_log = false;
+ i::FLAG_prof = false;
+ i::FLAG_interpreted_frames_native_stack = true;
+
+ 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("InterpretedFunction",
+ "testCodeEventListenerBeforeStart"));
+
+ code_event_handler.Enable();
+
+ CHECK_NOT_NULL(code_event_handler.FindLine(
+ "InterpretedFunction", "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(
+ "InterpretedFunction", "testCodeEventListenerAfterStart"));
+
+ context->Exit();
+ }
+ isolate->Dispose();
+}
+
TEST(TraceMaps) {
SETUP_FLAGS();
i::FLAG_trace_maps = true;
--
2.17.0
From 4ee0aa9e5546b96f883297bb3c2aaa5aa138b451 Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Mon, 18 Jun 2018 08:59:12 -0700
Subject: [PATCH 04/23] deps: cherry-pick acc336c from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Original commit message:
[log] fix boolean logic on LogCodeObject
R=yangguo@google.com
Change-Id: Icb4825344991e5b2d15050e037064c60eeb9617e
Reviewed-on: https://chromium-review.googlesource.com/1097578
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53777}
Refs: https://github.com/v8/v8/commit/acc336c1257cc7ceee4b31094ee1e9e41
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21126
Refs: https://github.com/v8/v8/commit/aa6ce3e
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
src/log.cc | 2 +-
test/cctest/test-log.cc | 84 ++++++++++++++++++++++++++---------------
2 files changed, 55 insertions(+), 31 deletions(-)
diff --git a/src/log.cc b/src/log.cc
index 8ed6f43ba6..f46f849b4d 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -2033,7 +2033,7 @@ void ExistingCodeLogger::LogCodeObject(Object* object) {
break;
case AbstractCode::BUILTIN:
if (Code::cast(object)->is_interpreter_trampoline_builtin() &&
- Code::cast(object) ==
+ Code::cast(object) !=
*BUILTIN_CODE(isolate_, InterpreterEntryTrampoline)) {
return;
}
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index c7864034c9..767541d4a3 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -36,6 +36,9 @@
#include <unordered_set>
#include <vector>
+// The C++ style guide recommends using <re2> instead of <regex>. However, the
+// former isn't available in V8.
+#include <regex> // NOLINT(build/c++11)
#include "src/api.h"
#include "src/log-utils.h"
#include "src/log.h"
@@ -257,30 +260,41 @@ class TestCodeEventHandler : public v8::CodeEventHandler {
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);
+ size_t CountLines(std::string prefix, std::string suffix = std::string()) {
+ if (!log_.length()) return 0;
+
+ std::regex expression("(^|\\n)" + prefix + ".*" + suffix + "(?=\\n|$)");
+
+ size_t match_count(std::distance(
+ std::sregex_iterator(log_.begin(), log_.end(), expression),
+ std::sregex_iterator()));
+
+ return match_count;
}
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);
+ std::string log_line = "";
+ log_line += v8::CodeEvent::GetCodeEventTypeName(code_event->GetCodeType());
+ log_line += " ";
+ log_line += FormatName(code_event);
+ log_line += "\n";
+ log_ += log_line;
}
private:
+ std::string FormatName(v8::CodeEvent* code_event) {
+ std::string name = std::string(code_event->GetComment());
+ if (name.empty()) {
+ v8::Local<v8::String> functionName = code_event->GetFunctionName();
+ std::string buffer(functionName->Utf8Length() + 1, 0);
+ functionName->WriteUtf8(&buffer[0], functionName->Utf8Length() + 1);
+ // Sanitize name, removing unwanted \0 resulted from WriteUtf8
+ name = std::string(buffer.c_str());
+ }
+
+ return name;
+ }
+
std::string log_;
};
@@ -854,21 +868,24 @@ TEST(ExternalCodeEventListener) {
"testCodeEventListenerBeforeStart('1', 1);";
CompileRun(source_text_before_start);
- CHECK_NULL(code_event_handler.FindLine("LazyCompile",
- "testCodeEventListenerBeforeStart"));
+ CHECK_EQ(code_event_handler.CountLines("LazyCompile",
+ "testCodeEventListenerBeforeStart"),
+ 0);
code_event_handler.Enable();
- CHECK_NOT_NULL(code_event_handler.FindLine(
- "LazyCompile", "testCodeEventListenerBeforeStart"));
+ CHECK_GE(code_event_handler.CountLines("LazyCompile",
+ "testCodeEventListenerBeforeStart"),
+ 1);
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"));
+ CHECK_GE(code_event_handler.CountLines("LazyCompile",
+ "testCodeEventListenerAfterStart"),
+ 1);
context->Exit();
}
@@ -897,21 +914,28 @@ TEST(ExternalCodeEventListenerWithInterpretedFramesNativeStack) {
"testCodeEventListenerBeforeStart('1', 1);";
CompileRun(source_text_before_start);
- CHECK_NULL(code_event_handler.FindLine("InterpretedFunction",
- "testCodeEventListenerBeforeStart"));
+ CHECK_EQ(code_event_handler.CountLines("InterpretedFunction",
+ "testCodeEventListenerBeforeStart"),
+ 0);
code_event_handler.Enable();
- CHECK_NOT_NULL(code_event_handler.FindLine(
- "InterpretedFunction", "testCodeEventListenerBeforeStart"));
+ CHECK_GE(code_event_handler.CountLines("InterpretedFunction",
+ "testCodeEventListenerBeforeStart"),
+ 1);
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(
- "InterpretedFunction", "testCodeEventListenerAfterStart"));
+ CHECK_GE(code_event_handler.CountLines("InterpretedFunction",
+ "testCodeEventListenerAfterStart"),
+ 1);
+
+ CHECK_EQ(
+ code_event_handler.CountLines("Builtin", "InterpreterEntryTrampoline"),
+ 1);
context->Exit();
}
--
2.17.0
From 6677c309e2efa042846da56863f7bc5f7e1f4c7a Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Thu, 21 Jun 2018 08:39:49 -0700
Subject: [PATCH 05/23] deps: cherry-pick 70c4340 from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Original commit message:
[log][api] Fix GCC 4.9 build failure
GCC 4.9 used on some Node.js CI machines complains when the control
reaches the end of a non-void function and no return is encountered.
R=bmeurer@google.com, ofrobots@google.com, yangguo@google.com
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I5af0192cb187eccbf34dbb60ff3ac2e4774af803
Reviewed-on: https://chromium-review.googlesource.com/1105619
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53861}
Refs: https://github.com/v8/v8/commit/70c43402ee04d482ff64e186e2faf43fe
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21126
Refs: https://github.com/v8/v8/commit/aa6ce3e
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
src/api.cc | 4 ++++
src/log.cc | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/src/api.cc b/src/api.cc
index f51576ed2a..34b4773c2f 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -10148,6 +10148,10 @@ const char* CodeEvent::GetCodeEventTypeName(CodeEventType code_event_type) {
CODE_EVENTS_LIST(V)
#undef V
}
+ // The execution should never pass here
+ UNREACHABLE();
+ // NOTE(mmarchini): Workaround to fix a compiler failure on GCC 4.9
+ return "Unknown";
}
CodeEventHandler::CodeEventHandler(Isolate* isolate) {
diff --git a/src/log.cc b/src/log.cc
index f46f849b4d..fb3b4761a3 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -59,6 +59,10 @@ static v8::CodeEventType GetCodeEventTypeForTag(
TAGS_LIST(V)
#undef V
}
+ // The execution should never pass here
+ UNREACHABLE();
+ // NOTE(mmarchini): Workaround to fix a compiler failure on GCC 4.9
+ return v8::CodeEventType::kUnknownType;
}
#define CALL_CODE_EVENT_HANDLER(Call) \
if (listener_) { \
--
2.17.0
From f44286f6a4977728c6a405070ad17333f5c81e6c Mon Sep 17 00:00:00 2001
From: Gus Caplan <me@gus.host>
Date: Tue, 3 Jul 2018 17:42:16 -0500
Subject: [PATCH 06/23] deps: cherry-pick 477df06 from upstream v8
Original commit message:
[API] Expand BigInt API
Provide a more complete BigInt API.
Bug: v8:7712
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: Ic8562d616f3125deabdf8b52c7019b191bef0e07
Reviewed-on: chromium-review.googlesource.com/1101198
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54122}
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21644
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
---
include/v8.h | 42 +++++++++++++++
src/api.cc | 43 +++++++++++++++
src/api.h | 1 +
src/counters.h | 1 +
src/objects/bigint.cc | 64 ++++++++++++++++++++++
src/objects/bigint.h | 5 ++
test/cctest/test-api.cc | 114 ++++++++++++++++++++++++++++++++++++++++
7 files changed, 270 insertions(+)
diff --git a/include/v8.h b/include/v8.h
index b68d9fbbfc..6c56f918e3 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -3165,6 +3165,48 @@ class V8_EXPORT Uint32 : public Integer {
class V8_EXPORT BigInt : public Primitive {
public:
static Local<BigInt> New(Isolate* isolate, int64_t value);
+ static Local<BigInt> NewFromUnsigned(Isolate* isolate, uint64_t value);
+ /**
+ * Creates a new BigInt object using a specified sign bit and a
+ * specified list of digits/words.
+ * The resulting number is calculated as:
+ *
+ * (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...)
+ */
+ static MaybeLocal<BigInt> NewFromWords(Local<Context> context, int sign_bit,
+ int word_count, const uint64_t* words);
+
+ /**
+ * Returns the value of this BigInt as an unsigned 64-bit integer.
+ * If `lossless` is provided, it will reflect whether the return value was
+ * truncated or wrapped around. In particular, it is set to `false` if this
+ * BigInt is negative.
+ */
+ uint64_t Uint64Value(bool* lossless = nullptr) const;
+
+ /**
+ * Returns the value of this BigInt as a signed 64-bit integer.
+ * If `lossless` is provided, it will reflect whether this BigInt was
+ * truncated or not.
+ */
+ int64_t Int64Value(bool* lossless = nullptr) const;
+
+ /**
+ * Returns the number of 64-bit words needed to store the result of
+ * ToWordsArray().
+ */
+ int WordCount() const;
+
+ /**
+ * Writes the contents of this BigInt to a specified memory location.
+ * `sign_bit` must be provided and will be set to 1 if this BigInt is
+ * negative.
+ * `*word_count` has to be initialized to the length of the `words` array.
+ * Upon return, it will be set to the actual number of words that would
+ * be needed to store this BigInt (i.e. the return value of `WordCount()`).
+ */
+ void ToWordsArray(int* sign_bit, int* word_count, uint64_t* words) const;
+
V8_INLINE static BigInt* Cast(v8::Value* obj);
private:
diff --git a/src/api.cc b/src/api.cc
index 34b4773c2f..66ca4bb96a 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -8000,6 +8000,49 @@ Local<BigInt> v8::BigInt::New(Isolate* isolate, int64_t value) {
return Utils::ToLocal(result);
}
+Local<BigInt> v8::BigInt::NewFromUnsigned(Isolate* isolate, uint64_t value) {
+ CHECK(i::FLAG_harmony_bigint);
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ ENTER_V8_NO_SCRIPT_NO_EXCEPTION(internal_isolate);
+ i::Handle<i::BigInt> result = i::BigInt::FromUint64(internal_isolate, value);
+ return Utils::ToLocal(result);
+}
+
+MaybeLocal<BigInt> v8::BigInt::NewFromWords(Local<Context> context,
+ int sign_bit, int word_count,
+ const uint64_t* words) {
+ CHECK(i::FLAG_harmony_bigint);
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
+ ENTER_V8_NO_SCRIPT(isolate, context, BigInt, NewFromWords,
+ MaybeLocal<BigInt>(), InternalEscapableScope);
+ i::MaybeHandle<i::BigInt> result =
+ i::BigInt::FromWords64(isolate, sign_bit, word_count, words);
+ has_pending_exception = result.is_null();
+ RETURN_ON_FAILED_EXECUTION(BigInt);
+ RETURN_ESCAPED(Utils::ToLocal(result.ToHandleChecked()));
+}
+
+uint64_t v8::BigInt::Uint64Value(bool* lossless) const {
+ i::Handle<i::BigInt> handle = Utils::OpenHandle(this);
+ return handle->AsUint64(lossless);
+}
+
+int64_t v8::BigInt::Int64Value(bool* lossless) const {
+ i::Handle<i::BigInt> handle = Utils::OpenHandle(this);
+ return handle->AsInt64(lossless);
+}
+
+int BigInt::WordCount() const {
+ i::Handle<i::BigInt> handle = Utils::OpenHandle(this);
+ return handle->Words64Count();
+}
+
+void BigInt::ToWordsArray(int* sign_bit, int* word_count,
+ uint64_t* words) const {
+ i::Handle<i::BigInt> handle = Utils::OpenHandle(this);
+ return handle->ToWordsArray64(sign_bit, word_count, words);
+}
+
void Isolate::ReportExternalAllocationLimitReached() {
i::Heap* heap = reinterpret_cast<i::Isolate*>(this)->heap();
if (heap->gc_state() != i::Heap::NOT_IN_GC) return;
diff --git a/src/api.h b/src/api.h
index d1297c8f38..342ab855ac 100644
--- a/src/api.h
+++ b/src/api.h
@@ -127,6 +127,7 @@ class RegisteredExtension {
V(Promise, JSPromise) \
V(Primitive, Object) \
V(PrimitiveArray, FixedArray) \
+ V(BigInt, BigInt) \
V(ScriptOrModule, Script)
class Utils {
diff --git a/src/counters.h b/src/counters.h
index 95812cf518..255c8db7c6 100644
--- a/src/counters.h
+++ b/src/counters.h
@@ -692,6 +692,7 @@ class RuntimeCallTimer final {
V(ArrayBuffer_New) \
V(Array_CloneElementAt) \
V(Array_New) \
+ V(BigInt_NewFromWords) \
V(BigInt64Array_New) \
V(BigUint64Array_New) \
V(BigIntObject_New) \
diff --git a/src/objects/bigint.cc b/src/objects/bigint.cc
index 3a0bb0fba3..61de3a066a 100644
--- a/src/objects/bigint.cc
+++ b/src/objects/bigint.cc
@@ -2259,6 +2259,70 @@ Handle<BigInt> BigInt::FromUint64(Isolate* isolate, uint64_t n) {
return MutableBigInt::MakeImmutable(result);
}
+MaybeHandle<BigInt> BigInt::FromWords64(Isolate* isolate, int sign_bit,
+ int words64_count,
+ const uint64_t* words) {
+ if (words64_count < 0 || words64_count > kMaxLength / (64 / kDigitBits)) {
+ THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
+ BigInt);
+ }
+ if (words64_count == 0) return MutableBigInt::Zero(isolate);
+ STATIC_ASSERT(kDigitBits == 64 || kDigitBits == 32);
+ int length = (64 / kDigitBits) * words64_count;
+ DCHECK_GT(length, 0);
+ if (kDigitBits == 32 && words[words64_count - 1] <= (1ULL << 32)) length--;
+
+ Handle<MutableBigInt> result;
+ if (!MutableBigInt::New(isolate, length).ToHandle(&result)) {
+ return MaybeHandle<BigInt>();
+ }
+
+ result->set_sign(sign_bit);
+ if (kDigitBits == 64) {
+ for (int i = 0; i < length; ++i) {
+ result->set_digit(i, static_cast<digit_t>(words[i]));
+ }
+ } else {
+ for (int i = 0; i < length; i += 2) {
+ digit_t lo = static_cast<digit_t>(words[i / 2]);
+ digit_t hi = static_cast<digit_t>(words[i / 2] >> 32);
+ result->set_digit(i, lo);
+ if (i + 1 < length) result->set_digit(i + 1, hi);
+ }
+ }
+
+ return MutableBigInt::MakeImmutable(result);
+}
+
+int BigInt::Words64Count() {
+ STATIC_ASSERT(kDigitBits == 64 || kDigitBits == 32);
+ return length() / (64 / kDigitBits) +
+ (kDigitBits == 32 && length() % 2 == 1 ? 1 : 0);
+}
+
+void BigInt::ToWordsArray64(int* sign_bit, int* words64_count,
+ uint64_t* words) {
+ DCHECK_NE(sign_bit, nullptr);
+ DCHECK_NE(words64_count, nullptr);
+ *sign_bit = sign();
+ int available_words = *words64_count;
+ *words64_count = Words64Count();
+ if (available_words == 0) return;
+ DCHECK_NE(words, nullptr);
+
+ int len = length();
+ if (kDigitBits == 64) {
+ for (int i = 0; i < len && i < available_words; ++i) words[i] = digit(i);
+ } else {
+ for (int i = 0; i < len && available_words > 0; i += 2) {
+ uint64_t lo = digit(i);
+ uint64_t hi = (i + 1) < len ? digit(i + 1) : 0;
+ words[i / 2] = lo | (hi << 32);
+ available_words--;
+ }
+ }
+}
+
uint64_t MutableBigInt::GetRawBits(BigIntBase* x, bool* lossless) {
if (lossless != nullptr) *lossless = true;
if (x->is_zero()) return 0;
diff --git a/src/objects/bigint.h b/src/objects/bigint.h
index 7c36a47436..e654738934 100644
--- a/src/objects/bigint.h
+++ b/src/objects/bigint.h
@@ -144,8 +144,13 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase {
static Handle<BigInt> FromInt64(Isolate* isolate, int64_t n);
static Handle<BigInt> FromUint64(Isolate* isolate, uint64_t n);
+ static MaybeHandle<BigInt> FromWords64(Isolate* isolate, int sign_bit,
+ int words64_count,
+ const uint64_t* words);
int64_t AsInt64(bool* lossless = nullptr);
uint64_t AsUint64(bool* lossless = nullptr);
+ int Words64Count();
+ void ToWordsArray64(int* sign_bit, int* words64_count, uint64_t* words);
DECL_CAST(BigInt)
DECL_VERIFIER(BigInt)
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index a827c886ce..953648fe42 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -27805,3 +27805,117 @@ TEST(WasmStreamingAbortNoReject) {
streaming.Abort({});
CHECK_EQ(streaming.GetPromise()->State(), v8::Promise::kPending);
}
+
+TEST(BigIntAPI) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ bool lossless;
+ uint64_t words1[10];
+ uint64_t words2[10];
+
+ {
+ Local<Value> bi = CompileRun("12n");
+ CHECK(bi->IsBigInt());
+
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 12);
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless), 12);
+ CHECK_EQ(lossless, true);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), 12);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), 12);
+ CHECK_EQ(lossless, true);
+ }
+
+ {
+ Local<Value> bi = CompileRun("-12n");
+ CHECK(bi->IsBigInt());
+
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), static_cast<uint64_t>(-12));
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless),
+ static_cast<uint64_t>(-12));
+ CHECK_EQ(lossless, false);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), -12);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), -12);
+ CHECK_EQ(lossless, true);
+ }
+
+ {
+ Local<Value> bi = CompileRun("123456789012345678901234567890n");
+ CHECK(bi->IsBigInt());
+
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 14083847773837265618ULL);
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless),
+ 14083847773837265618ULL);
+ CHECK_EQ(lossless, false);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), -4362896299872285998LL);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless),
+ -4362896299872285998LL);
+ CHECK_EQ(lossless, false);
+ }
+
+ {
+ Local<Value> bi = CompileRun("-123456789012345678901234567890n");
+ CHECK(bi->IsBigInt());
+
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 4362896299872285998LL);
+ CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless),
+ 4362896299872285998LL);
+ CHECK_EQ(lossless, false);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), 4362896299872285998LL);
+ CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), 4362896299872285998LL);
+ CHECK_EQ(lossless, false);
+ }
+
+ {
+ Local<v8::BigInt> bi =
+ v8::BigInt::NewFromWords(env.local(), 0, 0, words1).ToLocalChecked();
+ CHECK_EQ(bi->Uint64Value(), 0);
+ CHECK_EQ(bi->WordCount(), 0);
+ }
+
+ {
+ TryCatch try_catch(isolate);
+ v8::MaybeLocal<v8::BigInt> bi = v8::BigInt::NewFromWords(
+ env.local(), 0, std::numeric_limits<int>::max(), words1);
+ CHECK(bi.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ TryCatch try_catch(isolate);
+ v8::MaybeLocal<v8::BigInt> bi =
+ v8::BigInt::NewFromWords(env.local(), 0, -1, words1);
+ CHECK(bi.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ TryCatch try_catch(isolate);
+ v8::MaybeLocal<v8::BigInt> bi =
+ v8::BigInt::NewFromWords(env.local(), 0, 1 << 30, words1);
+ CHECK(bi.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ }
+
+ for (int sign_bit = 0; sign_bit <= 1; sign_bit++) {
+ words1[0] = 0xffffffff00000000ULL;
+ words1[1] = 0x00000000ffffffffULL;
+ v8::Local<v8::BigInt> bi =
+ v8::BigInt::NewFromWords(env.local(), sign_bit, 2, words1)
+ .ToLocalChecked();
+ CHECK_EQ(bi->Uint64Value(&lossless),
+ sign_bit ? static_cast<uint64_t>(-static_cast<int64_t>(words1[0]))
+ : words1[0]);
+ CHECK_EQ(lossless, false);
+ CHECK_EQ(bi->Int64Value(&lossless), sign_bit
+ ? -static_cast<int64_t>(words1[0])
+ : static_cast<int64_t>(words1[0]));
+ CHECK_EQ(lossless, false);
+ CHECK_EQ(bi->WordCount(), 2);
+ int real_sign_bit;
+ int word_count = arraysize(words2);
+ bi->ToWordsArray(&real_sign_bit, &word_count, words2);
+ CHECK_EQ(real_sign_bit, sign_bit);
+ CHECK_EQ(word_count, 2);
+ }
+}
--
2.17.0
From 847cd77f7dc5efcb651dd389e7b6bf783bc576ac Mon Sep 17 00:00:00 2001
From: Anna Henningsen <anna@addaleax.net>
Date: Sat, 30 Jun 2018 21:09:21 +0200
Subject: [PATCH 07/23] deps: cherry-pick 555c811 from upstream V8
Original commit message:
[api] Switch from `SetBuildEmbedderGraphCallback` to `AddBuildEmbedderGraphCallback`
`SetBuildEmbedderGraphCallback`, unlike `SetWrapperClassInfoProvider`,
assumes a monolithic embedder that can provide all necessary information.
That is not the case for e.g. Node.js, which can e.g. provide multiple Node.js
instances per V8 Isolate, as well as native addons that may allocate resources
on their own.
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: Ib53dfde82416dd69934b08623e27d674a483ac2d
Reviewed-on: https://chromium-review.googlesource.com/1082441
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53545}
Refs: https://github.com/v8/v8/commit/555c811c0d44d9aaaccf8e76059ed24537b3f012
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21741
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
---
include/v8-profiler.h | 22 +++++--
src/api.cc | 22 ++++++-
src/profiler/heap-profiler.cc | 21 ++++--
src/profiler/heap-profiler.h | 12 ++--
test/cctest/test-heap-profiler.cc | 104 ++++++++++++++++++++++++++----
5 files changed, 151 insertions(+), 30 deletions(-)
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 76490afe3c..0e09d8732f 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -640,7 +640,7 @@ class V8_EXPORT AllocationProfile {
* Usage:
* 1) Define derived class of EmbedderGraph::Node for embedder objects.
* 2) Set the build embedder graph callback on the heap profiler using
- * HeapProfiler::SetBuildEmbedderGraphCallback.
+ * HeapProfiler::AddBuildEmbedderGraphCallback.
* 3) In the callback use graph->AddEdge(node1, node2) to add an edge from
* node1 to node2.
* 4) To represent references from/to V8 object, construct V8 nodes using
@@ -740,7 +740,12 @@ class V8_EXPORT HeapProfiler {
* The callback must not trigger garbage collection in V8.
*/
typedef void (*BuildEmbedderGraphCallback)(v8::Isolate* isolate,
- v8::EmbedderGraph* graph);
+ v8::EmbedderGraph* graph,
+ void* data);
+
+ /** TODO(addaleax): Remove */
+ typedef void (*LegacyBuildEmbedderGraphCallback)(v8::Isolate* isolate,
+ v8::EmbedderGraph* graph);
/** Returns the number of snapshots taken. */
int GetSnapshotCount();
@@ -882,15 +887,22 @@ class V8_EXPORT HeapProfiler {
/** Binds a callback to embedder's class ID. */
V8_DEPRECATED(
- "Use SetBuildEmbedderGraphCallback to provide info about embedder nodes",
+ "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes",
void SetWrapperClassInfoProvider(uint16_t class_id,
WrapperInfoCallback callback));
V8_DEPRECATED(
- "Use SetBuildEmbedderGraphCallback to provide info about embedder nodes",
+ "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes",
void SetGetRetainerInfosCallback(GetRetainerInfosCallback callback));
- void SetBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback);
+ V8_DEPRECATE_SOON(
+ "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes",
+ void SetBuildEmbedderGraphCallback(
+ LegacyBuildEmbedderGraphCallback callback));
+ void AddBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback,
+ void* data);
+ void RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback,
+ void* data);
/**
* Default value of persistent handle class ID. Must not be used to
diff --git a/src/api.cc b/src/api.cc
index 66ca4bb96a..87b6b24270 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -10451,9 +10451,25 @@ void HeapProfiler::SetGetRetainerInfosCallback(
}
void HeapProfiler::SetBuildEmbedderGraphCallback(
- BuildEmbedderGraphCallback callback) {
- reinterpret_cast<i::HeapProfiler*>(this)->SetBuildEmbedderGraphCallback(
- callback);
+ LegacyBuildEmbedderGraphCallback callback) {
+ reinterpret_cast<i::HeapProfiler*>(this)->AddBuildEmbedderGraphCallback(
+ [](v8::Isolate* isolate, v8::EmbedderGraph* graph, void* data) {
+ reinterpret_cast<LegacyBuildEmbedderGraphCallback>(data)(isolate,
+ graph);
+ },
+ reinterpret_cast<void*>(callback));
+}
+
+void HeapProfiler::AddBuildEmbedderGraphCallback(
+ BuildEmbedderGraphCallback callback, void* data) {
+ reinterpret_cast<i::HeapProfiler*>(this)->AddBuildEmbedderGraphCallback(
+ callback, data);
+}
+
+void HeapProfiler::RemoveBuildEmbedderGraphCallback(
+ BuildEmbedderGraphCallback callback, void* data) {
+ reinterpret_cast<i::HeapProfiler*>(this)->RemoveBuildEmbedderGraphCallback(
+ callback, data);
}
v8::Testing::StressType internal::Testing::stress_type_ =
diff --git a/src/profiler/heap-profiler.cc b/src/profiler/heap-profiler.cc
index 39808f7047..10645ad161 100644
--- a/src/profiler/heap-profiler.cc
+++ b/src/profiler/heap-profiler.cc
@@ -69,16 +69,25 @@ v8::HeapProfiler::RetainerInfos HeapProfiler::GetRetainerInfos(
return infos;
}
-void HeapProfiler::SetBuildEmbedderGraphCallback(
- v8::HeapProfiler::BuildEmbedderGraphCallback callback) {
- build_embedder_graph_callback_ = callback;
+void HeapProfiler::AddBuildEmbedderGraphCallback(
+ v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) {
+ build_embedder_graph_callbacks_.push_back({callback, data});
+}
+
+void HeapProfiler::RemoveBuildEmbedderGraphCallback(
+ v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) {
+ auto it = std::find(build_embedder_graph_callbacks_.begin(),
+ build_embedder_graph_callbacks_.end(),
+ std::make_pair(callback, data));
+ if (it != build_embedder_graph_callbacks_.end())
+ build_embedder_graph_callbacks_.erase(it);
}
void HeapProfiler::BuildEmbedderGraph(Isolate* isolate,
v8::EmbedderGraph* graph) {
- if (build_embedder_graph_callback_ != nullptr)
- build_embedder_graph_callback_(reinterpret_cast<v8::Isolate*>(isolate),
- graph);
+ for (const auto& cb : build_embedder_graph_callbacks_) {
+ cb.first(reinterpret_cast<v8::Isolate*>(isolate), graph, cb.second);
+ }
}
HeapSnapshot* HeapProfiler::TakeSnapshot(
diff --git a/src/profiler/heap-profiler.h b/src/profiler/heap-profiler.h
index 507dd579bf..fc0b005e1c 100644
--- a/src/profiler/heap-profiler.h
+++ b/src/profiler/heap-profiler.h
@@ -71,11 +71,13 @@ class HeapProfiler : public HeapObjectAllocationTracker {
v8::HeapProfiler::GetRetainerInfosCallback callback);
v8::HeapProfiler::RetainerInfos GetRetainerInfos(Isolate* isolate);
- void SetBuildEmbedderGraphCallback(
- v8::HeapProfiler::BuildEmbedderGraphCallback callback);
+ void AddBuildEmbedderGraphCallback(
+ v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data);
+ void RemoveBuildEmbedderGraphCallback(
+ v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data);
void BuildEmbedderGraph(Isolate* isolate, v8::EmbedderGraph* graph);
bool HasBuildEmbedderGraphCallback() {
- return build_embedder_graph_callback_ != nullptr;
+ return !build_embedder_graph_callbacks_.empty();
}
bool is_tracking_object_moves() const { return is_tracking_object_moves_; }
@@ -103,8 +105,8 @@ class HeapProfiler : public HeapObjectAllocationTracker {
std::unique_ptr<SamplingHeapProfiler> sampling_heap_profiler_;
v8::HeapProfiler::GetRetainerInfosCallback get_retainer_infos_callback_ =
nullptr;
- v8::HeapProfiler::BuildEmbedderGraphCallback build_embedder_graph_callback_ =
- nullptr;
+ std::vector<std::pair<v8::HeapProfiler::BuildEmbedderGraphCallback, void*>>
+ build_embedder_graph_callbacks_;
DISALLOW_COPY_AND_ASSIGN(HeapProfiler);
};
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index ead28c76e8..70dc07d3dc 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -1541,8 +1541,8 @@ class EmbedderGraphBuilder : public v8::PersistentHandleVisitor {
graph->AddNode(std::unique_ptr<Group>(new Group("ccc-group")));
}
- static void BuildEmbedderGraph(v8::Isolate* isolate,
- v8::EmbedderGraph* graph) {
+ static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
+ void* data) {
EmbedderGraphBuilder builder(isolate, graph);
isolate->VisitHandlesWithClassIds(&builder);
}
@@ -1604,8 +1604,8 @@ TEST(HeapSnapshotRetainedObjectInfo) {
v8::HandleScope scope(isolate);
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
- heap_profiler->SetBuildEmbedderGraphCallback(
- EmbedderGraphBuilder::BuildEmbedderGraph);
+ heap_profiler->AddBuildEmbedderGraphCallback(
+ EmbedderGraphBuilder::BuildEmbedderGraph, nullptr);
v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
p_AAA.SetWrapperClassId(1);
v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
@@ -2932,7 +2932,8 @@ class EmbedderRootNode : public EmbedderNode {
// global object.
v8::Local<v8::Value>* global_object_pointer;
-void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph) {
+void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph,
+ void* data) {
using Node = v8::EmbedderGraph::Node;
Node* global_node = graph->V8Node(*global_object_pointer);
Node* embedder_node_A = graph->AddNode(
@@ -2979,12 +2980,92 @@ TEST(EmbedderGraph) {
(isolate->context()->native_context()->global_object())));
global_object_pointer = &global_object;
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- heap_profiler->SetBuildEmbedderGraphCallback(BuildEmbedderGraph);
+ heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraph, nullptr);
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
CHECK(ValidateSnapshot(snapshot));
CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot);
}
+struct GraphBuildingContext {
+ int counter = 0;
+};
+
+void CheckEmbedderGraphSnapshotWithContext(
+ v8::Isolate* isolate, const v8::HeapSnapshot* snapshot,
+ const GraphBuildingContext* context) {
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_GE(context->counter, 1);
+ CHECK_LE(context->counter, 2);
+
+ const v8::HeapGraphNode* embedder_node_A =
+ GetChildByName(global, "EmbedderNodeA");
+ CHECK_EQ(10, GetSize(embedder_node_A));
+
+ const v8::HeapGraphNode* embedder_node_B =
+ GetChildByName(global, "EmbedderNodeB");
+ if (context->counter == 2) {
+ CHECK_NOT_NULL(embedder_node_B);
+ CHECK_EQ(20, GetSize(embedder_node_B));
+ } else {
+ CHECK_NULL(embedder_node_B);
+ }
+}
+
+void BuildEmbedderGraphWithContext(v8::Isolate* v8_isolate,
+ v8::EmbedderGraph* graph, void* data) {
+ using Node = v8::EmbedderGraph::Node;
+ GraphBuildingContext* context = static_cast<GraphBuildingContext*>(data);
+ Node* global_node = graph->V8Node(*global_object_pointer);
+
+ CHECK_GE(context->counter, 0);
+ CHECK_LE(context->counter, 1);
+ switch (context->counter++) {
+ case 0: {
+ Node* embedder_node_A = graph->AddNode(
+ std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
+ graph->AddEdge(global_node, embedder_node_A);
+ break;
+ }
+ case 1: {
+ Node* embedder_node_B = graph->AddNode(
+ std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
+ graph->AddEdge(global_node, embedder_node_B);
+ break;
+ }
+ }
+}
+
+TEST(EmbedderGraphMultipleCallbacks) {
+ i::FLAG_heap_profiler_use_embedder_graph = true;
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
+ v8::Local<v8::Value> global_object =
+ v8::Utils::ToLocal(i::Handle<i::JSObject>(
+ (isolate->context()->native_context()->global_object())));
+ global_object_pointer = &global_object;
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ GraphBuildingContext context;
+
+ heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
+ &context);
+ heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
+ &context);
+ const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
+ CHECK_EQ(context.counter, 2);
+ CHECK(ValidateSnapshot(snapshot));
+ CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
+
+ heap_profiler->RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
+ &context);
+ context.counter = 0;
+
+ snapshot = heap_profiler->TakeHeapSnapshot();
+ CHECK_EQ(context.counter, 1);
+ CHECK(ValidateSnapshot(snapshot));
+ CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
+}
+
TEST(StrongHandleAnnotation) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
@@ -3010,7 +3091,7 @@ TEST(StrongHandleAnnotation) {
}
void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate,
- v8::EmbedderGraph* graph) {
+ v8::EmbedderGraph* graph, void* data) {
using Node = v8::EmbedderGraph::Node;
Node* global_node = graph->V8Node(*global_object_pointer);
Node* wrapper_node = graph->AddNode(
@@ -3041,8 +3122,8 @@ TEST(EmbedderGraphWithWrapperNode) {
(isolate->context()->native_context()->global_object())));
global_object_pointer = &global_object;
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- heap_profiler->SetBuildEmbedderGraphCallback(
- BuildEmbedderGraphWithWrapperNode);
+ heap_profiler->AddBuildEmbedderGraphCallback(
+ BuildEmbedderGraphWithWrapperNode, nullptr);
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
@@ -3080,7 +3161,7 @@ class EmbedderNodeWithPrefix : public v8::EmbedderGraph::Node {
};
void BuildEmbedderGraphWithPrefix(v8::Isolate* v8_isolate,
- v8::EmbedderGraph* graph) {
+ v8::EmbedderGraph* graph, void* data) {
using Node = v8::EmbedderGraph::Node;
Node* global_node = graph->V8Node(*global_object_pointer);
Node* node = graph->AddNode(
@@ -3098,7 +3179,8 @@ TEST(EmbedderGraphWithPrefix) {
(isolate->context()->native_context()->global_object())));
global_object_pointer = &global_object;
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- heap_profiler->SetBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix);
+ heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix,
+ nullptr);
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
--
2.17.0
From f02ce4872dd6c81019ec791e9715662727624171 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Mon, 16 Jul 2018 21:57:05 +0200
Subject: [PATCH 08/23] deps: cherry-pick 2075910 from upstream V8
Original commit message:
[turbofan] Remove optimization of default Promise capability functions.
The JSCallReducer could in theory inline the default resolve and reject
functions passed to the executor in the Promise constructor. But that
inlining is almost never triggered because we don't have SFI based feedback
in the CallIC. Also the use of the Promise constructor is discouraged,
so we shouldn't really need to squeeze the last bit of performance out
of this even in the future.
Getting rid of this optimization will make significantly easier to
implement the Swallowed Rejection Hook, as there's less churn on the
TurboFan side then.
Bug: v8:7919
Change-Id: If0c54f1c6c7ce95686cd74232be6b8693ac688c9
Reviewed-on: https://chromium-review.googlesource.com/1125926
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54210}
Refs: https://github.com/v8/v8/commit/2075910f3d070159bebd80e128dd09fdd87be56e
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21838
Refs: https://github.com/nodejs/promises-debugging/issues/8
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
---
src/compiler/js-call-reducer.cc | 118 --------------------------------
src/compiler/js-call-reducer.h | 2 -
2 files changed, 120 deletions(-)
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
index 2acdf0945e..45664593c5 100644
--- a/src/compiler/js-call-reducer.cc
+++ b/src/compiler/js-call-reducer.cc
@@ -3551,10 +3551,6 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceAsyncFunctionPromiseCreate(node);
case Builtins::kAsyncFunctionPromiseRelease:
return ReduceAsyncFunctionPromiseRelease(node);
- case Builtins::kPromiseCapabilityDefaultReject:
- return ReducePromiseCapabilityDefaultReject(node);
- case Builtins::kPromiseCapabilityDefaultResolve:
- return ReducePromiseCapabilityDefaultResolve(node);
case Builtins::kPromiseInternalConstructor:
return ReducePromiseInternalConstructor(node);
case Builtins::kPromiseInternalReject:
@@ -5310,120 +5306,6 @@ Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) {
return Replace(value);
}
-// ES section #sec-promise-reject-functions
-Reduction JSCallReducer::ReducePromiseCapabilityDefaultReject(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
- Node* target = NodeProperties::GetValueInput(node, 0);
- Node* resolution = node->op()->ValueInputCount() > 2
- ? NodeProperties::GetValueInput(node, 2)
- : jsgraph()->UndefinedConstant();
- Node* frame_state = NodeProperties::GetFrameStateInput(node);
- Node* effect = NodeProperties::GetEffectInput(node);
- Node* control = NodeProperties::GetControlInput(node);
-
- // We need to execute in the {target}s context.
- Node* context = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
- effect, control);
-
- // Grab the promise closed over by {target}.
- Node* promise = effect =
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
- PromiseBuiltinsAssembler::kPromiseSlot)),
- context, effect, control);
-
- // Check if the {promise} is still pending or already settled.
- Node* check = graph()->NewNode(simplified()->ReferenceEqual(), promise,
- jsgraph()->UndefinedConstant());
- Node* branch =
- graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
-
- Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
- Node* etrue = effect;
-
- Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
- Node* efalse = effect;
- {
- // Mark the {promise} as settled.
- efalse = graph()->NewNode(
- simplified()->StoreField(AccessBuilder::ForContextSlot(
- PromiseBuiltinsAssembler::kPromiseSlot)),
- context, jsgraph()->UndefinedConstant(), efalse, if_false);
-
- // Check if we should emit a debug event.
- Node* debug_event = efalse =
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
- PromiseBuiltinsAssembler::kDebugEventSlot)),
- context, efalse, if_false);
-
- // Actually reject the {promise}.
- efalse =
- graph()->NewNode(javascript()->RejectPromise(), promise, resolution,
- debug_event, context, frame_state, efalse, if_false);
- }
-
- control = graph()->NewNode(common()->Merge(2), if_true, if_false);
- effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
-
- Node* value = jsgraph()->UndefinedConstant();
- ReplaceWithValue(node, value, effect, control);
- return Replace(value);
-}
-
-// ES section #sec-promise-resolve-functions
-Reduction JSCallReducer::ReducePromiseCapabilityDefaultResolve(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
- Node* target = NodeProperties::GetValueInput(node, 0);
- Node* resolution = node->op()->ValueInputCount() > 2
- ? NodeProperties::GetValueInput(node, 2)
- : jsgraph()->UndefinedConstant();
- Node* frame_state = NodeProperties::GetFrameStateInput(node);
- Node* effect = NodeProperties::GetEffectInput(node);
- Node* control = NodeProperties::GetControlInput(node);
-
- // We need to execute in the {target}s context.
- Node* context = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
- effect, control);
-
- // Grab the promise closed over by {target}.
- Node* promise = effect =
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
- PromiseBuiltinsAssembler::kPromiseSlot)),
- context, effect, control);
-
- // Check if the {promise} is still pending or already settled.
- Node* check = graph()->NewNode(simplified()->ReferenceEqual(), promise,
- jsgraph()->UndefinedConstant());
- Node* branch =
- graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
-
- Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
- Node* etrue = effect;
-
- Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
- Node* efalse = effect;
- {
- // Mark the {promise} as settled.
- efalse = graph()->NewNode(
- simplified()->StoreField(AccessBuilder::ForContextSlot(
- PromiseBuiltinsAssembler::kPromiseSlot)),
- context, jsgraph()->UndefinedConstant(), efalse, if_false);
-
- // Actually resolve the {promise}.
- efalse =
- graph()->NewNode(javascript()->ResolvePromise(), promise, resolution,
- context, frame_state, efalse, if_false);
- }
-
- control = graph()->NewNode(common()->Merge(2), if_true, if_false);
- effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
-
- Node* value = jsgraph()->UndefinedConstant();
- ReplaceWithValue(node, value, effect, control);
- return Replace(value);
-}
-
Node* JSCallReducer::CreateArtificialFrameState(
Node* node, Node* outer_frame_state, int parameter_count,
BailoutId bailout_id, FrameStateType frame_state_type,
diff --git a/src/compiler/js-call-reducer.h b/src/compiler/js-call-reducer.h
index 04a9cf920e..15228032cf 100644
--- a/src/compiler/js-call-reducer.h
+++ b/src/compiler/js-call-reducer.h
@@ -132,8 +132,6 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceAsyncFunctionPromiseCreate(Node* node);
Reduction ReduceAsyncFunctionPromiseRelease(Node* node);
- Reduction ReducePromiseCapabilityDefaultReject(Node* node);
- Reduction ReducePromiseCapabilityDefaultResolve(Node* node);
Reduction ReducePromiseConstructor(Node* node);
Reduction ReducePromiseInternalConstructor(Node* node);
Reduction ReducePromiseInternalReject(Node* node);
--
2.17.0
From 6de088473d805d0a69c41d56d528b4f531cd7d4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Mon, 16 Jul 2018 22:03:01 +0200
Subject: [PATCH 09/23] deps: cherry-pick 907d7bc from upstream V8
Original commit message:
[promise] Implement Swallowed Rejection Hook.
This extends the current Promise Rejection Hook with two new events
kPromiseRejectAfterResolved
kPromiseResolveAfterResolved
which are used to detect (and signal) misuse of the Promise constructor.
Specifically the common bug like
new Promise((res, rej) => {
res(1);
throw new Error("something")
});
where the error is silently swallowed by the Promise constructor without
the user ever noticing can be caught via this hook.
Doc: https://goo.gl/2stLUY
Bug: v8:7919
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I890a7e766cdd1be88db94844fb744f72823dba33
Reviewed-on: https://chromium-review.googlesource.com/1126099
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54309}
Refs: https://github.com/v8/v8/commit/907d7bcd18c13a04a14eea6699e54167494bf9f9
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21838
Refs: https://github.com/nodejs/promises-debugging/issues/8
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
---
include/v8.h | 4 +-
src/builtins/builtins-promise-gen.cc | 34 ++++++--
src/builtins/builtins-promise-gen.h | 7 +-
src/compiler/js-call-reducer.cc | 4 +
src/isolate.cc | 3 +-
src/runtime/runtime-promise.cc | 20 +++++
src/runtime/runtime.h | 4 +-
test/cctest/test-api.cc | 116 ++++++++++++++++++++-------
8 files changed, 148 insertions(+), 44 deletions(-)
diff --git a/include/v8.h b/include/v8.h
index 6c56f918e3..2bacd1a480 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -6520,7 +6520,9 @@ typedef void (*PromiseHook)(PromiseHookType type, Local<Promise> promise,
// --- Promise Reject Callback ---
enum PromiseRejectEvent {
kPromiseRejectWithNoHandler = 0,
- kPromiseHandlerAddedAfterReject = 1
+ kPromiseHandlerAddedAfterReject = 1,
+ kPromiseRejectAfterResolved = 2,
+ kPromiseResolveAfterResolved = 3,
};
class PromiseRejectMessage {
diff --git a/src/builtins/builtins-promise-gen.cc b/src/builtins/builtins-promise-gen.cc
index 6dcf9f5372..aef9e40ac8 100644
--- a/src/builtins/builtins-promise-gen.cc
+++ b/src/builtins/builtins-promise-gen.cc
@@ -246,6 +246,8 @@ Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
Node* const context =
CreatePromiseContext(native_context, kPromiseContextLength);
StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise);
+ StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
+ FalseConstant());
StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event);
return context;
}
@@ -736,17 +738,27 @@ TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) {
Node* const promise = LoadContextElement(context, kPromiseSlot);
// 3. Let alreadyResolved be F.[[AlreadyResolved]].
+ Label if_already_resolved(this, Label::kDeferred);
+ Node* const already_resolved =
+ LoadContextElement(context, kAlreadyResolvedSlot);
+
// 4. If alreadyResolved.[[Value]] is true, return undefined.
- // We use undefined as a marker for the [[AlreadyResolved]] state.
- ReturnIf(IsUndefined(promise), UndefinedConstant());
+ GotoIf(IsTrue(already_resolved), &if_already_resolved);
// 5. Set alreadyResolved.[[Value]] to true.
- StoreContextElementNoWriteBarrier(context, kPromiseSlot, UndefinedConstant());
+ StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
+ TrueConstant());
// 6. Return RejectPromise(promise, reason).
Node* const debug_event = LoadContextElement(context, kDebugEventSlot);
Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
debug_event));
+
+ BIND(&if_already_resolved);
+ {
+ Return(CallRuntime(Runtime::kPromiseRejectAfterResolved, context, promise,
+ reason));
+ }
}
// ES #sec-promise-resolve-functions
@@ -758,16 +770,26 @@ TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
Node* const promise = LoadContextElement(context, kPromiseSlot);
// 3. Let alreadyResolved be F.[[AlreadyResolved]].
+ Label if_already_resolved(this, Label::kDeferred);
+ Node* const already_resolved =
+ LoadContextElement(context, kAlreadyResolvedSlot);
+
// 4. If alreadyResolved.[[Value]] is true, return undefined.
- // We use undefined as a marker for the [[AlreadyResolved]] state.
- ReturnIf(IsUndefined(promise), UndefinedConstant());
+ GotoIf(IsTrue(already_resolved), &if_already_resolved);
// 5. Set alreadyResolved.[[Value]] to true.
- StoreContextElementNoWriteBarrier(context, kPromiseSlot, UndefinedConstant());
+ StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
+ TrueConstant());
// The rest of the logic (and the catch prediction) is
// encapsulated in the dedicated ResolvePromise builtin.
Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution));
+
+ BIND(&if_already_resolved);
+ {
+ Return(CallRuntime(Runtime::kPromiseResolveAfterResolved, context, promise,
+ resolution));
+ }
}
TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
diff --git a/src/builtins/builtins-promise-gen.h b/src/builtins/builtins-promise-gen.h
index 46fb9338b4..4954b383fe 100644
--- a/src/builtins/builtins-promise-gen.h
+++ b/src/builtins/builtins-promise-gen.h
@@ -17,11 +17,12 @@ typedef compiler::CodeAssemblerState CodeAssemblerState;
class PromiseBuiltinsAssembler : public CodeStubAssembler {
public:
enum PromiseResolvingFunctionContextSlot {
- // The promise which resolve/reject callbacks fulfill. If this is
- // undefined, then we've already visited this callback and it
- // should be a no-op.
+ // The promise which resolve/reject callbacks fulfill.
kPromiseSlot = Context::MIN_CONTEXT_SLOTS,
+ // Whether the callback was already invoked.
+ kAlreadyResolvedSlot,
+
// Whether to trigger a debug event or not. Used in catch
// prediction.
kDebugEventSlot,
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
index 45664593c5..1bf59adb4a 100644
--- a/src/compiler/js-call-reducer.cc
+++ b/src/compiler/js-call-reducer.cc
@@ -5406,6 +5406,10 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
promise_context, promise, effect, control);
+ effect = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForContextSlot(
+ PromiseBuiltinsAssembler::kAlreadyResolvedSlot)),
+ promise_context, jsgraph()->FalseConstant(), effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kDebugEventSlot)),
diff --git a/src/isolate.cc b/src/isolate.cc
index dd66479f73..0e457df4d1 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -3872,10 +3872,9 @@ void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
void Isolate::ReportPromiseReject(Handle<JSPromise> promise,
Handle<Object> value,
v8::PromiseRejectEvent event) {
- DCHECK_EQ(v8::Promise::kRejected, promise->status());
if (promise_reject_callback_ == nullptr) return;
Handle<FixedArray> stack_trace;
- if (event == v8::kPromiseRejectWithNoHandler && value->IsJSObject()) {
+ if (event != v8::kPromiseHandlerAddedAfterReject && value->IsJSObject()) {
stack_trace = GetDetailedStackTrace(Handle<JSObject>::cast(value));
}
promise_reject_callback_(v8::PromiseRejectMessage(
diff --git a/src/runtime/runtime-promise.cc b/src/runtime/runtime-promise.cc
index f5b9db3c02..6c4f7d69eb 100644
--- a/src/runtime/runtime-promise.cc
+++ b/src/runtime/runtime-promise.cc
@@ -38,6 +38,26 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
return isolate->heap()->undefined_value();
}
+RUNTIME_FUNCTION(Runtime_PromiseRejectAfterResolved) {
+ DCHECK_EQ(2, args.length());
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1);
+ isolate->ReportPromiseReject(promise, reason,
+ v8::kPromiseRejectAfterResolved);
+ return isolate->heap()->undefined_value();
+}
+
+RUNTIME_FUNCTION(Runtime_PromiseResolveAfterResolved) {
+ DCHECK_EQ(2, args.length());
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, resolution, 1);
+ isolate->ReportPromiseReject(promise, resolution,
+ v8::kPromiseResolveAfterResolved);
+ return isolate->heap()->undefined_value();
+}
+
RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index f8997c50ad..382bac7d2a 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -432,7 +432,9 @@ namespace internal {
F(PromiseRevokeReject, 1, 1) \
F(PromiseStatus, 1, 1) \
F(RejectPromise, 3, 1) \
- F(ResolvePromise, 2, 1)
+ F(ResolvePromise, 2, 1) \
+ F(PromiseRejectAfterResolved, 2, 1) \
+ F(PromiseResolveAfterResolved, 2, 1)
#define FOR_EACH_INTRINSIC_PROXY(F) \
F(CheckProxyGetSetTrapResult, 2, 1) \
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 953648fe42..366b940d61 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -17700,6 +17700,8 @@ TEST(RethrowBogusErrorStackTrace) {
v8::PromiseRejectEvent reject_event = v8::kPromiseRejectWithNoHandler;
int promise_reject_counter = 0;
int promise_revoke_counter = 0;
+int promise_reject_after_resolved_counter = 0;
+int promise_resolve_after_resolved_counter = 0;
int promise_reject_msg_line_number = -1;
int promise_reject_msg_column_number = -1;
int promise_reject_line_number = -1;
@@ -17709,40 +17711,56 @@ int promise_reject_frame_count = -1;
void PromiseRejectCallback(v8::PromiseRejectMessage reject_message) {
v8::Local<v8::Object> global = CcTest::global();
v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
- CHECK_EQ(v8::Promise::PromiseState::kRejected,
+ CHECK_NE(v8::Promise::PromiseState::kPending,
reject_message.GetPromise()->State());
- if (reject_message.GetEvent() == v8::kPromiseRejectWithNoHandler) {
- promise_reject_counter++;
- global->Set(context, v8_str("rejected"), reject_message.GetPromise())
- .FromJust();
- global->Set(context, v8_str("value"), reject_message.GetValue()).FromJust();
- v8::Local<v8::Message> message = v8::Exception::CreateMessage(
- CcTest::isolate(), reject_message.GetValue());
- v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
-
- promise_reject_msg_line_number = message->GetLineNumber(context).FromJust();
- promise_reject_msg_column_number =
- message->GetStartColumn(context).FromJust() + 1;
-
- if (!stack_trace.IsEmpty()) {
- promise_reject_frame_count = stack_trace->GetFrameCount();
- if (promise_reject_frame_count > 0) {
- CHECK(stack_trace->GetFrame(0)
- ->GetScriptName()
- ->Equals(context, v8_str("pro"))
- .FromJust());
- promise_reject_line_number = stack_trace->GetFrame(0)->GetLineNumber();
- promise_reject_column_number = stack_trace->GetFrame(0)->GetColumn();
- } else {
- promise_reject_line_number = -1;
- promise_reject_column_number = -1;
+ switch (reject_message.GetEvent()) {
+ case v8::kPromiseRejectWithNoHandler: {
+ promise_reject_counter++;
+ global->Set(context, v8_str("rejected"), reject_message.GetPromise())
+ .FromJust();
+ global->Set(context, v8_str("value"), reject_message.GetValue())
+ .FromJust();
+ v8::Local<v8::Message> message = v8::Exception::CreateMessage(
+ CcTest::isolate(), reject_message.GetValue());
+ v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
+
+ promise_reject_msg_line_number =
+ message->GetLineNumber(context).FromJust();
+ promise_reject_msg_column_number =
+ message->GetStartColumn(context).FromJust() + 1;
+
+ if (!stack_trace.IsEmpty()) {
+ promise_reject_frame_count = stack_trace->GetFrameCount();
+ if (promise_reject_frame_count > 0) {
+ CHECK(stack_trace->GetFrame(0)
+ ->GetScriptName()
+ ->Equals(context, v8_str("pro"))
+ .FromJust());
+ promise_reject_line_number =
+ stack_trace->GetFrame(0)->GetLineNumber();
+ promise_reject_column_number = stack_trace->GetFrame(0)->GetColumn();
+ } else {
+ promise_reject_line_number = -1;
+ promise_reject_column_number = -1;
+ }
}
+ break;
+ }
+ case v8::kPromiseHandlerAddedAfterReject: {
+ promise_revoke_counter++;
+ global->Set(context, v8_str("revoked"), reject_message.GetPromise())
+ .FromJust();
+ CHECK(reject_message.GetValue().IsEmpty());
+ break;
+ }
+ case v8::kPromiseRejectAfterResolved: {
+ promise_reject_after_resolved_counter++;
+ break;
+ }
+ case v8::kPromiseResolveAfterResolved: {
+ promise_resolve_after_resolved_counter++;
+ break;
}
- } else {
- promise_revoke_counter++;
- global->Set(context, v8_str("revoked"), reject_message.GetPromise())
- .FromJust();
- CHECK(reject_message.GetValue().IsEmpty());
}
}
@@ -17765,6 +17783,8 @@ v8::Local<v8::Value> RejectValue() {
void ResetPromiseStates() {
promise_reject_counter = 0;
promise_revoke_counter = 0;
+ promise_reject_after_resolved_counter = 0;
+ promise_resolve_after_resolved_counter = 0;
promise_reject_msg_line_number = -1;
promise_reject_msg_column_number = -1;
promise_reject_line_number = -1;
@@ -17990,6 +18010,40 @@ TEST(PromiseRejectCallback) {
CHECK_EQ(0, promise_revoke_counter);
CHECK(RejectValue()->Equals(env.local(), v8_str("sss")).FromJust());
+ ResetPromiseStates();
+
+ // Swallowed exceptions in the Promise constructor.
+ CompileRun(
+ "var v0 = new Promise(\n"
+ " function(res, rej) {\n"
+ " res(1);\n"
+ " throw new Error();\n"
+ " }\n"
+ ");\n");
+ CHECK(!GetPromise("v0")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK_EQ(1, promise_reject_after_resolved_counter);
+ CHECK_EQ(0, promise_resolve_after_resolved_counter);
+
+ ResetPromiseStates();
+
+ // Duplication resolve.
+ CompileRun(
+ "var r;\n"
+ "var y0 = new Promise(\n"
+ " function(res, rej) {\n"
+ " r = res;\n"
+ " throw new Error();\n"
+ " }\n"
+ ");\n"
+ "r(1);\n");
+ CHECK(!GetPromise("y0")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK_EQ(0, promise_reject_after_resolved_counter);
+ CHECK_EQ(1, promise_resolve_after_resolved_counter);
+
// Test stack frames.
env->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(true);
--
2.17.0
From da4cc303d8fcfca16961c7913deb02db0e5447c2 Mon Sep 17 00:00:00 2001
From: James M Snell <jasnell@gmail.com>
Date: Thu, 19 Jul 2018 16:37:41 -0700
Subject: [PATCH 10/23] deps: V8: Backport of 0dd3390 from upstream
Original commit message:
Reland "[builtins] Add %IsTraceCategoryEnabled and %Trace builtins"
This is a reland of 8d4572a
Original change's description:
> [builtins] Add %IsTraceCategoryEnabled and %Trace builtins
>
> Adds the builtin Trace and IsTraceCategoryEnabled functions
> exposed via extra bindings. These are intended to use by
> embedders to allow basic trace event support from JavaScript.
>
> ```js
> isTraceCategoryEnabled('v8.some-category')
>
> trace('e'.charCodeAt(0), 'v8.some-category',
> 'Foo', 0, { abc: 'xyz'})
> ```
>
> Bug: v8:7851
> Change-Id: I7bfb9bb059efdf87d92a56a0aae326650730c250
> Reviewed-on: chromium-review.googlesource.com/1103294
> Commit-Queue: Yang Guo <yangguo@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Reviewed-by: Fadi Meawad <fmeawad@chromium.org>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#54121}
TBR=cbruni@chromium.org
Bug: v8:7851
Change-Id: Id063754b2834b3b6a2b2654e76e8637bcd6aa5f8
Reviewed-on: chromium-review.googlesource.com/1137071
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54532}
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21899
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
---
AUTHORS | 1 +
BUILD.gn | 1 +
src/bootstrapper.cc | 9 ++
src/builtins/builtins-definitions.h | 6 +-
src/builtins/builtins-trace.cc | 191 ++++++++++++++++++++++++++++
src/debug/debug-evaluate.cc | 3 +
src/messages.h | 9 +-
test/cctest/test-trace-event.cc | 134 ++++++++++++++++++-
8 files changed, 351 insertions(+), 3 deletions(-)
create mode 100644 src/builtins/builtins-trace.cc
diff --git a/AUTHORS b/AUTHORS
index ba54db5505..e920bbf42b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -86,6 +86,7 @@ Jan de Mooij <jandemooij@gmail.com>
Jan Krems <jan.krems@gmail.com>
Jay Freeman <saurik@saurik.com>
James Pike <g00gle@chilon.net>
+James M Snell <jasnell@gmail.com>
Jianghua Yang <jianghua.yjh@alibaba-inc.com>
Joel Stanley <joel@jms.id.au>
Johan Bergström <johan@bergstroem.nu>
diff --git a/BUILD.gn b/BUILD.gn
index 456a318c1c..f00a8280c1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1539,6 +1539,7 @@ v8_source_set("v8_base") {
"src/builtins/builtins-sharedarraybuffer.cc",
"src/builtins/builtins-string.cc",
"src/builtins/builtins-symbol.cc",
+ "src/builtins/builtins-trace.cc",
"src/builtins/builtins-typed-array.cc",
"src/builtins/builtins-utils.h",
"src/builtins/builtins.cc",
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index ed8fd72c91..548ef5109a 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -4981,6 +4981,15 @@ bool Genesis::InstallExtraNatives() {
Handle<JSObject> extras_binding =
factory()->NewJSObject(isolate()->object_function());
+
+ // binding.isTraceCategoryenabled(category)
+ SimpleInstallFunction(extras_binding, "isTraceCategoryEnabled",
+ Builtins::kIsTraceCategoryEnabled, 1, true);
+
+ // binding.trace(phase, category, name, id, data)
+ SimpleInstallFunction(extras_binding, "trace", Builtins::kTrace, 5,
+ true);
+
native_context()->set_extras_binding_object(*extras_binding);
for (int i = ExtraNatives::GetDebuggerCount();
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 3da81f17a4..4a4b17006c 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -1269,7 +1269,11 @@ namespace internal {
/* Miscellaneous */ \
ASM(DoubleToI) \
TFC(GetProperty, GetProperty, 1) \
- ASM(MathPowInternal)
+ ASM(MathPowInternal) \
+ \
+ /* Trace */ \
+ CPP(IsTraceCategoryEnabled) \
+ CPP(Trace)
#ifdef V8_INTL_SUPPORT
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
diff --git a/src/builtins/builtins-trace.cc b/src/builtins/builtins-trace.cc
new file mode 100644
index 0000000000..a10c136338
--- /dev/null
+++ b/src/builtins/builtins-trace.cc
@@ -0,0 +1,191 @@
+// Copyright 2018 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/api.h"
+#include "src/builtins/builtins-utils.h"
+#include "src/builtins/builtins.h"
+#include "src/counters.h"
+#include "src/json-stringifier.h"
+#include "src/objects-inl.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+using v8::tracing::TracedValue;
+
+#define MAX_STACK_LENGTH 100
+
+class MaybeUtf8 {
+ public:
+ explicit MaybeUtf8(Isolate* isolate, Handle<String> string) : buf_(data_) {
+ string = String::Flatten(string);
+ int len;
+ if (string->IsOneByteRepresentation()) {
+ // Technically this allows unescaped latin1 characters but the trace
+ // events mechanism currently does the same and the current consuming
+ // tools are tolerant of it. A more correct approach here would be to
+ // escape non-ascii characters but this is easier and faster.
+ len = string->length();
+ AllocateSufficientSpace(len);
+ if (len > 0) {
+ // Why copy? Well, the trace event mechanism requires null-terminated
+ // strings, the bytes we get from SeqOneByteString are not. buf_ is
+ // guaranteed to be null terminated.
+ memcpy(buf_, Handle<SeqOneByteString>::cast(string)->GetChars(), len);
+ }
+ } else {
+ Local<v8::String> local = Utils::ToLocal(string);
+ len = local->Utf8Length();
+ AllocateSufficientSpace(len);
+ if (len > 0) {
+ local->WriteUtf8(reinterpret_cast<char*>(buf_));
+ }
+ }
+ buf_[len] = 0;
+ }
+ const char* operator*() const { return reinterpret_cast<const char*>(buf_); }
+
+ private:
+ void AllocateSufficientSpace(int len) {
+ if (len + 1 > MAX_STACK_LENGTH) {
+ allocated_.reset(new uint8_t[len + 1]);
+ buf_ = allocated_.get();
+ }
+ }
+
+ // In the most common cases, the buffer here will be stack allocated.
+ // A heap allocation will only occur if the data is more than MAX_STACK_LENGTH
+ // Given that this is used primarily for trace event categories and names,
+ // the MAX_STACK_LENGTH should be more than enough.
+ uint8_t* buf_;
+ uint8_t data_[MAX_STACK_LENGTH];
+ std::unique_ptr<uint8_t> allocated_;
+};
+
+class JsonTraceValue : public ConvertableToTraceFormat {
+ public:
+ explicit JsonTraceValue(Isolate* isolate, Handle<String> object) {
+ // object is a JSON string serialized using JSON.stringify() from within
+ // the BUILTIN(Trace) method. This may (likely) contain UTF8 values so
+ // to grab the appropriate buffer data we have to serialize it out. We
+ // hold on to the bits until the AppendAsTraceFormat method is called.
+ MaybeUtf8 data(isolate, object);
+ data_ = *data;
+ }
+
+ void AppendAsTraceFormat(std::string* out) const override { *out += data_; }
+
+ private:
+ std::string data_;
+};
+
+const uint8_t* GetCategoryGroupEnabled(Isolate* isolate,
+ Handle<String> string) {
+ MaybeUtf8 category(isolate, string);
+ return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(*category);
+}
+
+#undef MAX_STACK_LENGTH
+
+} // namespace
+
+// Builins::kIsTraceCategoryEnabled(category) : bool
+BUILTIN(IsTraceCategoryEnabled) {
+ HandleScope scope(isolate);
+ Handle<Object> category = args.atOrUndefined(isolate, 1);
+ if (!category->IsString()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError));
+ }
+ return isolate->heap()->ToBoolean(
+ *GetCategoryGroupEnabled(isolate, Handle<String>::cast(category)));
+}
+
+// Builtins::kTrace(phase, category, name, id, data) : bool
+BUILTIN(Trace) {
+ HandleScope handle_scope(isolate);
+
+ Handle<Object> phase_arg = args.atOrUndefined(isolate, 1);
+ Handle<Object> category = args.atOrUndefined(isolate, 2);
+ Handle<Object> name_arg = args.atOrUndefined(isolate, 3);
+ Handle<Object> id_arg = args.atOrUndefined(isolate, 4);
+ Handle<Object> data_arg = args.atOrUndefined(isolate, 5);
+
+ const uint8_t* category_group_enabled =
+ GetCategoryGroupEnabled(isolate, Handle<String>::cast(category));
+
+ // Exit early if the category group is not enabled.
+ if (!*category_group_enabled) {
+ return isolate->heap()->false_value();
+ }
+
+ if (!phase_arg->IsNumber()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError));
+ }
+ if (!category->IsString()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError));
+ }
+ if (!name_arg->IsString()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventNameError));
+ }
+
+ uint32_t flags = TRACE_EVENT_FLAG_COPY;
+ int32_t id = 0;
+ if (!id_arg->IsNullOrUndefined(isolate)) {
+ if (!id_arg->IsNumber()) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventIDError));
+ }
+ flags |= TRACE_EVENT_FLAG_HAS_ID;
+ id = DoubleToInt32(id_arg->Number());
+ }
+
+ Handle<String> name_str = Handle<String>::cast(name_arg);
+ if (name_str->length() == 0) {
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate, NewTypeError(MessageTemplate::kTraceEventNameLengthError));
+ }
+ MaybeUtf8 name(isolate, name_str);
+
+ // We support passing one additional trace event argument with the
+ // name "data". Any JSON serializable value may be passed.
+ static const char* arg_name = "data";
+ int32_t num_args = 0;
+ uint8_t arg_type;
+ uint64_t arg_value;
+
+ if (!data_arg->IsUndefined(isolate)) {
+ // Serializes the data argument as a JSON string, which is then
+ // copied into an object. This eliminates duplicated code but
+ // could have perf costs. It is also subject to all the same
+ // limitations as JSON.stringify() as it relates to circular
+ // references and value limitations (e.g. BigInt is not supported).
+ JsonStringifier stringifier(isolate);
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ stringifier.Stringify(data_arg, isolate->factory()->undefined_value(),
+ isolate->factory()->undefined_value()));
+ std::unique_ptr<JsonTraceValue> traced_value;
+ traced_value.reset(
+ new JsonTraceValue(isolate, Handle<String>::cast(result)));
+ tracing::SetTraceValue(std::move(traced_value), &arg_type, &arg_value);
+ num_args++;
+ }
+
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ static_cast<char>(DoubleToInt32(phase_arg->Number())),
+ category_group_enabled, *name, tracing::kGlobalScope, id, tracing::kNoId,
+ num_args, &arg_name, &arg_type, &arg_value, flags);
+
+ return isolate->heap()->true_value();
+}
+
+} // namespace internal
+} // namespace v8
diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc
index dd63a0187c..505bbad2dd 100644
--- a/src/debug/debug-evaluate.cc
+++ b/src/debug/debug-evaluate.cc
@@ -630,6 +630,9 @@ SharedFunctionInfo::SideEffectState BuiltinGetSideEffectState(
case Builtins::kArrayMap:
case Builtins::kArrayReduce:
case Builtins::kArrayReduceRight:
+ // Trace builtins
+ case Builtins::kIsTraceCategoryEnabled:
+ case Builtins::kTrace:
// TypedArray builtins.
case Builtins::kTypedArrayConstructor:
case Builtins::kTypedArrayPrototypeBuffer:
diff --git a/src/messages.h b/src/messages.h
index 32df6d72e9..c4877ddf17 100644
--- a/src/messages.h
+++ b/src/messages.h
@@ -754,7 +754,14 @@ class ErrorUtils : public AllStatic {
T(DataCloneDeserializationError, "Unable to deserialize cloned data.") \
T(DataCloneDeserializationVersionError, \
"Unable to deserialize cloned data due to invalid or unsupported " \
- "version.")
+ "version.") \
+ /* Builtins-Trace Errors */ \
+ T(TraceEventCategoryError, "Trace event category must be a string.") \
+ T(TraceEventNameError, "Trace event name must be a string.") \
+ T(TraceEventNameLengthError, \
+ "Trace event name must not be an empty string.") \
+ T(TraceEventPhaseError, "Trace event phase must be a number.") \
+ T(TraceEventIDError, "Trace event id must be a number.")
class MessageTemplate {
public:
diff --git a/test/cctest/test-trace-event.cc b/test/cctest/test-trace-event.cc
index 7b736b907d..47545af37f 100644
--- a/test/cctest/test-trace-event.cc
+++ b/test/cctest/test-trace-event.cc
@@ -75,7 +75,7 @@ class MockTracingController : public v8::TracingController {
const char* name, uint64_t handle) override {}
const uint8_t* GetCategoryGroupEnabled(const char* name) override {
- if (strcmp(name, "v8-cat")) {
+ if (strncmp(name, "v8-cat", 6)) {
static uint8_t no = 0;
return &no;
} else {
@@ -282,3 +282,135 @@ TEST(TestEventWithTimestamp) {
CHECK_EQ(20683, GET_TRACE_OBJECT(3)->timestamp);
CHECK_EQ(32832, GET_TRACE_OBJECT(4)->timestamp);
}
+
+TEST(BuiltinsIsTraceCategoryEnabled) {
+ CcTest::InitializeVM();
+ MockTracingPlatform platform;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handle_scope(isolate);
+ LocalContext env;
+
+ v8::Local<v8::Object> binding = env->GetExtrasBindingObject();
+ CHECK(!binding.IsEmpty());
+
+ auto undefined = v8::Undefined(isolate);
+ auto isTraceCategoryEnabled =
+ binding->Get(env.local(), v8_str("isTraceCategoryEnabled"))
+ .ToLocalChecked()
+ .As<v8::Function>();
+
+ {
+ // Test with an enabled category
+ v8::Local<v8::Value> argv[] = {v8_str("v8-cat")};
+ auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(result->BooleanValue());
+ }
+
+ {
+ // Test with a disabled category
+ v8::Local<v8::Value> argv[] = {v8_str("cat")};
+ auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(!result->BooleanValue());
+ }
+
+ {
+ // Test with an enabled utf8 category
+ v8::Local<v8::Value> argv[] = {v8_str("v8-cat\u20ac")};
+ auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(result->BooleanValue());
+ }
+}
+
+TEST(BuiltinsTrace) {
+ CcTest::InitializeVM();
+ MockTracingPlatform platform;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handle_scope(isolate);
+ LocalContext env;
+
+ v8::Local<v8::Object> binding = env->GetExtrasBindingObject();
+ CHECK(!binding.IsEmpty());
+
+ auto undefined = v8::Undefined(isolate);
+ auto trace = binding->Get(env.local(), v8_str("trace"))
+ .ToLocalChecked()
+ .As<v8::Function>();
+
+ // Test with disabled category
+ {
+ v8::Local<v8::String> category = v8_str("cat");
+ v8::Local<v8::String> name = v8_str("name");
+ v8::Local<v8::Value> argv[] = {
+ v8::Integer::New(isolate, 'b'), // phase
+ category, name, v8::Integer::New(isolate, 0), // id
+ undefined // data
+ };
+ auto result = trace->Call(env.local(), undefined, 5, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(!result->BooleanValue());
+ CHECK_EQ(0, GET_TRACE_OBJECTS_LIST->size());
+ }
+
+ // Test with enabled category
+ {
+ v8::Local<v8::String> category = v8_str("v8-cat");
+ v8::Local<v8::String> name = v8_str("name");
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> data = v8::Object::New(isolate);
+ data->Set(context, v8_str("foo"), v8_str("bar")).FromJust();
+ v8::Local<v8::Value> argv[] = {
+ v8::Integer::New(isolate, 'b'), // phase
+ category, name, v8::Integer::New(isolate, 123), // id
+ data // data arg
+ };
+ auto result = trace->Call(env.local(), undefined, 5, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(result->BooleanValue());
+ CHECK_EQ(1, GET_TRACE_OBJECTS_LIST->size());
+
+ CHECK_EQ(123, GET_TRACE_OBJECT(0)->id);
+ CHECK_EQ('b', GET_TRACE_OBJECT(0)->phase);
+ CHECK_EQ("name", GET_TRACE_OBJECT(0)->name);
+ CHECK_EQ(1, GET_TRACE_OBJECT(0)->num_args);
+ }
+
+ // Test with enabled utf8 category
+ {
+ v8::Local<v8::String> category = v8_str("v8-cat\u20ac");
+ v8::Local<v8::String> name = v8_str("name\u20ac");
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> data = v8::Object::New(isolate);
+ data->Set(context, v8_str("foo"), v8_str("bar")).FromJust();
+ v8::Local<v8::Value> argv[] = {
+ v8::Integer::New(isolate, 'b'), // phase
+ category, name, v8::Integer::New(isolate, 123), // id
+ data // data arg
+ };
+ auto result = trace->Call(env.local(), undefined, 5, argv)
+ .ToLocalChecked()
+ .As<v8::Boolean>();
+
+ CHECK(result->BooleanValue());
+ CHECK_EQ(2, GET_TRACE_OBJECTS_LIST->size());
+
+ CHECK_EQ(123, GET_TRACE_OBJECT(1)->id);
+ CHECK_EQ('b', GET_TRACE_OBJECT(1)->phase);
+ CHECK_EQ("name\u20ac", GET_TRACE_OBJECT(1)->name);
+ CHECK_EQ(1, GET_TRACE_OBJECT(1)->num_args);
+ }
+}
--
2.17.0
From fcea0536bae5474562a47ca7dec7b3697d737e19 Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Tue, 17 Jul 2018 13:55:20 -0300
Subject: [PATCH 11/23] deps: cherry-pick 804a693 from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Original commit message:
[postmortem] add JS_ERROR_TYPE and context embedder index
* JS_ERROR_TYPE is required for postmortem tools to inspect
JSError objects (see https://github.com/nodejs/llnode/pull/215
for a usage example)
* The context embedder index is required for postmortem tools to
access embedder data stored in the context (see
https://github.com/nodejs/llnode/pull/204 for a usage example)
R=bmeurer@google.com, yangguo@google.com
Change-Id: Ib7c7eb44f6ad327fc71a1d45f510c49377db7a25
Reviewed-on: https://chromium-review.googlesource.com/1138493
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54475}
Refs: https://github.com/v8/v8/commit/804a693eb4ac2fed160c683d16444a53b5
Backport-PR-URL: https://github.com/nodejs/node/pull/21668
PR-URL: https://github.com/nodejs/node/pull/21855
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
---
tools/gen-postmortem-metadata.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py
index 97f2ac9c63..88407bb96f 100644
--- a/tools/gen-postmortem-metadata.py
+++ b/tools/gen-postmortem-metadata.py
@@ -211,6 +211,9 @@ consts_misc = [
'value': 'Context::EXTENSION_INDEX' },
{ 'name': 'context_min_slots',
'value': 'Context::MIN_CONTEXT_SLOTS' },
+ { 'name': 'context_idx_embedder_data',
+ 'value': 'Internals::kContextEmbedderDataIndex' },
+
{ 'name': 'namedictionaryshape_prefix_size',
'value': 'NameDictionaryShape::kPrefixSize' },
@@ -231,6 +234,8 @@ consts_misc = [
'value': 'SimpleNumberDictionaryShape::kPrefixSize' },
{ 'name': 'simplenumberdictionaryshape_entry_size',
'value': 'SimpleNumberDictionaryShape::kEntrySize' },
+
+ { 'name': 'type_JSError__JS_ERROR_TYPE', 'value': 'JS_ERROR_TYPE' },
];
#
--
2.17.0
From 8d0176e1fd08ecbf1493ff59eb70b3e6d1139641 Mon Sep 17 00:00:00 2001
From: Peter Marshall <p.s.marshall0@gmail.com>
Date: Fri, 3 Aug 2018 12:35:10 +0200
Subject: [PATCH 12/23] deps: remove thread_local to fix V8 compilation
PR-URL: https://github.com/nodejs/node/pull/21668
Fixes: https://github.com/I
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
---
src/torque/contextual.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/torque/contextual.h b/src/torque/contextual.h
index 33f0481f9a..738cdf38b1 100644
--- a/src/torque/contextual.h
+++ b/src/torque/contextual.h
@@ -50,11 +50,11 @@ class ContextualVariable {
}
private:
- static thread_local VarType* top_;
+ static VarType* top_;
};
template <class Derived, class VarType>
-thread_local VarType* ContextualVariable<Derived, VarType>::top_ = nullptr;
+VarType* ContextualVariable<Derived, VarType>::top_ = nullptr;
// Usage: DECLARE_CONTEXTUAL_VARIABLE(VarName, VarType)
#define DECLARE_CONTEXTUAL_VARIABLE(VarName, ...) \
--
2.17.0
From 49ec847cc95c19a7dcdc6a91bd45002ff21b30f8 Mon Sep 17 00:00:00 2001
From: Matheus Marchini <matheus@sthima.com>
Date: Wed, 1 Aug 2018 15:09:51 -0300
Subject: [PATCH 13/23] deps: cherry-pick 09bca09 from upstream V8
Original commit message:
[postmortem] add ScopeInfo and Context types
The metadata introduced in this patch will be useful for postmortem
tools to inspect Contexts and ScopeInfos (see
https://github.com/nodejs/llnode/issues/211).
R=bmeurer@google.com, yangguo@google.com
Change-Id: I927fcab4014d128bd782046c1ecb9ee045723e95
Reviewed-on: https://chromium-review.googlesource.com/1153858
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54768}
Refs: https://github.com/v8/v8/commit/09bca095e38d6e4770ae48e174f59d33c
PR-URL: https://github.com/nodejs/node/pull/22068
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
---
BUILD.gn | 1 +
tools/gen-postmortem-metadata.py | 5 ++++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/BUILD.gn b/BUILD.gn
index f00a8280c1..fbb34a0b3d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -828,6 +828,7 @@ action("postmortem-metadata") {
"src/objects/js-regexp-string-iterator.h",
"src/objects/map.h",
"src/objects/map-inl.h",
+ "src/objects/scope-info.h",
"src/objects/script.h",
"src/objects/script-inl.h",
"src/objects/shared-function-info.h",
diff --git a/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py
index 88407bb96f..9a78819943 100644
--- a/tools/gen-postmortem-metadata.py
+++ b/tools/gen-postmortem-metadata.py
@@ -58,6 +58,9 @@ consts_misc = [
{ 'name': 'APIObjectType', 'value': 'JS_API_OBJECT_TYPE' },
{ 'name': 'SpecialAPIObjectType', 'value': 'JS_SPECIAL_API_OBJECT_TYPE' },
+ { 'name': 'FirstContextType', 'value': 'FIRST_CONTEXT_TYPE' },
+ { 'name': 'LastContextType', 'value': 'LAST_CONTEXT_TYPE' },
+
{ 'name': 'IsNotStringMask', 'value': 'kIsNotStringMask' },
{ 'name': 'StringTag', 'value': 'kStringTag' },
@@ -289,7 +292,7 @@ extras_accessors = [
expected_classes = [
'ConsString', 'FixedArray', 'HeapNumber', 'JSArray', 'JSFunction',
'JSObject', 'JSRegExp', 'JSValue', 'Map', 'Oddball', 'Script',
- 'SeqOneByteString', 'SharedFunctionInfo'
+ 'SeqOneByteString', 'SharedFunctionInfo', 'ScopeInfo'
];
--
2.17.0
From bf930e5e6f50ea1ebf58526e9fa9ee46ce7249bf Mon Sep 17 00:00:00 2001
From: Ruben Bridgewater <ruben@bridgewater.de>
Date: Wed, 8 Aug 2018 03:43:20 +0200
Subject: [PATCH 14/23] deps: backport c608122b from upstream
Original commit message:
[api][keys] Allow skipping indices for Proxies with GetPropertyNames
Bug: v8:7942
Change-Id: I7b3740b04cbcaa56dc809150900ab8d821b054ce
Reviewed-on: https://chromium-review.googlesource.com/1156544
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54821}
PR-URL: https://github.com/nodejs/node/pull/22210
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
---
src/keys.cc | 46 ++++++++++-------
src/keys.h | 14 ++++--
src/runtime/runtime-forin.cc | 3 +-
test/cctest/test-api.cc | 97 +++++++++++++++++++++++++++++++++++-
4 files changed, 135 insertions(+), 25 deletions(-)
diff --git a/src/keys.cc b/src/keys.cc
index 98226ae8b7..0213ae0619 100644
--- a/src/keys.cc
+++ b/src/keys.cc
@@ -37,10 +37,10 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
// static
MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
- GetKeysConversion keys_conversion, bool is_for_in) {
+ GetKeysConversion keys_conversion, bool is_for_in, bool skip_indices) {
Isolate* isolate = object->GetIsolate();
- FastKeyAccumulator accumulator(isolate, object, mode, filter);
- accumulator.set_is_for_in(is_for_in);
+ FastKeyAccumulator accumulator(isolate, object, mode, filter, is_for_in,
+ skip_indices);
return accumulator.GetKeys(keys_conversion);
}
@@ -356,7 +356,8 @@ Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
template <bool fast_properties>
MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
Handle<JSObject> object,
- GetKeysConversion convert) {
+ GetKeysConversion convert,
+ bool skip_indices) {
Handle<FixedArray> keys;
ElementsAccessor* accessor = object->GetElementsAccessor();
if (fast_properties) {
@@ -365,8 +366,13 @@ MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
// TODO(cbruni): preallocate big enough array to also hold elements.
keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate, object);
}
- MaybeHandle<FixedArray> result =
- accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
+ MaybeHandle<FixedArray> result;
+ if (skip_indices) {
+ result = keys;
+ } else {
+ result =
+ accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
+ }
if (FLAG_trace_for_in_enumerate) {
PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n",
@@ -404,7 +410,8 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
// Do not try to use the enum-cache for dict-mode objects.
if (map->is_dictionary_map()) {
- return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion);
+ return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion,
+ skip_indices_);
}
int enum_length = receiver_->map()->EnumLength();
if (enum_length == kInvalidEnumCacheSentinel) {
@@ -422,7 +429,8 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
}
// The properties-only case failed because there were probably elements on the
// receiver.
- return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion);
+ return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion,
+ skip_indices_);
}
MaybeHandle<FixedArray>
@@ -451,6 +459,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
GetKeysConversion keys_conversion) {
KeyAccumulator accumulator(isolate_, mode_, filter_);
accumulator.set_is_for_in(is_for_in_);
+ accumulator.set_skip_indices(skip_indices_);
accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
@@ -698,13 +707,15 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
Handle<JSObject> object) {
- MAYBE_RETURN((CollectInterceptorKeysInternal(
- receiver, object,
- handle(InterceptorInfo::cast(
- access_check_info->indexed_interceptor()),
- isolate_),
- this, kIndexed)),
- Nothing<bool>());
+ if (!skip_indices_) {
+ MAYBE_RETURN((CollectInterceptorKeysInternal(
+ receiver, object,
+ handle(InterceptorInfo::cast(
+ access_check_info->indexed_interceptor()),
+ isolate_),
+ this, kIndexed)),
+ Nothing<bool>());
+ }
MAYBE_RETURN(
(CollectInterceptorKeysInternal(
receiver, object,
@@ -935,8 +946,9 @@ Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
Handle<FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, keys,
- KeyAccumulator::GetKeys(target, KeyCollectionMode::kOwnOnly, filter_,
- GetKeysConversion::kConvertToString, is_for_in_),
+ KeyAccumulator::GetKeys(
+ target, KeyCollectionMode::kOwnOnly, filter_,
+ GetKeysConversion::kConvertToString, is_for_in_, skip_indices_),
Nothing<bool>());
Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
return result;
diff --git a/src/keys.h b/src/keys.h
index 649d6a9599..5abbaac5cd 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -40,7 +40,7 @@ class KeyAccumulator final BASE_EMBEDDED {
static MaybeHandle<FixedArray> GetKeys(
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
GetKeysConversion keys_conversion = GetKeysConversion::kKeepNumbers,
- bool is_for_in = false);
+ bool is_for_in = false, bool skip_indices = false);
Handle<FixedArray> GetKeys(
GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
@@ -128,14 +128,19 @@ class KeyAccumulator final BASE_EMBEDDED {
class FastKeyAccumulator {
public:
FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver,
- KeyCollectionMode mode, PropertyFilter filter)
- : isolate_(isolate), receiver_(receiver), mode_(mode), filter_(filter) {
+ KeyCollectionMode mode, PropertyFilter filter,
+ bool is_for_in = false, bool skip_indices = false)
+ : isolate_(isolate),
+ receiver_(receiver),
+ mode_(mode),
+ filter_(filter),
+ is_for_in_(is_for_in),
+ skip_indices_(skip_indices) {
Prepare();
}
bool is_receiver_simple_enum() { return is_receiver_simple_enum_; }
bool has_empty_prototype() { return has_empty_prototype_; }
- void set_is_for_in(bool value) { is_for_in_ = value; }
MaybeHandle<FixedArray> GetKeys(
GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
@@ -153,6 +158,7 @@ class FastKeyAccumulator {
KeyCollectionMode mode_;
PropertyFilter filter_;
bool is_for_in_ = false;
+ bool skip_indices_ = false;
bool is_receiver_simple_enum_ = false;
bool has_empty_prototype_ = false;
diff --git a/src/runtime/runtime-forin.cc b/src/runtime/runtime-forin.cc
index 9193daf9db..58e47f621e 100644
--- a/src/runtime/runtime-forin.cc
+++ b/src/runtime/runtime-forin.cc
@@ -25,8 +25,7 @@ MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
JSObject::MakePrototypesFast(receiver, kStartAtReceiver, isolate);
FastKeyAccumulator accumulator(isolate, receiver,
KeyCollectionMode::kIncludePrototypes,
- ENUMERABLE_STRINGS);
- accumulator.set_is_for_in(true);
+ ENUMERABLE_STRINGS, true);
// Test if we have an enum cache for {receiver}.
if (!accumulator.is_receiver_simple_enum()) {
Handle<FixedArray> keys;
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 366b940d61..5bea5b14d0 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -15356,14 +15356,107 @@ THREADED_TEST(PropertyEnumeration2) {
}
}
-THREADED_TEST(PropertyNames) {
+THREADED_TEST(GetPropertyNames) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"var result = {0: 0, 1: 1, a: 2, b: 3};"
"result[Symbol('symbol')] = true;"
- "result.__proto__ = {2: 4, 3: 5, c: 6, d: 7};"
+ "result.__proto__ = {__proto__:null, 2: 4, 3: 5, c: 6, d: 7};"
+ "result;");
+ v8::Local<v8::Object> object = result.As<v8::Object>();
+ v8::PropertyFilter default_filter =
+ static_cast<v8::PropertyFilter>(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS);
+ v8::PropertyFilter include_symbols_filter = v8::ONLY_ENUMERABLE;
+
+ v8::Local<v8::Array> properties =
+ object->GetPropertyNames(context.local()).ToLocalChecked();
+ const char* expected_properties1[] = {"0", "1", "a", "b", "2", "3", "c", "d"};
+ CheckStringArray(isolate, properties, 8, expected_properties1);
+
+ properties =
+ object
+ ->GetPropertyNames(context.local(),
+ v8::KeyCollectionMode::kIncludePrototypes,
+ default_filter, v8::IndexFilter::kIncludeIndices)
+ .ToLocalChecked();
+ CheckStringArray(isolate, properties, 8, expected_properties1);
+
+ properties = object
+ ->GetPropertyNames(context.local(),
+ v8::KeyCollectionMode::kIncludePrototypes,
+ include_symbols_filter,
+ v8::IndexFilter::kIncludeIndices)
+ .ToLocalChecked();
+ const char* expected_properties1_1[] = {"0", "1", "a", "b", nullptr,
+ "2", "3", "c", "d"};
+ CheckStringArray(isolate, properties, 9, expected_properties1_1);
+ CheckIsSymbolAt(isolate, properties, 4, "symbol");
+
+ properties =
+ object
+ ->GetPropertyNames(context.local(),
+ v8::KeyCollectionMode::kIncludePrototypes,
+ default_filter, v8::IndexFilter::kSkipIndices)
+ .ToLocalChecked();
+ const char* expected_properties2[] = {"a", "b", "c", "d"};
+ CheckStringArray(isolate, properties, 4, expected_properties2);
+
+ properties = object
+ ->GetPropertyNames(context.local(),
+ v8::KeyCollectionMode::kIncludePrototypes,
+ include_symbols_filter,
+ v8::IndexFilter::kSkipIndices)
+ .ToLocalChecked();
+ const char* expected_properties2_1[] = {"a", "b", nullptr, "c", "d"};
+ CheckStringArray(isolate, properties, 5, expected_properties2_1);
+ CheckIsSymbolAt(isolate, properties, 2, "symbol");
+
+ properties =
+ object
+ ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly,
+ default_filter, v8::IndexFilter::kIncludeIndices)
+ .ToLocalChecked();
+ const char* expected_properties3[] = {"0", "1", "a", "b"};
+ CheckStringArray(isolate, properties, 4, expected_properties3);
+
+ properties = object
+ ->GetPropertyNames(
+ context.local(), v8::KeyCollectionMode::kOwnOnly,
+ include_symbols_filter, v8::IndexFilter::kIncludeIndices)
+ .ToLocalChecked();
+ const char* expected_properties3_1[] = {"0", "1", "a", "b", nullptr};
+ CheckStringArray(isolate, properties, 5, expected_properties3_1);
+ CheckIsSymbolAt(isolate, properties, 4, "symbol");
+
+ properties =
+ object
+ ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly,
+ default_filter, v8::IndexFilter::kSkipIndices)
+ .ToLocalChecked();
+ const char* expected_properties4[] = {"a", "b"};
+ CheckStringArray(isolate, properties, 2, expected_properties4);
+
+ properties = object
+ ->GetPropertyNames(
+ context.local(), v8::KeyCollectionMode::kOwnOnly,
+ include_symbols_filter, v8::IndexFilter::kSkipIndices)
+ .ToLocalChecked();
+ const char* expected_properties4_1[] = {"a", "b", nullptr};
+ CheckStringArray(isolate, properties, 3, expected_properties4_1);
+ CheckIsSymbolAt(isolate, properties, 2, "symbol");
+}
+
+THREADED_TEST(ProxyGetPropertyNames) {
+ LocalContext context;
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Value> result = CompileRun(
+ "var target = {0: 0, 1: 1, a: 2, b: 3};"
+ "target[Symbol('symbol')] = true;"
+ "target.__proto__ = {__proto__:null, 2: 4, 3: 5, c: 6, d: 7};"
+ "var result = new Proxy(target, {});"
"result;");
v8::Local<v8::Object> object = result.As<v8::Object>();
v8::PropertyFilter default_filter =
--
2.17.0
From c1863a1f3975f8848171ebdc33d3a819c92eb3de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Fri, 24 Aug 2018 11:49:42 +0200
Subject: [PATCH 15/23] src,deps: add isolate parameter to String::Concat
Partially backport an upstream commit that deprecates String::Concat
without the isolate parameter. This overload has already been removed
in V8 7.0.
PR-URL: https://github.com/nodejs/node/pull/22521
Refs: https://github.com/v8/v8/commit/8a011b57d8b26e9cfe1c20a2ef26adb14be6ecc2
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
---
include/v8.h | 6 +++++-
src/api.cc | 11 ++++++++---
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/include/v8.h b/include/v8.h
index 2bacd1a480..7f0cb6d53c 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -2901,7 +2901,11 @@ class V8_EXPORT String : public Name {
* Creates a new string by concatenating the left and the right strings
* passed in as parameters.
*/
- static Local<String> Concat(Local<String> left, Local<String> right);
+ static Local<String> Concat(Isolate* isolate, Local<String> left,
+ Local<String> right);
+ static V8_DEPRECATE_SOON("Use Isolate* version",
+ Local<String> Concat(Local<String> left,
+ Local<String> right));
/**
* Creates a new external string using the data defined in the given
diff --git a/src/api.cc b/src/api.cc
index 87b6b24270..5f3f715999 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -6650,10 +6650,10 @@ MaybeLocal<String> String::NewFromTwoByte(Isolate* isolate,
return result;
}
-
-Local<String> v8::String::Concat(Local<String> left, Local<String> right) {
+Local<String> v8::String::Concat(Isolate* v8_isolate, Local<String> left,
+ Local<String> right) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::Handle<i::String> left_string = Utils::OpenHandle(*left);
- i::Isolate* isolate = left_string->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
LOG_API(isolate, String, Concat);
i::Handle<i::String> right_string = Utils::OpenHandle(*right);
@@ -6667,6 +6667,11 @@ Local<String> v8::String::Concat(Local<String> left, Local<String> right) {
return Utils::ToLocal(result);
}
+Local<String> v8::String::Concat(Local<String> left, Local<String> right) {
+ i::Handle<i::String> left_string = Utils::OpenHandle(*left);
+ i::Isolate* isolate = left_string->GetIsolate();
+ return Concat(reinterpret_cast<Isolate*>(isolate), left, right);
+}
MaybeLocal<String> v8::String::NewExternalTwoByte(
Isolate* isolate, v8::String::ExternalStringResource* resource) {
--
2.17.0
From d159935b6046811e539f51f23495e31ddb01efe3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Sun, 26 Aug 2018 09:29:37 +0200
Subject: [PATCH 16/23] deps: backport StackFrame::GetFrame with isolate
This overload was added in V8 6.9 and the one without isolate was
removed in V8 7.0.
Refs: https://github.com/v8/v8/commit/8a011b57d8b26e9cfe1c20a2ef26adb14be6ecc2
PR-URL: https://github.com/nodejs/node/pull/22531
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
include/v8.h | 4 +++-
src/api.cc | 11 ++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/include/v8.h b/include/v8.h
index 7f0cb6d53c..71909f4cb2 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1828,7 +1828,9 @@ class V8_EXPORT StackTrace {
/**
* Returns a StackFrame at a particular index.
*/
- Local<StackFrame> GetFrame(uint32_t index) const;
+ V8_DEPRECATE_SOON("Use Isolate version",
+ Local<StackFrame> GetFrame(uint32_t index) const);
+ Local<StackFrame> GetFrame(Isolate* isolate, uint32_t index) const;
/**
* Returns the number of StackFrames.
diff --git a/src/api.cc b/src/api.cc
index 5f3f715999..01e4c84895 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3027,15 +3027,20 @@ void Message::PrintCurrentStackTrace(Isolate* isolate, FILE* out) {
// --- S t a c k T r a c e ---
-Local<StackFrame> StackTrace::GetFrame(uint32_t index) const {
- i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+Local<StackFrame> StackTrace::GetFrame(Isolate* v8_isolate,
+ uint32_t index) const {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
- EscapableHandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ EscapableHandleScope scope(v8_isolate);
auto obj = handle(Utils::OpenHandle(this)->get(index), isolate);
auto info = i::Handle<i::StackFrameInfo>::cast(obj);
return scope.Escape(Utils::StackFrameToLocal(info));
}
+Local<StackFrame> StackTrace::GetFrame(uint32_t index) const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ return GetFrame(reinterpret_cast<Isolate*>(isolate), index);
+}
int StackTrace::GetFrameCount() const {
return Utils::OpenHandle(this)->length();
--
2.17.0
From 91ad7bde3c6d0b9291d6ca0f371f24ac3ca1ee80 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Sun, 26 Aug 2018 09:41:26 +0200
Subject: [PATCH 17/23] deps: backport String::Write{OneByte,Utf8} with isolate
These overloads were added in V8 6.9 and the ones without the isolate
parameter were removed in V8 7.0.
Refs: https://github.com/v8/v8/commit/8a011b57d8b26e9cfe1c20a2ef26adb14be6ecc2
PR-URL: https://github.com/nodejs/node/pull/22531
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
include/v8.h | 27 ++++++++++++++++-----------
src/api.cc | 42 +++++++++++++++++++++++++++++-------------
2 files changed, 45 insertions(+), 24 deletions(-)
diff --git a/include/v8.h b/include/v8.h
index 71909f4cb2..2402835106 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -2728,20 +2728,25 @@ class V8_EXPORT String : public Name {
};
// 16-bit character codes.
- int Write(uint16_t* buffer,
- int start = 0,
- int length = -1,
+ int Write(Isolate* isolate, uint16_t* buffer, int start = 0, int length = -1,
int options = NO_OPTIONS) const;
+ V8_DEPRECATE_SOON("Use Isolate* version",
+ int Write(uint16_t* buffer, int start = 0, int length = -1,
+ int options = NO_OPTIONS) const);
// One byte characters.
- int WriteOneByte(uint8_t* buffer,
- int start = 0,
- int length = -1,
- int options = NO_OPTIONS) const;
+ int WriteOneByte(Isolate* isolate, uint8_t* buffer, int start = 0,
+ int length = -1, int options = NO_OPTIONS) const;
+ V8_DEPRECATE_SOON("Use Isolate* version",
+ int WriteOneByte(uint8_t* buffer, int start = 0,
+ int length = -1, int options = NO_OPTIONS)
+ const);
// UTF-8 encoded characters.
- int WriteUtf8(char* buffer,
- int length = -1,
- int* nchars_ref = NULL,
- int options = NO_OPTIONS) const;
+ int WriteUtf8(Isolate* isolate, char* buffer, int length = -1,
+ int* nchars_ref = NULL, int options = NO_OPTIONS) const;
+ V8_DEPRECATE_SOON("Use Isolate* version",
+ int WriteUtf8(char* buffer, int length = -1,
+ int* nchars_ref = NULL,
+ int options = NO_OPTIONS) const);
/**
* A zero length string.
diff --git a/src/api.cc b/src/api.cc
index 01e4c84895..026f38d242 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -5714,12 +5714,10 @@ static bool RecursivelySerializeToUtf8(i::String* current,
}
-int String::WriteUtf8(char* buffer,
- int capacity,
- int* nchars_ref,
- int options) const {
+int String::WriteUtf8(Isolate* v8_isolate, char* buffer, int capacity,
+ int* nchars_ref, int options) const {
i::Handle<i::String> str = Utils::OpenHandle(this);
- i::Isolate* isolate = str->GetIsolate();
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
LOG_API(isolate, String, WriteUtf8);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
str = i::String::Flatten(str); // Flatten the string for efficiency.
@@ -5760,14 +5758,18 @@ int String::WriteUtf8(char* buffer,
return writer.CompleteWrite(write_null, nchars_ref);
}
+int String::WriteUtf8(char* buffer, int capacity, int* nchars_ref,
+ int options) const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ i::Isolate* isolate = str->GetIsolate();
+ return WriteUtf8(reinterpret_cast<Isolate*>(isolate), buffer, capacity,
+ nchars_ref, options);
+}
-template<typename CharType>
-static inline int WriteHelper(const String* string,
- CharType* buffer,
- int start,
- int length,
+template <typename CharType>
+static inline int WriteHelper(i::Isolate* isolate, const String* string,
+ CharType* buffer, int start, int length,
int options) {
- i::Isolate* isolate = Utils::OpenHandle(string)->GetIsolate();
LOG_API(isolate, String, Write);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
DCHECK(start >= 0 && length >= -1);
@@ -5790,7 +5792,14 @@ int String::WriteOneByte(uint8_t* buffer,
int start,
int length,
int options) const {
- return WriteHelper(this, buffer, start, length, options);
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ return WriteHelper(isolate, this, buffer, start, length, options);
+}
+
+int String::WriteOneByte(Isolate* isolate, uint8_t* buffer, int start,
+ int length, int options) const {
+ return WriteHelper(reinterpret_cast<i::Isolate*>(isolate), this, buffer,
+ start, length, options);
}
@@ -5798,7 +5807,14 @@ int String::Write(uint16_t* buffer,
int start,
int length,
int options) const {
- return WriteHelper(this, buffer, start, length, options);
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ return WriteHelper(isolate, this, buffer, start, length, options);
+}
+
+int String::Write(Isolate* isolate, uint16_t* buffer, int start, int length,
+ int options) const {
+ return WriteHelper(reinterpret_cast<i::Isolate*>(isolate), this, buffer,
+ start, length, options);
}
--
2.17.0
From a70d7606f8b563842bf9ae8297e0d9cbd63a880f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Sun, 26 Aug 2018 10:12:15 +0200
Subject: [PATCH 18/23] deps: backport String::Utf8Length with isolate
This overload was added in V8 6.9 and the one without the isolate
parameter was removed in V8 7.0.
Refs: https://github.com/v8/v8/commit/3dd5c6fe38355b8323597341409b37f931de5a85
PR-URL: https://github.com/nodejs/node/pull/22531
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
include/v8.h | 4 +++-
src/api.cc | 3 +++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/v8.h b/include/v8.h
index 2402835106..184e898d75 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -2674,7 +2674,9 @@ class V8_EXPORT String : public Name {
* Returns the number of bytes in the UTF-8 encoded
* representation of this string.
*/
- int Utf8Length() const;
+ V8_DEPRECATE_SOON("Use Isolate version instead", int Utf8Length() const);
+
+ int Utf8Length(Isolate* isolate) const;
/**
* Returns whether this string is known to contain only one byte data,
diff --git a/src/api.cc b/src/api.cc
index 026f38d242..691db3e470 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -5488,6 +5488,9 @@ bool String::ContainsOnlyOneByte() const {
return helper.Check(*str);
}
+int String::Utf8Length(Isolate* isolate) const {
+ return Utf8Length();
+}
int String::Utf8Length() const {
i::Handle<i::String> str = Utils::OpenHandle(this);
--
2.17.0
From a7ce9ed686a729a4b4d90404b89b01efbfa5a0f5 Mon Sep 17 00:00:00 2001
From: Peter Marshall <p.s.marshall0@gmail.com>
Date: Mon, 30 Jul 2018 13:54:55 +0200
Subject: [PATCH 19/23] deps: backport 4 CPU profiler commits from upstream V8
[cpu-profiler] Add a new profiling mode with a more detailed call tree.
https://chromium.googlesource.com/v8/v8.git/+/ecae80cdb350dde1e654c531b56f5b6c44dc8c77
[cpu-profiler] Reuse free slots in code_entries_
https://chromium.googlesource.com/v8/v8.git/+/3e1126bf15e62c433c4e9cb21316d182f691c63a
[cpu-profiler] Only store deopt inline frames for functions that need it
https://chromium.googlesource.com/v8/v8.git/+/0bfcbdd4726920755e51dab28c18ab93e050819b
[cpu-profiler] Use instruction start as the key for the CodeMap
https://chromium.googlesource.com/v8/v8.git/+/ba752ea4c50713dff1e94f45a79db3ba968a8d66
PR-URL: https://github.com/nodejs/node/pull/22028
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
---
include/v8-profiler.h | 17 +++
src/api.cc | 9 +-
src/code-events.h | 4 +-
src/heap/mark-compact.cc | 2 +-
src/log.cc | 25 ++-
src/log.h | 4 +-
src/perf-jit.cc | 2 +-
src/perf-jit.h | 4 +-
src/profiler/cpu-profiler-inl.h | 16 +-
src/profiler/cpu-profiler.cc | 14 +-
src/profiler/cpu-profiler.h | 23 +--
src/profiler/profile-generator-inl.h | 3 +-
src/profiler/profile-generator.cc | 210 +++++++++++++++++---------
src/profiler/profile-generator.h | 97 ++++++++----
src/profiler/profiler-listener.cc | 88 ++++++-----
src/profiler/profiler-listener.h | 5 +-
src/snapshot/serializer.h | 4 +-
test/cctest/cctest.status | 1 +
test/cctest/test-cpu-profiler.cc | 98 ++++++++++--
test/cctest/test-profile-generator.cc | 45 +++++-
20 files changed, 468 insertions(+), 203 deletions(-)
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 0e09d8732f..7e73eb6ea4 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -277,6 +277,16 @@ class V8_EXPORT CpuProfile {
void Delete();
};
+enum CpuProfilingMode {
+ // In the resulting CpuProfile tree, intermediate nodes in a stack trace
+ // (from the root to a leaf) will have line numbers that point to the start
+ // line of the function, rather than the line of the callsite of the child.
+ kLeafNodeLineNumbers,
+ // In the resulting CpuProfile tree, nodes are separated based on the line
+ // number of their callsite in their parent.
+ kCallerLineNumbers,
+};
+
/**
* Interface for controlling CPU profiling. Instance of the
* profiler can be created using v8::CpuProfiler::New method.
@@ -320,6 +330,13 @@ class V8_EXPORT CpuProfiler {
* |record_samples| parameter controls whether individual samples should
* be recorded in addition to the aggregated tree.
*/
+ void StartProfiling(Local<String> title, CpuProfilingMode mode,
+ bool record_samples = false);
+ /**
+ * The same as StartProfiling above, but the CpuProfilingMode defaults to
+ * kLeafNodeLineNumbers mode, which was the previous default behavior of the
+ * profiler.
+ */
void StartProfiling(Local<String> title, bool record_samples = false);
/**
diff --git a/src/api.cc b/src/api.cc
index 691db3e470..66eecfa787 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -10021,7 +10021,7 @@ const char* CpuProfileNode::GetScriptResourceNameStr() const {
}
int CpuProfileNode::GetLineNumber() const {
- return reinterpret_cast<const i::ProfileNode*>(this)->entry()->line_number();
+ return reinterpret_cast<const i::ProfileNode*>(this)->line_number();
}
@@ -10159,9 +10159,14 @@ void CpuProfiler::CollectSample() {
void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) {
reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
- *Utils::OpenHandle(*title), record_samples);
+ *Utils::OpenHandle(*title), record_samples, kLeafNodeLineNumbers);
}
+void CpuProfiler::StartProfiling(Local<String> title, CpuProfilingMode mode,
+ bool record_samples) {
+ reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
+ *Utils::OpenHandle(*title), record_samples, mode);
+}
CpuProfile* CpuProfiler::StopProfiling(Local<String> title) {
return reinterpret_cast<CpuProfile*>(
diff --git a/src/code-events.h b/src/code-events.h
index caed5160f4..5194628912 100644
--- a/src/code-events.h
+++ b/src/code-events.h
@@ -83,7 +83,7 @@ class CodeEventListener {
virtual void GetterCallbackEvent(Name* name, Address entry_point) = 0;
virtual void SetterCallbackEvent(Name* name, Address entry_point) = 0;
virtual void RegExpCodeCreateEvent(AbstractCode* code, String* source) = 0;
- virtual void CodeMoveEvent(AbstractCode* from, Address to) = 0;
+ virtual void CodeMoveEvent(AbstractCode* from, AbstractCode* to) = 0;
virtual void SharedFunctionInfoMoveEvent(Address from, Address to) = 0;
virtual void CodeMovingGCEvent() = 0;
virtual void CodeDisableOptEvent(AbstractCode* code,
@@ -155,7 +155,7 @@ class CodeEventDispatcher {
void RegExpCodeCreateEvent(AbstractCode* code, String* source) {
CODE_EVENT_DISPATCH(RegExpCodeCreateEvent(code, source));
}
- void CodeMoveEvent(AbstractCode* from, Address to) {
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
CODE_EVENT_DISPATCH(CodeMoveEvent(from, to));
}
void SharedFunctionInfoMoveEvent(Address from, Address to) {
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index 66575f8250..e69551f70e 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -1136,7 +1136,7 @@ class ProfilingMigrationObserver final : public MigrationObserver {
int size) final {
if (dest == CODE_SPACE || (dest == OLD_SPACE && dst->IsBytecodeArray())) {
PROFILE(heap_->isolate(),
- CodeMoveEvent(AbstractCode::cast(src), dst->address()));
+ CodeMoveEvent(AbstractCode::cast(src), AbstractCode::cast(dst)));
}
heap_->OnMoveEvent(dst, src, size);
}
diff --git a/src/log.cc b/src/log.cc
index fb3b4761a3..da9e126879 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -270,7 +270,7 @@ class PerfBasicLogger : public CodeEventLogger {
PerfBasicLogger();
~PerfBasicLogger() override;
- void CodeMoveEvent(AbstractCode* from, Address to) override {}
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override {}
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
@@ -492,7 +492,7 @@ class LowLevelLogger : public CodeEventLogger {
explicit LowLevelLogger(const char* file_name);
~LowLevelLogger() override;
- void CodeMoveEvent(AbstractCode* from, Address to) override;
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
void SnapshotPositionEvent(HeapObject* obj, int pos);
@@ -610,11 +610,10 @@ void LowLevelLogger::LogRecordedBuffer(const wasm::WasmCode* code,
code->instructions().length());
}
-void LowLevelLogger::CodeMoveEvent(AbstractCode* from, Address to) {
+void LowLevelLogger::CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
CodeMoveStruct event;
event.from_address = from->InstructionStart();
- size_t header_size = from->InstructionStart() - from->address();
- event.to_address = to + header_size;
+ event.to_address = to->InstructionStart();
LogWriteStruct(event);
}
@@ -636,7 +635,7 @@ class JitLogger : public CodeEventLogger {
public:
explicit JitLogger(JitCodeEventHandler code_event_handler);
- void CodeMoveEvent(AbstractCode* from, Address to) override;
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
void AddCodeLinePosInfoEvent(void* jit_handler_data, int pc_offset,
@@ -694,7 +693,7 @@ void JitLogger::LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
code_event_handler_(&event);
}
-void JitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
+void JitLogger::CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
base::LockGuard<base::Mutex> guard(&logger_mutex_);
JitCodeEvent event;
@@ -703,12 +702,7 @@ void JitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
from->IsCode() ? JitCodeEvent::JIT_CODE : JitCodeEvent::BYTE_CODE;
event.code_start = reinterpret_cast<void*>(from->InstructionStart());
event.code_len = from->InstructionSize();
-
- // Calculate the header size.
- const size_t header_size = from->InstructionStart() - from->address();
-
- // Calculate the new start address of the instructions.
- event.new_code_start = reinterpret_cast<void*>(to + header_size);
+ event.new_code_start = reinterpret_cast<void*>(to->InstructionStart());
code_event_handler_(&event);
}
@@ -1450,9 +1444,10 @@ void Logger::RegExpCodeCreateEvent(AbstractCode* code, String* source) {
msg.WriteToLogFile();
}
-void Logger::CodeMoveEvent(AbstractCode* from, Address to) {
+void Logger::CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
if (!is_listening_to_code_events()) return;
- MoveEventInternal(CodeEventListener::CODE_MOVE_EVENT, from->address(), to);
+ MoveEventInternal(CodeEventListener::CODE_MOVE_EVENT, from->address(),
+ to->address());
}
namespace {
diff --git a/src/log.h b/src/log.h
index ad254097e6..35f6688559 100644
--- a/src/log.h
+++ b/src/log.h
@@ -209,7 +209,7 @@ class Logger : public CodeEventListener {
// Emits a code create event for a RegExp.
void RegExpCodeCreateEvent(AbstractCode* code, String* source);
// Emits a code move event.
- void CodeMoveEvent(AbstractCode* from, Address to);
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to);
// Emits a code line info record event.
void CodeLinePosInfoRecordEvent(Address code_start,
ByteArray* source_position_table);
@@ -466,7 +466,7 @@ class ExternalCodeEventListener : public CodeEventListener {
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 CodeMoveEvent(AbstractCode* from, AbstractCode* to) override {}
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
void CodeMovingGCEvent() override {}
diff --git a/src/perf-jit.cc b/src/perf-jit.cc
index b20af564bb..dfccb293d1 100644
--- a/src/perf-jit.cc
+++ b/src/perf-jit.cc
@@ -419,7 +419,7 @@ void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
}
-void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
+void PerfJitLogger::CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
// We may receive a CodeMove event if a BytecodeArray object moves. Otherwise
// code relocation is not supported.
CHECK(from->IsBytecodeArray());
diff --git a/src/perf-jit.h b/src/perf-jit.h
index ef83e9423d..bbcc79dd1c 100644
--- a/src/perf-jit.h
+++ b/src/perf-jit.h
@@ -41,7 +41,7 @@ class PerfJitLogger : public CodeEventLogger {
PerfJitLogger();
virtual ~PerfJitLogger();
- void CodeMoveEvent(AbstractCode* from, Address to) override;
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override {}
@@ -118,7 +118,7 @@ class PerfJitLogger : public CodeEventLogger {
// PerfJitLogger is only implemented on Linux
class PerfJitLogger : public CodeEventLogger {
public:
- void CodeMoveEvent(AbstractCode* from, Address to) override {
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override {
UNIMPLEMENTED();
}
diff --git a/src/profiler/cpu-profiler-inl.h b/src/profiler/cpu-profiler-inl.h
index 3bc6541048..d8603c8168 100644
--- a/src/profiler/cpu-profiler-inl.h
+++ b/src/profiler/cpu-profiler-inl.h
@@ -16,17 +16,17 @@ namespace v8 {
namespace internal {
void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
- code_map->AddCode(start, entry, size);
+ code_map->AddCode(instruction_start, entry, instruction_size);
}
void CodeMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
- code_map->MoveCode(from, to);
+ code_map->MoveCode(from_instruction_start, to_instruction_start);
}
void CodeDisableOptEventRecord::UpdateCodeMap(CodeMap* code_map) {
- CodeEntry* entry = code_map->FindEntry(start);
+ CodeEntry* entry = code_map->FindEntry(instruction_start);
if (entry != nullptr) {
entry->set_bailout_reason(bailout_reason);
}
@@ -34,13 +34,17 @@ void CodeDisableOptEventRecord::UpdateCodeMap(CodeMap* code_map) {
void CodeDeoptEventRecord::UpdateCodeMap(CodeMap* code_map) {
- CodeEntry* entry = code_map->FindEntry(start);
- if (entry != nullptr) entry->set_deopt_info(deopt_reason, deopt_id);
+ CodeEntry* entry = code_map->FindEntry(instruction_start);
+ if (entry == nullptr) return;
+ std::vector<CpuProfileDeoptFrame> frames_vector(
+ deopt_frames, deopt_frames + deopt_frame_count);
+ entry->set_deopt_info(deopt_reason, deopt_id, std::move(frames_vector));
+ delete[] deopt_frames;
}
void ReportBuiltinEventRecord::UpdateCodeMap(CodeMap* code_map) {
- CodeEntry* entry = code_map->FindEntry(start);
+ CodeEntry* entry = code_map->FindEntry(instruction_start);
if (!entry) {
// Code objects for builtins should already have been added to the map but
// some of them have been filtered out by CpuProfiler.
diff --git a/src/profiler/cpu-profiler.cc b/src/profiler/cpu-profiler.cc
index 9035be3eed..79606dc812 100644
--- a/src/profiler/cpu-profiler.cc
+++ b/src/profiler/cpu-profiler.cc
@@ -345,20 +345,20 @@ void CpuProfiler::CollectSample() {
}
}
-void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
- if (profiles_->StartProfiling(title, record_samples)) {
+void CpuProfiler::StartProfiling(const char* title, bool record_samples,
+ ProfilingMode mode) {
+ if (profiles_->StartProfiling(title, record_samples, mode)) {
TRACE_EVENT0("v8", "CpuProfiler::StartProfiling");
StartProcessorIfNotStarted();
}
}
-
-void CpuProfiler::StartProfiling(String* title, bool record_samples) {
- StartProfiling(profiles_->GetName(title), record_samples);
+void CpuProfiler::StartProfiling(String* title, bool record_samples,
+ ProfilingMode mode) {
+ StartProfiling(profiles_->GetName(title), record_samples, mode);
isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
}
-
void CpuProfiler::StartProcessorIfNotStarted() {
if (processor_) {
processor_->AddCurrentStack(isolate_);
@@ -426,7 +426,7 @@ void CpuProfiler::LogBuiltins() {
CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
Builtins::Name id = static_cast<Builtins::Name>(i);
- rec->start = builtins->builtin(id)->address();
+ rec->instruction_start = builtins->builtin(id)->InstructionStart();
rec->builtin_id = id;
processor_->Enqueue(evt_rec);
}
diff --git a/src/profiler/cpu-profiler.h b/src/profiler/cpu-profiler.h
index 1ed0975c3a..4e56c7bd74 100644
--- a/src/profiler/cpu-profiler.h
+++ b/src/profiler/cpu-profiler.h
@@ -53,9 +53,9 @@ class CodeEventRecord {
class CodeCreateEventRecord : public CodeEventRecord {
public:
- Address start;
+ Address instruction_start;
CodeEntry* entry;
- unsigned size;
+ unsigned instruction_size;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
@@ -63,8 +63,8 @@ class CodeCreateEventRecord : public CodeEventRecord {
class CodeMoveEventRecord : public CodeEventRecord {
public:
- Address from;
- Address to;
+ Address from_instruction_start;
+ Address to_instruction_start;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
@@ -72,7 +72,7 @@ class CodeMoveEventRecord : public CodeEventRecord {
class CodeDisableOptEventRecord : public CodeEventRecord {
public:
- Address start;
+ Address instruction_start;
const char* bailout_reason;
INLINE(void UpdateCodeMap(CodeMap* code_map));
@@ -81,11 +81,13 @@ class CodeDisableOptEventRecord : public CodeEventRecord {
class CodeDeoptEventRecord : public CodeEventRecord {
public:
- Address start;
+ Address instruction_start;
const char* deopt_reason;
int deopt_id;
Address pc;
int fp_to_sp_delta;
+ CpuProfileDeoptFrame* deopt_frames;
+ int deopt_frame_count;
INLINE(void UpdateCodeMap(CodeMap* code_map));
};
@@ -93,7 +95,7 @@ class CodeDeoptEventRecord : public CodeEventRecord {
class ReportBuiltinEventRecord : public CodeEventRecord {
public:
- Address start;
+ Address instruction_start;
Builtins::Name builtin_id;
INLINE(void UpdateCodeMap(CodeMap* code_map));
@@ -197,10 +199,13 @@ class CpuProfiler : public CodeEventObserver {
static void CollectSample(Isolate* isolate);
+ typedef v8::CpuProfilingMode ProfilingMode;
+
void set_sampling_interval(base::TimeDelta value);
void CollectSample();
- void StartProfiling(const char* title, bool record_samples = false);
- void StartProfiling(String* title, bool record_samples);
+ void StartProfiling(const char* title, bool record_samples = false,
+ ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
+ void StartProfiling(String* title, bool record_samples, ProfilingMode mode);
CpuProfile* StopProfiling(const char* title);
CpuProfile* StopProfiling(String* title);
int GetProfilesCount();
diff --git a/src/profiler/profile-generator-inl.h b/src/profiler/profile-generator-inl.h
index 7ed6d54e17..31652ba9f9 100644
--- a/src/profiler/profile-generator-inl.h
+++ b/src/profiler/profile-generator-inl.h
@@ -33,10 +33,11 @@ inline CodeEntry* ProfileGenerator::FindEntry(Address address) {
}
ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry,
- ProfileNode* parent)
+ ProfileNode* parent, int line_number)
: tree_(tree),
entry_(entry),
self_ticks_(0),
+ line_number_(line_number),
parent_(parent),
id_(tree->next_node_id()) {
tree_->EnqueueNode(this);
diff --git a/src/profiler/profile-generator.cc b/src/profiler/profile-generator.cc
index 388078455e..4273234dd2 100644
--- a/src/profiler/profile-generator.cc
+++ b/src/profiler/profile-generator.cc
@@ -131,15 +131,14 @@ const std::vector<std::unique_ptr<CodeEntry>>* CodeEntry::GetInlineStack(
return it != rare_data_->inline_locations_.end() ? &it->second : nullptr;
}
-void CodeEntry::AddDeoptInlinedFrames(
- int deopt_id, std::vector<CpuProfileDeoptFrame> inlined_frames) {
- EnsureRareData()->deopt_inlined_frames_.insert(
- std::make_pair(deopt_id, std::move(inlined_frames)));
-}
-
-bool CodeEntry::HasDeoptInlinedFramesFor(int deopt_id) const {
- return rare_data_ && rare_data_->deopt_inlined_frames_.find(deopt_id) !=
- rare_data_->deopt_inlined_frames_.end();
+void CodeEntry::set_deopt_info(
+ const char* deopt_reason, int deopt_id,
+ std::vector<CpuProfileDeoptFrame> inlined_frames) {
+ DCHECK(!has_deopt_info());
+ RareData* rare_data = EnsureRareData();
+ rare_data->deopt_reason_ = deopt_reason;
+ rare_data->deopt_id_ = deopt_id;
+ rare_data->deopt_inlined_frames_ = std::move(inlined_frames);
}
void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) {
@@ -158,12 +157,11 @@ CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
CpuProfileDeoptInfo info;
info.deopt_reason = rare_data_->deopt_reason_;
DCHECK_NE(kNoDeoptimizationId, rare_data_->deopt_id_);
- if (rare_data_->deopt_inlined_frames_.find(rare_data_->deopt_id_) ==
- rare_data_->deopt_inlined_frames_.end()) {
+ if (rare_data_->deopt_inlined_frames_.empty()) {
info.stack.push_back(CpuProfileDeoptFrame(
{script_id_, static_cast<size_t>(std::max(0, position()))}));
} else {
- info.stack = rare_data_->deopt_inlined_frames_[rare_data_->deopt_id_];
+ info.stack = rare_data_->deopt_inlined_frames_;
}
return info;
}
@@ -180,18 +178,16 @@ void ProfileNode::CollectDeoptInfo(CodeEntry* entry) {
entry->clear_deopt_info();
}
-
-ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
- auto map_entry = children_.find(entry);
+ProfileNode* ProfileNode::FindChild(CodeEntry* entry, int line_number) {
+ auto map_entry = children_.find({entry, line_number});
return map_entry != children_.end() ? map_entry->second : nullptr;
}
-
-ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
- auto map_entry = children_.find(entry);
+ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry, int line_number) {
+ auto map_entry = children_.find({entry, line_number});
if (map_entry == children_.end()) {
- ProfileNode* node = new ProfileNode(tree_, entry, this);
- children_[entry] = node;
+ ProfileNode* node = new ProfileNode(tree_, entry, this, line_number);
+ children_[{entry, line_number}] = node;
children_list_.push_back(node);
return node;
} else {
@@ -234,8 +230,9 @@ bool ProfileNode::GetLineTicks(v8::CpuProfileNode::LineTick* entries,
void ProfileNode::Print(int indent) {
- base::OS::Print("%5u %*s %s %d #%d", self_ticks_, indent, "", entry_->name(),
- entry_->script_id(), id());
+ int line_number = line_number_ != 0 ? line_number_ : entry_->line_number();
+ base::OS::Print("%5u %*s %s:%d %d #%d", self_ticks_, indent, "",
+ entry_->name(), line_number, entry_->script_id(), id());
if (entry_->resource_name()[0] != '\0')
base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
base::OS::Print("\n");
@@ -304,7 +301,33 @@ ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path,
for (auto it = path.rbegin(); it != path.rend(); ++it) {
if (*it == nullptr) continue;
last_entry = *it;
- node = node->FindOrAddChild(*it);
+ node = node->FindOrAddChild(*it, v8::CpuProfileNode::kNoLineNumberInfo);
+ }
+ if (last_entry && last_entry->has_deopt_info()) {
+ node->CollectDeoptInfo(last_entry);
+ }
+ if (update_stats) {
+ node->IncrementSelfTicks();
+ if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) {
+ node->IncrementLineTicks(src_line);
+ }
+ }
+ return node;
+}
+
+ProfileNode* ProfileTree::AddPathFromEnd(const ProfileStackTrace& path,
+ int src_line, bool update_stats,
+ ProfilingMode mode) {
+ ProfileNode* node = root_;
+ CodeEntry* last_entry = nullptr;
+ int parent_line_number = v8::CpuProfileNode::kNoLineNumberInfo;
+ for (auto it = path.rbegin(); it != path.rend(); ++it) {
+ if ((*it).code_entry == nullptr) continue;
+ last_entry = (*it).code_entry;
+ node = node->FindOrAddChild((*it).code_entry, parent_line_number);
+ parent_line_number = mode == ProfilingMode::kCallerLineNumbers
+ ? (*it).line_number
+ : v8::CpuProfileNode::kNoLineNumberInfo;
}
if (last_entry && last_entry->has_deopt_info()) {
node->CollectDeoptInfo(last_entry);
@@ -363,9 +386,10 @@ void ProfileTree::TraverseDepthFirst(Callback* callback) {
using v8::tracing::TracedValue;
CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
- bool record_samples)
+ bool record_samples, ProfilingMode mode)
: title_(title),
record_samples_(record_samples),
+ mode_(mode),
start_time_(base::TimeTicks::HighResolutionNow()),
top_down_(profiler->isolate()),
profiler_(profiler),
@@ -378,14 +402,16 @@ CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
}
void CpuProfile::AddPath(base::TimeTicks timestamp,
- const std::vector<CodeEntry*>& path, int src_line,
+ const ProfileStackTrace& path, int src_line,
bool update_stats) {
ProfileNode* top_frame_node =
- top_down_.AddPathFromEnd(path, src_line, update_stats);
+ top_down_.AddPathFromEnd(path, src_line, update_stats, mode_);
+
if (record_samples_ && !timestamp.IsNull()) {
timestamps_.push_back(timestamp);
samples_.push_back(top_frame_node);
}
+
const int kSamplesFlushCount = 100;
const int kNodesFlushCount = 10;
if (samples_.size() - streaming_next_sample_ >= kSamplesFlushCount ||
@@ -482,13 +508,25 @@ void CpuProfile::Print() {
}
CodeMap::CodeMap() = default;
-CodeMap::~CodeMap() = default;
+
+CodeMap::~CodeMap() {
+ // First clean the free list as it's otherwise impossible to tell
+ // the slot type.
+ unsigned free_slot = free_list_head_;
+ while (free_slot != kNoFreeSlot) {
+ unsigned next_slot = code_entries_[free_slot].next_free_slot;
+ code_entries_[free_slot].entry = nullptr;
+ free_slot = next_slot;
+ }
+ for (auto slot : code_entries_) delete slot.entry;
+}
void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
ClearCodesInRange(addr, addr + size);
- code_map_.emplace(
- addr, CodeEntryInfo{static_cast<unsigned>(code_entries_.size()), size});
- code_entries_.push_back(std::unique_ptr<CodeEntry>(entry));
+ unsigned index = AddCodeEntry(addr, entry);
+ code_map_.emplace(addr, CodeEntryMapInfo{index, size});
+ DCHECK(entry->instruction_start() == kNullAddress ||
+ addr == entry->instruction_start());
}
void CodeMap::ClearCodesInRange(Address start, Address end) {
@@ -499,9 +537,8 @@ void CodeMap::ClearCodesInRange(Address start, Address end) {
}
auto right = left;
for (; right != code_map_.end() && right->first < end; ++right) {
- std::unique_ptr<CodeEntry>& entry = code_entries_[right->second.index];
- if (!entry->used()) {
- entry.reset();
+ if (!entry(right->second.index)->used()) {
+ DeleteCodeEntry(right->second.index);
}
}
code_map_.erase(left, right);
@@ -511,28 +548,51 @@ CodeEntry* CodeMap::FindEntry(Address addr) {
auto it = code_map_.upper_bound(addr);
if (it == code_map_.begin()) return nullptr;
--it;
- Address end_address = it->first + it->second.size;
- if (addr >= end_address) return nullptr;
- CodeEntry* entry = code_entries_[it->second.index].get();
- DCHECK(entry);
- return entry;
+ Address start_address = it->first;
+ Address end_address = start_address + it->second.size;
+ CodeEntry* ret = addr < end_address ? entry(it->second.index) : nullptr;
+ if (ret && ret->instruction_start() != kNullAddress) {
+ DCHECK_EQ(start_address, ret->instruction_start());
+ DCHECK(addr >= start_address && addr < end_address);
+ }
+ return ret;
}
void CodeMap::MoveCode(Address from, Address to) {
if (from == to) return;
auto it = code_map_.find(from);
if (it == code_map_.end()) return;
- CodeEntryInfo info = it->second;
+ CodeEntryMapInfo info = it->second;
code_map_.erase(it);
DCHECK(from + info.size <= to || to + info.size <= from);
ClearCodesInRange(to, to + info.size);
code_map_.emplace(to, info);
+
+ CodeEntry* entry = code_entries_[info.index].entry;
+ entry->set_instruction_start(to);
+}
+
+unsigned CodeMap::AddCodeEntry(Address start, CodeEntry* entry) {
+ if (free_list_head_ == kNoFreeSlot) {
+ code_entries_.push_back(CodeEntrySlotInfo{entry});
+ return static_cast<unsigned>(code_entries_.size()) - 1;
+ }
+ unsigned index = free_list_head_;
+ free_list_head_ = code_entries_[index].next_free_slot;
+ code_entries_[index].entry = entry;
+ return index;
+}
+
+void CodeMap::DeleteCodeEntry(unsigned index) {
+ delete code_entries_[index].entry;
+ code_entries_[index].next_free_slot = free_list_head_;
+ free_list_head_ = index;
}
void CodeMap::Print() {
for (const auto& pair : code_map_) {
base::OS::Print("%p %5d %s\n", reinterpret_cast<void*>(pair.first),
- pair.second.size, code_entries_[pair.second.index]->name());
+ pair.second.size, entry(pair.second.index)->name());
}
}
@@ -542,7 +602,8 @@ CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
current_profiles_semaphore_(1) {}
bool CpuProfilesCollection::StartProfiling(const char* title,
- bool record_samples) {
+ bool record_samples,
+ ProfilingMode mode) {
current_profiles_semaphore_.Wait();
if (static_cast<int>(current_profiles_.size()) >= kMaxSimultaneousProfiles) {
current_profiles_semaphore_.Signal();
@@ -557,7 +618,7 @@ bool CpuProfilesCollection::StartProfiling(const char* title,
}
}
current_profiles_.emplace_back(
- new CpuProfile(profiler_, title, record_samples));
+ new CpuProfile(profiler_, title, record_samples, mode));
current_profiles_semaphore_.Signal();
return true;
}
@@ -608,8 +669,8 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
}
void CpuProfilesCollection::AddPathToCurrentProfiles(
- base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
- int src_line, bool update_stats) {
+ base::TimeTicks timestamp, const ProfileStackTrace& path, int src_line,
+ bool update_stats) {
// As starting / stopping profiles is rare relatively to this
// method, we don't bother minimizing the duration of lock holding,
// e.g. copying contents of the list to a local vector.
@@ -624,47 +685,52 @@ ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
: profiles_(profiles) {}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
- std::vector<CodeEntry*> entries;
+ ProfileStackTrace stack_trace;
// Conservatively reserve space for stack frames + pc + function + vm-state.
// There could in fact be more of them because of inlined entries.
- entries.reserve(sample.frames_count + 3);
+ stack_trace.reserve(sample.frames_count + 3);
// The ProfileNode knows nothing about all versions of generated code for
// the same JS function. The line number information associated with
// the latest version of generated code is used to find a source line number
// for a JS function. Then, the detected source line is passed to
// ProfileNode to increase the tick count for this source line.
- int src_line = v8::CpuProfileNode::kNoLineNumberInfo;
+ const int no_line_info = v8::CpuProfileNode::kNoLineNumberInfo;
+ int src_line = no_line_info;
bool src_line_not_found = true;
if (sample.pc != nullptr) {
if (sample.has_external_callback && sample.state == EXTERNAL) {
// Don't use PC when in external callback code, as it can point
- // inside callback's code, and we will erroneously report
+ // inside a callback's code, and we will erroneously report
// that a callback calls itself.
- entries.push_back(
- FindEntry(reinterpret_cast<Address>(sample.external_callback_entry)));
+ stack_trace.push_back(
+ {FindEntry(reinterpret_cast<Address>(sample.external_callback_entry)),
+ no_line_info});
} else {
- CodeEntry* pc_entry = FindEntry(reinterpret_cast<Address>(sample.pc));
- // If there is no pc_entry we're likely in native code.
- // Find out, if top of stack was pointing inside a JS function
- // meaning that we have encountered a frameless invocation.
+ Address attributed_pc = reinterpret_cast<Address>(sample.pc);
+ CodeEntry* pc_entry = FindEntry(attributed_pc);
+ // If there is no pc_entry, we're likely in native code. Find out if the
+ // top of the stack (the return address) was pointing inside a JS
+ // function, meaning that we have encountered a frameless invocation.
if (!pc_entry && !sample.has_external_callback) {
- pc_entry = FindEntry(reinterpret_cast<Address>(sample.tos));
+ attributed_pc = reinterpret_cast<Address>(sample.tos);
+ pc_entry = FindEntry(attributed_pc);
}
// If pc is in the function code before it set up stack frame or after the
- // frame was destroyed SafeStackFrameIterator incorrectly thinks that
- // ebp contains return address of the current function and skips caller's
- // frame. Check for this case and just skip such samples.
+ // frame was destroyed, SafeStackFrameIterator incorrectly thinks that
+ // ebp contains the return address of the current function and skips the
+ // caller's frame. Check for this case and just skip such samples.
if (pc_entry) {
- int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) -
- pc_entry->instruction_start());
+ int pc_offset =
+ static_cast<int>(attributed_pc - pc_entry->instruction_start());
+ DCHECK_GE(pc_offset, 0);
src_line = pc_entry->GetSourceLine(pc_offset);
if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) {
src_line = pc_entry->line_number();
}
src_line_not_found = false;
- entries.push_back(pc_entry);
+ stack_trace.push_back({pc_entry, src_line});
if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
@@ -675,7 +741,8 @@ 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(CodeEntry::unresolved_entry());
+ stack_trace.push_back(
+ {CodeEntry::unresolved_entry(), no_line_info});
}
}
}
@@ -684,17 +751,21 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
for (unsigned i = 0; i < sample.frames_count; ++i) {
Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
CodeEntry* entry = FindEntry(stack_pos);
+ int line_number = no_line_info;
if (entry) {
// Find out if the entry has an inlining stack associated.
int pc_offset =
static_cast<int>(stack_pos - entry->instruction_start());
+ DCHECK_GE(pc_offset, 0);
const std::vector<std::unique_ptr<CodeEntry>>* inline_stack =
entry->GetInlineStack(pc_offset);
if (inline_stack) {
std::transform(
inline_stack->rbegin(), inline_stack->rend(),
- std::back_inserter(entries),
- [](const std::unique_ptr<CodeEntry>& ptr) { return ptr.get(); });
+ std::back_inserter(stack_trace),
+ [=](const std::unique_ptr<CodeEntry>& ptr) {
+ return CodeEntryAndLineNumber{ptr.get(), no_line_info};
+ });
}
// Skip unresolved frames (e.g. internal frame) and get source line of
// the first JS caller.
@@ -705,26 +776,27 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
}
src_line_not_found = false;
}
+ line_number = entry->GetSourceLine(pc_offset);
}
- entries.push_back(entry);
+ stack_trace.push_back({entry, line_number});
}
}
if (FLAG_prof_browser_mode) {
bool no_symbolized_entries = true;
- for (auto e : entries) {
- if (e != nullptr) {
+ for (auto e : stack_trace) {
+ if (e.code_entry != nullptr) {
no_symbolized_entries = false;
break;
}
}
// If no frames were symbolized, put the VM state entry in.
if (no_symbolized_entries) {
- entries.push_back(EntryForVMState(sample.state));
+ stack_trace.push_back({EntryForVMState(sample.state), no_line_info});
}
}
- profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line,
+ profiles_->AddPathToCurrentProfiles(sample.timestamp, stack_trace, src_line,
sample.update_stats);
}
diff --git a/src/profiler/profile-generator.h b/src/profiler/profile-generator.h
index 3b9c083b6d..e575a78648 100644
--- a/src/profiler/profile-generator.h
+++ b/src/profiler/profile-generator.h
@@ -6,9 +6,11 @@
#define V8_PROFILER_PROFILE_GENERATOR_H_
#include <deque>
+#include <limits>
#include <map>
#include <memory>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "include/v8-profiler.h"
@@ -72,12 +74,9 @@ class CodeEntry {
return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason;
}
- void set_deopt_info(const char* deopt_reason, int deopt_id) {
- DCHECK(!has_deopt_info());
- RareData* rare_data = EnsureRareData();
- rare_data->deopt_reason_ = deopt_reason;
- rare_data->deopt_id_ = deopt_id;
- }
+ void set_deopt_info(const char* deopt_reason, int deopt_id,
+ std::vector<CpuProfileDeoptFrame> inlined_frames);
+
CpuProfileDeoptInfo GetDeoptInfo();
bool has_deopt_info() const {
return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
@@ -108,10 +107,9 @@ class CodeEntry {
const std::vector<std::unique_ptr<CodeEntry>>* GetInlineStack(
int pc_offset) const;
- void AddDeoptInlinedFrames(int deopt_id, std::vector<CpuProfileDeoptFrame>);
- bool HasDeoptInlinedFramesFor(int deopt_id) const;
-
+ void set_instruction_start(Address start) { instruction_start_ = start; }
Address instruction_start() const { return instruction_start_; }
+
CodeEventListener::LogEventsAndTags tag() const {
return TagField::decode(bit_field_);
}
@@ -143,8 +141,7 @@ class CodeEntry {
int deopt_id_ = kNoDeoptimizationId;
std::unordered_map<int, std::vector<std::unique_ptr<CodeEntry>>>
inline_locations_;
- std::unordered_map<int, std::vector<CpuProfileDeoptFrame>>
- deopt_inlined_frames_;
+ std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_;
};
RareData* EnsureRareData();
@@ -189,15 +186,24 @@ class CodeEntry {
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
};
+struct CodeEntryAndLineNumber {
+ CodeEntry* code_entry;
+ int line_number;
+};
+
+typedef std::vector<CodeEntryAndLineNumber> ProfileStackTrace;
class ProfileTree;
class ProfileNode {
public:
- inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent);
+ inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent,
+ int line_number = 0);
- ProfileNode* FindChild(CodeEntry* entry);
- ProfileNode* FindOrAddChild(CodeEntry* entry);
+ ProfileNode* FindChild(
+ CodeEntry* entry,
+ int line_number = v8::CpuProfileNode::kNoLineNumberInfo);
+ ProfileNode* FindOrAddChild(CodeEntry* entry, int line_number = 0);
void IncrementSelfTicks() { ++self_ticks_; }
void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
void IncrementLineTicks(int src_line);
@@ -208,6 +214,10 @@ class ProfileNode {
unsigned id() const { return id_; }
unsigned function_id() const;
ProfileNode* parent() const { return parent_; }
+ int line_number() const {
+ return line_number_ != 0 ? line_number_ : entry_->line_number();
+ }
+
unsigned int GetHitLineCount() const {
return static_cast<unsigned int>(line_ticks_.size());
}
@@ -222,20 +232,25 @@ class ProfileNode {
void Print(int indent);
private:
- struct CodeEntryEqual {
- bool operator()(CodeEntry* entry1, CodeEntry* entry2) const {
- return entry1 == entry2 || entry1->IsSameFunctionAs(entry2);
+ struct Equals {
+ bool operator()(CodeEntryAndLineNumber lhs,
+ CodeEntryAndLineNumber rhs) const {
+ return lhs.code_entry->IsSameFunctionAs(rhs.code_entry) &&
+ lhs.line_number == rhs.line_number;
}
};
- struct CodeEntryHash {
- std::size_t operator()(CodeEntry* entry) const { return entry->GetHash(); }
+ struct Hasher {
+ std::size_t operator()(CodeEntryAndLineNumber pair) const {
+ return pair.code_entry->GetHash() ^ ComputeIntegerHash(pair.line_number);
+ }
};
ProfileTree* tree_;
CodeEntry* entry_;
unsigned self_ticks_;
- std::unordered_map<CodeEntry*, ProfileNode*, CodeEntryHash, CodeEntryEqual>
+ std::unordered_map<CodeEntryAndLineNumber, ProfileNode*, Hasher, Equals>
children_;
+ int line_number_;
std::vector<ProfileNode*> children_list_;
ProfileNode* parent_;
unsigned id_;
@@ -253,10 +268,17 @@ class ProfileTree {
explicit ProfileTree(Isolate* isolate);
~ProfileTree();
+ typedef v8::CpuProfilingMode ProfilingMode;
+
ProfileNode* AddPathFromEnd(
const std::vector<CodeEntry*>& path,
int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
bool update_stats = true);
+ ProfileNode* AddPathFromEnd(
+ const ProfileStackTrace& path,
+ int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
+ bool update_stats = true,
+ ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
ProfileNode* root() const { return root_; }
unsigned next_node_id() { return next_node_id_++; }
unsigned GetFunctionId(const ProfileNode* node);
@@ -293,10 +315,13 @@ class ProfileTree {
class CpuProfile {
public:
- CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples);
+ typedef v8::CpuProfilingMode ProfilingMode;
+
+ CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples,
+ ProfilingMode mode);
// Add pc -> ... -> main() call path to the profile.
- void AddPath(base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
+ void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path,
int src_line, bool update_stats);
void FinishProfile();
@@ -322,6 +347,7 @@ class CpuProfile {
const char* title_;
bool record_samples_;
+ ProfilingMode mode_;
base::TimeTicks start_time_;
base::TimeTicks end_time_;
std::vector<ProfileNode*> samples_;
@@ -344,15 +370,27 @@ class CodeMap {
void Print();
private:
- struct CodeEntryInfo {
+ struct CodeEntryMapInfo {
unsigned index;
unsigned size;
};
+ union CodeEntrySlotInfo {
+ CodeEntry* entry;
+ unsigned next_free_slot;
+ };
+
+ static constexpr unsigned kNoFreeSlot = std::numeric_limits<unsigned>::max();
+
void ClearCodesInRange(Address start, Address end);
+ unsigned AddCodeEntry(Address start, CodeEntry*);
+ void DeleteCodeEntry(unsigned index);
- std::deque<std::unique_ptr<CodeEntry>> code_entries_;
- std::map<Address, CodeEntryInfo> code_map_;
+ CodeEntry* entry(unsigned index) { return code_entries_[index].entry; }
+
+ std::deque<CodeEntrySlotInfo> code_entries_;
+ std::map<Address, CodeEntryMapInfo> code_map_;
+ unsigned free_list_head_ = kNoFreeSlot;
DISALLOW_COPY_AND_ASSIGN(CodeMap);
};
@@ -361,8 +399,11 @@ class CpuProfilesCollection {
public:
explicit CpuProfilesCollection(Isolate* isolate);
+ typedef v8::CpuProfilingMode ProfilingMode;
+
void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
- bool StartProfiling(const char* title, bool record_samples);
+ bool StartProfiling(const char* title, bool record_samples,
+ ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
CpuProfile* StopProfiling(const char* title);
std::vector<std::unique_ptr<CpuProfile>>* profiles() {
return &finished_profiles_;
@@ -373,8 +414,8 @@ class CpuProfilesCollection {
// Called from profile generator thread.
void AddPathToCurrentProfiles(base::TimeTicks timestamp,
- const std::vector<CodeEntry*>& path,
- int src_line, bool update_stats);
+ const ProfileStackTrace& path, int src_line,
+ bool update_stats);
// Limits the number of profiles that can be simultaneously collected.
static const int kMaxSimultaneousProfiles = 100;
diff --git a/src/profiler/profiler-listener.cc b/src/profiler/profiler-listener.cc
index 70d907b216..e3c2c140fb 100644
--- a/src/profiler/profiler-listener.cc
+++ b/src/profiler/profiler-listener.cc
@@ -26,9 +26,9 @@ ProfilerListener::~ProfilerListener() = default;
void ProfilerListener::CallbackEvent(Name* name, Address entry_point) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
- rec->start = entry_point;
+ rec->instruction_start = entry_point;
rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
- rec->size = 1;
+ rec->instruction_size = 1;
DispatchCodeEvent(evt_rec);
}
@@ -36,13 +36,13 @@ 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->instruction_start = code->InstructionStart();
rec->entry = NewCodeEntry(
tag, GetFunctionName(name), CodeEntry::kEmptyResourceName,
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
nullptr, code->InstructionStart());
RecordInliningInfo(rec->entry, code);
- rec->size = code->ExecutableSize();
+ rec->instruction_size = code->InstructionSize();
DispatchCodeEvent(evt_rec);
}
@@ -50,13 +50,13 @@ 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->instruction_start = code->InstructionStart();
rec->entry = NewCodeEntry(
tag, GetFunctionName(name), CodeEntry::kEmptyResourceName,
CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
nullptr, code->InstructionStart());
RecordInliningInfo(rec->entry, code);
- rec->size = code->ExecutableSize();
+ rec->instruction_size = code->InstructionSize();
DispatchCodeEvent(evt_rec);
}
@@ -66,7 +66,7 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
Name* script_name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
- rec->start = code->address();
+ rec->instruction_start = code->InstructionStart();
rec->entry = NewCodeEntry(tag, GetFunctionName(shared->DebugName()),
GetName(InferScriptName(script_name, shared)),
CpuProfileNode::kNoLineNumberInfo,
@@ -74,7 +74,7 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
code->InstructionStart());
RecordInliningInfo(rec->entry, code);
rec->entry->FillFunctionInfo(shared);
- rec->size = code->ExecutableSize();
+ rec->instruction_size = code->InstructionSize();
DispatchCodeEvent(evt_rec);
}
@@ -85,7 +85,7 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
int column) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
- rec->start = abstract_code->address();
+ rec->instruction_start = abstract_code->InstructionStart();
std::unique_ptr<SourcePositionTable> line_table;
if (shared->script()->IsScript()) {
Script* script = Script::cast(shared->script());
@@ -106,9 +106,8 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
GetName(InferScriptName(script_name, shared)), line, column,
std::move(line_table), abstract_code->InstructionStart());
RecordInliningInfo(rec->entry, abstract_code);
- RecordDeoptInlinedFrames(rec->entry, abstract_code);
rec->entry->FillFunctionInfo(shared);
- rec->size = abstract_code->ExecutableSize();
+ rec->instruction_size = abstract_code->InstructionSize();
DispatchCodeEvent(evt_rec);
}
@@ -117,7 +116,7 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
wasm::WasmName name) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
- rec->start = code->instruction_start();
+ rec->instruction_start = code->instruction_start();
// TODO(herhut): Instead of sanitizing here, make sure all wasm functions
// have names.
const char* name_ptr =
@@ -126,15 +125,15 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
CpuProfileNode::kNoLineNumberInfo,
CpuProfileNode::kNoColumnNumberInfo, nullptr,
code->instruction_start());
- rec->size = code->instructions().length();
+ rec->instruction_size = code->instructions().length();
DispatchCodeEvent(evt_rec);
}
-void ProfilerListener::CodeMoveEvent(AbstractCode* from, Address to) {
+void ProfilerListener::CodeMoveEvent(AbstractCode* from, AbstractCode* to) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
- rec->from = from->address();
- rec->to = to;
+ rec->from_instruction_start = from->InstructionStart();
+ rec->to_instruction_start = to->InstructionStart();
DispatchCodeEvent(evt_rec);
}
@@ -142,7 +141,7 @@ void ProfilerListener::CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
- rec->start = code->address();
+ rec->instruction_start = code->InstructionStart();
rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
DispatchCodeEvent(evt_rec);
}
@@ -152,21 +151,25 @@ void ProfilerListener::CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
- rec->start = code->address();
+ rec->instruction_start = code->InstructionStart();
rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
rec->deopt_id = info.deopt_id;
rec->pc = pc;
rec->fp_to_sp_delta = fp_to_sp_delta;
+
+ // When a function is deoptimized, we store the deoptimized frame information
+ // for the use of GetDeoptInfos().
+ AttachDeoptInlinedFrames(code, rec);
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->instruction_start = entry_point;
rec->entry =
NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
- rec->size = 1;
+ rec->instruction_size = 1;
DispatchCodeEvent(evt_rec);
}
@@ -174,23 +177,22 @@ void ProfilerListener::RegExpCodeCreateEvent(AbstractCode* code,
String* source) {
CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
- rec->start = code->address();
+ rec->instruction_start = code->InstructionStart();
rec->entry = NewCodeEntry(
CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
- CpuProfileNode::kNoColumnNumberInfo, nullptr,
- code->raw_instruction_start());
- rec->size = code->ExecutableSize();
+ CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
+ rec->instruction_size = code->InstructionSize();
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->instruction_start = entry_point;
rec->entry =
NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
- rec->size = 1;
+ rec->instruction_size = 1;
DispatchCodeEvent(evt_rec);
}
@@ -254,16 +256,18 @@ void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
}
}
-void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
- AbstractCode* abstract_code) {
- if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
- Handle<Code> code(abstract_code->GetCode());
-
+void ProfilerListener::AttachDeoptInlinedFrames(Code* code,
+ CodeDeoptEventRecord* rec) {
+ int deopt_id = rec->deopt_id;
SourcePosition last_position = SourcePosition::Unknown();
int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
- for (RelocIterator it(*code, mask); !it.done(); it.next()) {
+
+ rec->deopt_frames = nullptr;
+ rec->deopt_frame_count = 0;
+
+ for (RelocIterator it(code, mask); !it.done(); it.next()) {
RelocInfo* info = it.rinfo();
if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
int script_offset = static_cast<int>(info->data());
@@ -274,25 +278,29 @@ void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
continue;
}
if (info->rmode() == RelocInfo::DEOPT_ID) {
- int deopt_id = static_cast<int>(info->data());
+ if (deopt_id != static_cast<int>(info->data())) continue;
DCHECK(last_position.IsKnown());
- std::vector<CpuProfileDeoptFrame> inlined_frames;
// SourcePosition::InliningStack allocates a handle for the SFI of each
// frame. These don't escape this function, but quickly add up. This
// scope limits their lifetime.
HandleScope scope(isolate_);
- for (SourcePositionInfo& pos_info : last_position.InliningStack(code)) {
+ std::vector<SourcePositionInfo> stack =
+ last_position.InliningStack(handle(code));
+ CpuProfileDeoptFrame* deopt_frames =
+ new CpuProfileDeoptFrame[stack.size()];
+
+ int deopt_frame_count = 0;
+ for (SourcePositionInfo& pos_info : stack) {
if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
if (pos_info.script.is_null()) continue;
int script_id = pos_info.script->id();
size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
- inlined_frames.push_back(CpuProfileDeoptFrame({script_id, offset}));
- }
- if (!inlined_frames.empty() &&
- !entry->HasDeoptInlinedFramesFor(deopt_id)) {
- entry->AddDeoptInlinedFrames(deopt_id, std::move(inlined_frames));
+ deopt_frames[deopt_frame_count++] = {script_id, offset};
}
+ rec->deopt_frames = deopt_frames;
+ rec->deopt_frame_count = deopt_frame_count;
+ break;
}
}
}
diff --git a/src/profiler/profiler-listener.h b/src/profiler/profiler-listener.h
index eafefcd753..313a6808c4 100644
--- a/src/profiler/profiler-listener.h
+++ b/src/profiler/profiler-listener.h
@@ -15,6 +15,7 @@ namespace v8 {
namespace internal {
class CodeEventsContainer;
+class CodeDeoptEventRecord;
class CodeEventObserver {
public:
@@ -43,7 +44,7 @@ class ProfilerListener : public CodeEventListener {
wasm::WasmName name) override;
void CodeMovingGCEvent() override {}
- void CodeMoveEvent(AbstractCode* from, Address to) override;
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override;
void CodeDisableOptEvent(AbstractCode* code,
SharedFunctionInfo* shared) override;
void CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
@@ -79,7 +80,7 @@ class ProfilerListener : public CodeEventListener {
private:
void RecordInliningInfo(CodeEntry* entry, AbstractCode* abstract_code);
- void RecordDeoptInlinedFrames(CodeEntry* entry, AbstractCode* abstract_code);
+ void AttachDeoptInlinedFrames(Code* code, CodeDeoptEventRecord* rec);
Name* InferScriptName(Name* name, SharedFunctionInfo* info);
V8_INLINE void DispatchCodeEvent(const CodeEventsContainer& evt_rec) {
observer_->CodeEventHandler(evt_rec);
diff --git a/src/snapshot/serializer.h b/src/snapshot/serializer.h
index c387bc046a..f1061a6c2f 100644
--- a/src/snapshot/serializer.h
+++ b/src/snapshot/serializer.h
@@ -28,8 +28,8 @@ class CodeAddressMap : public CodeEventLogger {
isolate_->logger()->RemoveCodeEventListener(this);
}
- void CodeMoveEvent(AbstractCode* from, Address to) override {
- address_to_name_map_.Move(from->address(), to);
+ void CodeMoveEvent(AbstractCode* from, AbstractCode* to) override {
+ address_to_name_map_.Move(from->address(), to->address());
}
void CodeDisableOptEvent(AbstractCode* code,
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index d6d6030222..da069e0a26 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -75,6 +75,7 @@
# BUG(5193). The cpu profiler tests are notoriously flaky.
'test-profile-generator/RecordStackTraceAtStartProfiling': [SKIP],
'test-cpu-profiler/CollectCpuProfile': [SKIP],
+ 'test-cpu-profiler/CollectCpuProfileCallerLineNumbers': [FAIL, PASS],
'test-cpu-profiler/CollectCpuProfileSamples': [SKIP],
'test-cpu-profiler/CollectDeoptEvents': [SKIP],
'test-cpu-profiler/CpuProfileDeepStack': [SKIP],
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 4936542e14..f74bdf1ede 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -176,27 +176,29 @@ TEST(CodeEvents) {
"comment");
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code,
"comment2");
- profiler_listener.CodeMoveEvent(comment2_code, moved_code->address());
+ profiler_listener.CodeMoveEvent(comment2_code, moved_code);
// Enqueue a tick event to enable code events processing.
- EnqueueTickSampleEvent(processor, aaa_code->address());
+ EnqueueTickSampleEvent(processor, aaa_code->InstructionStart());
isolate->logger()->RemoveCodeEventListener(&profiler_listener);
processor->StopSynchronously();
// Check the state of profile generator.
- CodeEntry* aaa = generator->code_map()->FindEntry(aaa_code->address());
+ CodeEntry* aaa =
+ generator->code_map()->FindEntry(aaa_code->InstructionStart());
CHECK(aaa);
CHECK_EQ(0, strcmp(aaa_str, aaa->name()));
CodeEntry* comment =
- generator->code_map()->FindEntry(comment_code->address());
+ generator->code_map()->FindEntry(comment_code->InstructionStart());
CHECK(comment);
CHECK_EQ(0, strcmp("comment", comment->name()));
- CHECK(!generator->code_map()->FindEntry(comment2_code->address()));
+ CHECK(!generator->code_map()->FindEntry(comment2_code->InstructionStart()));
- CodeEntry* comment2 = generator->code_map()->FindEntry(moved_code->address());
+ CodeEntry* comment2 =
+ generator->code_map()->FindEntry(moved_code->InstructionStart());
CHECK(comment2);
CHECK_EQ(0, strcmp("comment2", comment2->name()));
}
@@ -298,11 +300,11 @@ TEST(Issue1398) {
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
v8::TickSample* sample = processor->StartTickSample();
- sample->pc = reinterpret_cast<void*>(code->address());
+ sample->pc = reinterpret_cast<void*>(code->InstructionStart());
sample->tos = nullptr;
sample->frames_count = v8::TickSample::kMaxFramesCount;
for (unsigned i = 0; i < sample->frames_count; ++i) {
- sample->stack[i] = reinterpret_cast<void*>(code->address());
+ sample->stack[i] = reinterpret_cast<void*>(code->InstructionStart());
}
processor->FinishTickSample();
@@ -428,11 +430,14 @@ class ProfilerHelper {
profiler_->Dispose();
}
+ typedef v8::CpuProfilingMode ProfilingMode;
+
v8::CpuProfile* Run(v8::Local<v8::Function> function,
v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples = 0,
unsigned min_external_samples = 0,
- bool collect_samples = false);
+ bool collect_samples = false,
+ ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
v8::CpuProfiler* profiler() { return profiler_; }
@@ -445,11 +450,11 @@ v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples,
unsigned min_external_samples,
- bool collect_samples) {
+ bool collect_samples, ProfilingMode mode) {
v8::Local<v8::String> profile_name = v8_str("my_profile");
profiler_->SetSamplingInterval(100);
- profiler_->StartProfiling(profile_name, collect_samples);
+ profiler_->StartProfiling(profile_name, mode, collect_samples);
v8::internal::CpuProfiler* iprofiler =
reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
@@ -509,7 +514,6 @@ static const v8::CpuProfileNode* GetChild(v8::Local<v8::Context> context,
return result;
}
-
static void CheckSimpleBranch(v8::Local<v8::Context> context,
const v8::CpuProfileNode* node,
const char* names[], int length) {
@@ -519,7 +523,6 @@ static void CheckSimpleBranch(v8::Local<v8::Context> context,
}
}
-
static const ProfileNode* GetSimpleBranch(v8::Local<v8::Context> context,
v8::CpuProfile* profile,
const char* names[], int length) {
@@ -530,6 +533,41 @@ static const ProfileNode* GetSimpleBranch(v8::Local<v8::Context> context,
return reinterpret_cast<const ProfileNode*>(node);
}
+struct NameLinePair {
+ const char* name;
+ int line_number;
+};
+
+static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
+ NameLinePair pair) {
+ for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
+ const v8::CpuProfileNode* child = node->GetChild(i);
+ // The name and line number must match, or if the requested line number was
+ // -1, then match any function of the same name.
+ if (strcmp(child->GetFunctionNameStr(), pair.name) == 0 &&
+ (child->GetLineNumber() == pair.line_number ||
+ pair.line_number == -1)) {
+ return child;
+ }
+ }
+ return nullptr;
+}
+
+static const v8::CpuProfileNode* GetChild(const v8::CpuProfileNode* node,
+ NameLinePair pair) {
+ const v8::CpuProfileNode* result = FindChild(node, pair);
+ if (!result) FATAL("Failed to GetChild: %s:%d", pair.name, pair.line_number);
+ return result;
+}
+
+static void CheckBranch(const v8::CpuProfileNode* node, NameLinePair path[],
+ int length) {
+ for (int i = 0; i < length; i++) {
+ NameLinePair pair = path[i];
+ node = GetChild(node, pair);
+ }
+}
+
static const char* cpu_profiler_test_source =
"%NeverOptimizeFunction(loop);\n"
"%NeverOptimizeFunction(delay);\n"
@@ -610,6 +648,40 @@ TEST(CollectCpuProfile) {
profile->Delete();
}
+TEST(CollectCpuProfileCallerLineNumbers) {
+ i::FLAG_allow_natives_syntax = true;
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ CompileRun(cpu_profiler_test_source);
+ v8::Local<v8::Function> function = GetFunction(env.local(), "start");
+
+ int32_t profiling_interval_ms = 200;
+ v8::Local<v8::Value> args[] = {
+ v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
+ ProfilerHelper helper(env.local());
+ helper.Run(function, args, arraysize(args), 1000, 0, false,
+ v8::CpuProfilingMode::kCallerLineNumbers);
+ v8::CpuProfile* profile =
+ helper.Run(function, args, arraysize(args), 1000, 0, false,
+ v8::CpuProfilingMode::kCallerLineNumbers);
+
+ const v8::CpuProfileNode* root = profile->GetTopDownRoot();
+ const v8::CpuProfileNode* start_node = GetChild(root, {"start", 27});
+ const v8::CpuProfileNode* foo_node = GetChild(start_node, {"foo", 30});
+
+ NameLinePair bar_branch[] = {{"bar", 23}, {"delay", 19}, {"loop", 18}};
+ CheckBranch(foo_node, bar_branch, arraysize(bar_branch));
+ NameLinePair baz_branch[] = {{"baz", 25}, {"delay", 20}, {"loop", 18}};
+ CheckBranch(foo_node, baz_branch, arraysize(baz_branch));
+ NameLinePair delay_at22_branch[] = {{"delay", 22}, {"loop", 18}};
+ CheckBranch(foo_node, delay_at22_branch, arraysize(delay_at22_branch));
+ NameLinePair delay_at24_branch[] = {{"delay", 24}, {"loop", 18}};
+ CheckBranch(foo_node, delay_at24_branch, arraysize(delay_at24_branch));
+
+ profile->Delete();
+}
+
static const char* hot_deopt_no_frame_entry_test_source =
"%NeverOptimizeFunction(foo);\n"
"%NeverOptimizeFunction(start);\n"
diff --git a/test/cctest/test-profile-generator.cc b/test/cctest/test-profile-generator.cc
index c605efe195..b53bf148e6 100644
--- a/test/cctest/test-profile-generator.cc
+++ b/test/cctest/test-profile-generator.cc
@@ -64,6 +64,25 @@ TEST(ProfileNodeFindOrAddChild) {
CHECK_EQ(childNode3, node->FindOrAddChild(&entry3));
}
+TEST(ProfileNodeFindOrAddChildWithLineNumber) {
+ CcTest::InitializeVM();
+ ProfileTree tree(CcTest::i_isolate());
+ ProfileNode* root = tree.root();
+ CodeEntry a(i::CodeEventListener::FUNCTION_TAG, "a");
+ ProfileNode* a_node = root->FindOrAddChild(&a, -1);
+
+ // a --(22)--> child1
+ // --(23)--> child1
+
+ CodeEntry child1(i::CodeEventListener::FUNCTION_TAG, "child1");
+ ProfileNode* child1_node = a_node->FindOrAddChild(&child1, 22);
+ CHECK(child1_node);
+ CHECK_EQ(child1_node, a_node->FindOrAddChild(&child1, 22));
+
+ ProfileNode* child2_node = a_node->FindOrAddChild(&child1, 23);
+ CHECK(child2_node);
+ CHECK_NE(child1_node, child2_node);
+}
TEST(ProfileNodeFindOrAddChildForSameFunction) {
CcTest::InitializeVM();
@@ -172,6 +191,29 @@ TEST(ProfileTreeAddPathFromEnd) {
CHECK_EQ(1u, node4->self_ticks());
}
+TEST(ProfileTreeAddPathFromEndWithLineNumbers) {
+ CcTest::InitializeVM();
+ CodeEntry a(i::CodeEventListener::FUNCTION_TAG, "a");
+ CodeEntry b(i::CodeEventListener::FUNCTION_TAG, "b");
+ CodeEntry c(i::CodeEventListener::FUNCTION_TAG, "c");
+ ProfileTree tree(CcTest::i_isolate());
+ ProfileTreeTestHelper helper(&tree);
+
+ ProfileStackTrace path = {{&c, 5}, {&b, 3}, {&a, 1}};
+ tree.AddPathFromEnd(path, v8::CpuProfileNode::kNoLineNumberInfo, true,
+ v8::CpuProfilingMode::kCallerLineNumbers);
+
+ ProfileNode* a_node =
+ tree.root()->FindChild(&a, v8::CpuProfileNode::kNoLineNumberInfo);
+ tree.Print();
+ CHECK(a_node);
+
+ ProfileNode* b_node = a_node->FindChild(&b, 1);
+ CHECK(b_node);
+
+ ProfileNode* c_node = b_node->FindChild(&c, 3);
+ CHECK(c_node);
+}
TEST(ProfileTreeCalculateTotalTicks) {
CcTest::InitializeVM();
@@ -634,7 +676,8 @@ int GetFunctionLineNumber(CpuProfiler& profiler, LocalContext& env,
i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
env->Global()->Get(env.local(), v8_str(name)).ToLocalChecked())));
- CodeEntry* func_entry = code_map->FindEntry(func->abstract_code()->address());
+ CodeEntry* func_entry =
+ code_map->FindEntry(func->abstract_code()->InstructionStart());
if (!func_entry) FATAL("%s", name);
return func_entry->line_number();
}
--
2.17.0
From 1cd7204368b26956e0d20354a0b46d260f051d50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Mon, 3 Sep 2018 21:35:31 +0200
Subject: [PATCH 20/23] deps: fix V8 test regression
Fixes a regression introduced in a V8 backport PR.
A small change in cctest/test-log.cc was forgotten.
Refs: https://github.com/nodejs/node/pull/22028
Refs: https://github.com/v8/v8/commit/ba752ea4c50713dff1e94f45a79db3ba968a8d66
PR-URL: https://github.com/nodejs/node/pull/22677
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
---
test/cctest/test-log.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index 767541d4a3..aeafcfc5b6 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -738,7 +738,7 @@ TEST(LogVersion) {
TEST(Issue539892) {
class : public i::CodeEventLogger {
public:
- void CodeMoveEvent(i::AbstractCode* from, Address to) override {}
+ void CodeMoveEvent(i::AbstractCode* from, i::AbstractCode* to) override {}
void CodeDisableOptEvent(i::AbstractCode* code,
i::SharedFunctionInfo* shared) override {}
--
2.17.0
From ce6a2d93481268e25196c3d5f579c16ff6f27eef Mon Sep 17 00:00:00 2001
From: Ali Ijaz Sheikh <ofrobots@google.com>
Date: Fri, 3 Aug 2018 16:22:31 -0700
Subject: [PATCH 21/23] deps: cherry-pick bf5ea81 from upstream V8
Original commit message:
[tracing] allow dynamic control of tracing
If the trace_buffer_ was null, we were returning a pointer to a static
flag back that permanently disabled that particular trace point.
This implied an assumption that tracing will be statically enabled at
process startup, and once it is disabled, it will never be enabled
again. On Node.js side we want to dynamically enable/disable tracing as per
programmer intent.
Change-Id: Ic7a7839b8450ab5c356d85e8e0826f42824907f4
Reviewed-on: https://chromium-review.googlesource.com/1161518
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Ali Ijaz Sheikh <ofrobots@google.com>
Cr-Commit-Position: refs/heads/master@{#54903}
Refs: https://github.com/v8/v8/commit/bf5ea8138c0726613c9d722a3ccb552a8f477992
PR-URL: https://github.com/nodejs/node/pull/22114
Reviewed-By: James M Snell <jasnell@gmail.com>
---
src/libplatform/tracing/tracing-controller.cc | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/libplatform/tracing/tracing-controller.cc b/src/libplatform/tracing/tracing-controller.cc
index 647306d627..b4aa7baf72 100644
--- a/src/libplatform/tracing/tracing-controller.cc
+++ b/src/libplatform/tracing/tracing-controller.cc
@@ -24,18 +24,17 @@ namespace tracing {
// convert internally to determine the category name from the char enabled
// pointer.
const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
- "toplevel", "tracing already shutdown",
+ "toplevel",
"tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
"__metadata"};
// The enabled flag is char instead of bool so that the API can be used from C.
unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = {0};
// Indexes here have to match the g_category_groups array indexes above.
-const int g_category_already_shutdown = 1;
-const int g_category_categories_exhausted = 2;
+const int g_category_categories_exhausted = 1;
// Metadata category not used in V8.
-// const int g_category_metadata = 3;
-const int g_num_builtin_categories = 4;
+// const int g_category_metadata = 2;
+const int g_num_builtin_categories = 3;
// Skip default categories.
v8::base::AtomicWord g_category_index = g_num_builtin_categories;
@@ -103,10 +102,6 @@ void TracingController::UpdateTraceEventDuration(
const uint8_t* TracingController::GetCategoryGroupEnabled(
const char* category_group) {
- if (!trace_buffer_) {
- DCHECK(!g_category_group_enabled[g_category_already_shutdown]);
- return &g_category_group_enabled[g_category_already_shutdown];
- }
return GetCategoryGroupEnabledInternal(category_group);
}
--
2.17.0
From c6b0002d12192fbc1f3412368e90b291b3899e98 Mon Sep 17 00:00:00 2001
From: Ben Newman <ben@meteor.com>
Date: Sat, 4 Aug 2018 11:28:29 -0400
Subject: [PATCH 22/23] deps: backport a8f6869 from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Original commit message:
[debug] Fully implement Debug::ArchiveDebug and Debug::RestoreDebug.
I have a project that embeds V8 and uses a single `Isolate` from multiple
threads. The program runs just fine, but sometimes the inspector doesn't
stop on the correct line after stepping over a statement that switches
threads behind the scenes, even though the original thread is restored by
the time the next statement is executed.
After some digging, I discovered that the `Debug::ArchiveDebug` and
`Debug::RestoreDebug` methods, which should be responsible for
saving/restoring this `ThreadLocal` information when switching threads,
currently don't do anything.
This commit implements those methods using MemCopy, in the style of other
Archive/Restore methods in the V8 codebase.
Related: https://groups.google.com/forum/#!topic/v8-users/_Qf2rwljRk8
R=yangguo@chromium.org,jgruber@chromium.org
CC=info@bnoordhuis.nl
Bug: v8:7230
Change-Id: Id517c873eb81cd53f7216c7efd441b956cf7f943
Reviewed-on: https://chromium-review.googlesource.com/833260
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54902}
Refs: https://github.com/v8/v8/commit/a8f6869177685cfb9c199c454a86f4698c260515
Fix build errors by matching older V8 APIs used by Node.
It looks like
SetDebugDelegate(debug::DebugDelegate* delegate, bool pass_ownership)
was simplified to just
SetDebugDelegate(debug::DebugDelegate* delegate)
in https://github.com/v8/v8/commit/37dcd837dbafa7f1175be5f01f0def013437c7e7,
but the extra `pass_ownership` parameter is still there in the current
version of `node/deps/v8`. I should be able to fix those tests by passing
`false` for `pass_ownership`.
Also, the `DebugDelegate::BreakProgramRequested` method lost a parameter
in https://github.com/v8/v8/commit/e404670696b4c49d7f8adcdb075b98acab9967dd,
but it's not a parameter I was using in my test, so there shouldn't be any
harm in adding the `exec_state` parameter back to `BreakProgramRequested`
(and continuing to ignore it).
Skip restoring debug state unless thread previously in DebugScope.
A simpler version of the changes I proposed upstream in this V8 change
request: https://chromium-review.googlesource.com/c/v8/v8/+/1168449
In this version, Debug::RestoreDebug never attempts to enter a new
DebugScope, but merely reuses the previous one, if we're returning to a
thread that was previously in a DebugScope. If the thread was not
previously in a DebugScope, I believe it does not need to have any
debugging state restored with ClearOneShot and PrepareStep.
The tests from https://chromium-review.googlesource.com/c/v8/v8/+/833260
still pass, and the failing V8-CI tests are now passing locally for me.
PR-URL: https://github.com/nodejs/node/pull/22122
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
---
AUTHORS | 2 +
src/debug/debug.cc | 29 +++++++--
src/debug/debug.h | 1 +
src/v8threads.cc | 8 ++-
src/v8threads.h | 1 +
test/cctest/test-debug.cc | 129 ++++++++++++++++++++++++++++++++++++++
6 files changed, 162 insertions(+), 8 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index e920bbf42b..afad3a5041 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -32,6 +32,7 @@ Facebook, Inc. <*@fb.com>
Facebook, Inc. <*@oculus.com>
Vewd Software AS <*@vewd.com>
Groupon <*@groupon.com>
+Meteor Development Group <*@meteor.com>
Cloudflare, Inc. <*@cloudflare.com>
Aaron Bieber <deftly@gmail.com>
@@ -49,6 +50,7 @@ Andrei Kashcha <anvaka@gmail.com>
Anna Henningsen <anna@addaleax.net>
Bangfu Tao <bangfu.tao@samsung.com>
Ben Coe <ben@npmjs.com>
+Ben Newman <ben@meteor.com>
Ben Noordhuis <info@bnoordhuis.nl>
Benjamin Tan <demoneaux@gmail.com>
Bert Belder <bertbelder@gmail.com>
diff --git a/src/debug/debug.cc b/src/debug/debug.cc
index b5dbbf8412..d25c259846 100644
--- a/src/debug/debug.cc
+++ b/src/debug/debug.cc
@@ -355,19 +355,36 @@ void Debug::ThreadInit() {
char* Debug::ArchiveDebug(char* storage) {
- // Simply reset state. Don't archive anything.
- ThreadInit();
+ MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
+ ArchiveSpacePerThread());
return storage + ArchiveSpacePerThread();
}
-
char* Debug::RestoreDebug(char* storage) {
- // Simply reset state. Don't restore anything.
- ThreadInit();
+ MemCopy(reinterpret_cast<char*>(&thread_local_), storage,
+ ArchiveSpacePerThread());
+
+ if (in_debug_scope()) {
+ // If this thread was in a DebugScope when we archived it, restore the
+ // previous debugging state now. Note that in_debug_scope() returns
+ // true when thread_local_.current_debug_scope_ (restored by MemCopy
+ // above) is non-null.
+
+ // Clear any one-shot breakpoints that may have been set by the other
+ // thread, and reapply breakpoints for this thread.
+ HandleScope scope(isolate_);
+ ClearOneShot();
+
+ if (thread_local_.last_step_action_ != StepNone) {
+ // Reset the previous step action for this thread.
+ PrepareStep(thread_local_.last_step_action_);
+ }
+ }
+
return storage + ArchiveSpacePerThread();
}
-int Debug::ArchiveSpacePerThread() { return 0; }
+int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
void Debug::Iterate(RootVisitor* v) {
v->VisitRootPointer(Root::kDebug, nullptr, &thread_local_.return_value_);
diff --git a/src/debug/debug.h b/src/debug/debug.h
index 2c83d9855c..17bdfc0368 100644
--- a/src/debug/debug.h
+++ b/src/debug/debug.h
@@ -327,6 +327,7 @@ class Debug {
static int ArchiveSpacePerThread();
void FreeThreadResources() { }
void Iterate(RootVisitor* v);
+ void InitThread(const ExecutionAccess& lock) { ThreadInit(); }
bool CheckExecutionState(int id) {
return CheckExecutionState() && break_id() == id;
diff --git a/src/v8threads.cc b/src/v8threads.cc
index db927010ef..0fb333c1f3 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -45,7 +45,7 @@ void Locker::Initialize(v8::Isolate* isolate) {
} else {
internal::ExecutionAccess access(isolate_);
isolate_->stack_guard()->ClearThread(access);
- isolate_->stack_guard()->InitThread(access);
+ isolate_->thread_manager()->InitThread(access);
}
}
DCHECK(isolate_->thread_manager()->IsLockedByCurrentThread());
@@ -95,6 +95,10 @@ Unlocker::~Unlocker() {
namespace internal {
+void ThreadManager::InitThread(const ExecutionAccess& lock) {
+ isolate_->stack_guard()->InitThread(lock);
+ isolate_->debug()->InitThread(lock);
+}
bool ThreadManager::RestoreThread() {
DCHECK(IsLockedByCurrentThread());
@@ -127,7 +131,7 @@ bool ThreadManager::RestoreThread() {
isolate_->FindPerThreadDataForThisThread();
if (per_thread == nullptr || per_thread->thread_state() == nullptr) {
// This is a new thread.
- isolate_->stack_guard()->InitThread(access);
+ InitThread(access);
return false;
}
ThreadState* state = per_thread->thread_state();
diff --git a/src/v8threads.h b/src/v8threads.h
index bb87afea7d..7fde0c9ec4 100644
--- a/src/v8threads.h
+++ b/src/v8threads.h
@@ -67,6 +67,7 @@ class ThreadManager {
void Lock();
void Unlock();
+ void InitThread(const ExecutionAccess&);
void ArchiveThread();
bool RestoreThread();
void FreeThreadResources();
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index f3daf05a9e..92abcae16e 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -6221,6 +6221,135 @@ TEST(DebugBreakOffThreadTerminate) {
}
+class ArchiveRestoreThread : public v8::base::Thread,
+ public v8::debug::DebugDelegate {
+ public:
+ ArchiveRestoreThread(v8::Isolate* isolate, int spawn_count)
+ : Thread(Options("ArchiveRestoreThread")),
+ isolate_(isolate),
+ debug_(reinterpret_cast<i::Isolate*>(isolate_)->debug()),
+ spawn_count_(spawn_count),
+ break_count_(0) {}
+
+ virtual void Run() {
+ v8::Locker locker(isolate_);
+ isolate_->Enter();
+
+ v8::HandleScope scope(isolate_);
+ v8::Local<v8::Context> context = v8::Context::New(isolate_);
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::Function> test = CompileFunction(isolate_,
+ "function test(n) {\n"
+ " debugger;\n"
+ " return n + 1;\n"
+ "}\n",
+ "test");
+
+ debug_->SetDebugDelegate(this, false);
+ v8::internal::DisableBreak enable_break(debug_, false);
+
+ v8::Local<v8::Value> args[1] = {v8::Integer::New(isolate_, spawn_count_)};
+
+ int result = test->Call(context, context->Global(), 1, args)
+ .ToLocalChecked()
+ ->Int32Value(context)
+ .FromJust();
+
+ // Verify that test(spawn_count_) returned spawn_count_ + 1.
+ CHECK_EQ(spawn_count_ + 1, result);
+
+ isolate_->Exit();
+ }
+
+ void BreakProgramRequested(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> exec_state,
+ const std::vector<v8::debug::BreakpointId>&) {
+ auto stack_traces = v8::debug::StackTraceIterator::Create(isolate_);
+ if (!stack_traces->Done()) {
+ v8::debug::Location location = stack_traces->GetSourceLocation();
+
+ i::PrintF("ArchiveRestoreThread #%d hit breakpoint at line %d\n",
+ spawn_count_, location.GetLineNumber());
+
+ switch (location.GetLineNumber()) {
+ case 1: // debugger;
+ CHECK_EQ(break_count_, 0);
+
+ // Attempt to stop on the next line after the first debugger
+ // statement. If debug->{Archive,Restore}Debug() improperly reset
+ // thread-local debug information, the debugger will fail to stop
+ // before the test function returns.
+ debug_->PrepareStep(StepNext);
+
+ // Spawning threads while handling the current breakpoint verifies
+ // that the parent thread correctly archived and restored the
+ // state necessary to stop on the next line. If not, then control
+ // will simply continue past the `return n + 1` statement.
+ MaybeSpawnChildThread();
+
+ break;
+
+ case 2: // return n + 1;
+ CHECK_EQ(break_count_, 1);
+ break;
+
+ default:
+ CHECK(false);
+ }
+ }
+
+ ++break_count_;
+ }
+
+ void MaybeSpawnChildThread() {
+ if (spawn_count_ > 1) {
+ v8::Unlocker unlocker(isolate_);
+
+ // Spawn a thread that spawns a thread that spawns a thread (and so
+ // on) so that the ThreadManager is forced to archive and restore
+ // the current thread.
+ ArchiveRestoreThread child(isolate_, spawn_count_ - 1);
+ child.Start();
+ child.Join();
+
+ // The child thread sets itself as the debug delegate, so we need to
+ // usurp it after the child finishes, or else future breakpoints
+ // will be delegated to a destroyed ArchiveRestoreThread object.
+ debug_->SetDebugDelegate(this, false);
+
+ // This is the most important check in this test, since
+ // child.GetBreakCount() will return 1 if the debugger fails to stop
+ // on the `return n + 1` line after the grandchild thread returns.
+ CHECK_EQ(child.GetBreakCount(), 2);
+ }
+ }
+
+ int GetBreakCount() { return break_count_; }
+
+ private:
+ v8::Isolate* isolate_;
+ v8::internal::Debug* debug_;
+ const int spawn_count_;
+ int break_count_;
+};
+
+TEST(DebugArchiveRestore) {
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+
+ ArchiveRestoreThread thread(isolate, 5);
+ // Instead of calling thread.Start() and thread.Join() here, we call
+ // thread.Run() directly, to make sure we exercise archive/restore
+ // logic on the *current* thread as well as other threads.
+ thread.Run();
+ CHECK_EQ(thread.GetBreakCount(), 2);
+
+ isolate->Dispose();
+}
+
+
static void DebugEventExpectNoException(
const v8::Debug::EventDetails& event_details) {
v8::DebugEvent event = event_details.GetEvent();
--
2.17.0
From ba9561c5c8df8d93994755c56b51c5d6acc1940e Mon Sep 17 00:00:00 2001
From: Marcel Laverdet <marcel@laverdet.com>
Date: Sun, 29 Jul 2018 10:37:56 -0600
Subject: [PATCH 23/23] deps: cherry-pick 22116dd from upstream V8
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Refs: https://github.com/v8/v8/commit/22116dd6c884c026225e56dd8e442a660193e729
Original commit message:
[snapshot] fix resetting function code.
Unconditionally setting the JSFunction code to that of the SFI
may skip initializing the feedback vector.
R=leszeks@chromium.org
Bug: v8:7857
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I65d4bf32493be4cade2eaf3d665d44f93e80f809
Reviewed-on: https://chromium-review.googlesource.com/1107618
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53881}
PR-URL: https://github.com/nodejs/node/pull/21992
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
---
src/api.cc | 7 +++--
src/snapshot/partial-serializer.cc | 2 +-
test/cctest/test-serialize.cc | 41 ++++++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/src/api.cc b/src/api.cc
index 66eecfa787..ef7e711514 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -766,8 +766,11 @@ StartupData SnapshotCreator::CreateBlob(
// Complete in-object slack tracking for all functions.
fun->CompleteInobjectSlackTrackingIfActive();
- // Also, clear out feedback vectors.
- fun->feedback_cell()->set_value(isolate->heap()->undefined_value());
+ // Also, clear out feedback vectors, or any optimized code.
+ if (fun->has_feedback_vector()) {
+ fun->feedback_cell()->set_value(isolate->heap()->undefined_value());
+ fun->set_code(isolate->builtins()->builtin(i::Builtins::kCompileLazy));
+ }
}
// Clear out re-compilable data from all shared function infos. Any
diff --git a/src/snapshot/partial-serializer.cc b/src/snapshot/partial-serializer.cc
index 8b4c9d8d92..5624ba9887 100644
--- a/src/snapshot/partial-serializer.cc
+++ b/src/snapshot/partial-serializer.cc
@@ -105,7 +105,7 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
// Unconditionally reset the JSFunction to its SFI's code, since we can't
// serialize optimized code anyway.
JSFunction* closure = JSFunction::cast(obj);
- closure->set_code(closure->shared()->GetCode());
+ if (closure->is_compiled()) closure->set_code(closure->shared()->GetCode());
}
CheckRehashability(obj);
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 453cb10881..c26a7e7348 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -2640,6 +2640,47 @@ TEST(SnapshotCreatorNoExternalReferencesDefault) {
delete[] blob.data;
}
+v8::StartupData CreateCustomSnapshotArrayJoinWithKeep() {
+ v8::SnapshotCreator creator;
+ v8::Isolate* isolate = creator.GetIsolate();
+ {
+ v8::HandleScope handle_scope(isolate);
+ {
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+ CompileRun(
+ "[].join('');\n"
+ "function g() { return String([1,2,3]); }\n");
+ ExpectString("g()", "1,2,3");
+ creator.SetDefaultContext(context);
+ }
+ }
+ return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
+}
+
+TEST(SnapshotCreatorArrayJoinWithKeep) {
+ DisableAlwaysOpt();
+ v8::StartupData blob = CreateCustomSnapshotArrayJoinWithKeep();
+
+ // Deserialize with an incomplete list of external references.
+ {
+ v8::Isolate::CreateParams params;
+ params.snapshot_blob = &blob;
+ params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ // Test-appropriate equivalent of v8::Isolate::New.
+ v8::Isolate* isolate = TestIsolate::New(params);
+ {
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+ ExpectString("g()", "1,2,3");
+ }
+ isolate->Dispose();
+ }
+ delete[] blob.data;
+}
+
TEST(SnapshotCreatorNoExternalReferencesCustomFail1) {
DisableAlwaysOpt();
v8::StartupData blob = CreateSnapshotWithDefaultAndCustom();
--
2.17.0