6741 lines
262 KiB
Diff
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
|
|
|