Adds external CodeObserver callback to the VM.
This enables external (such as by the embedder) accounting of the code objects for resolving stack traces.
Bug: b/117752777
Change-Id: I824fb9b27e9aa97373b155cf07e9e8f17722ba07
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/85360
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc
index 70022e8..019522b 100644
--- a/runtime/bin/run_vm_tests.cc
+++ b/runtime/bin/run_vm_tests.cc
@@ -322,7 +322,7 @@
dart::bin::DartUtils::OpenFile, dart::bin::DartUtils::ReadFile,
dart::bin::DartUtils::WriteFile, dart::bin::DartUtils::CloseFile,
nullptr /* entropy_source */, nullptr /* get_service_assets */,
- start_kernel_isolate);
+ start_kernel_isolate, nullptr /* observer */);
if (error != nullptr) {
Syslog::PrintErr("Failed to initialize VM: %s\n", error);
free(error);
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 31a7d1b..16a5b80 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -714,7 +714,31 @@
* The current version of the Dart_InitializeFlags. Should be incremented every
* time Dart_InitializeFlags changes in a binary incompatible way.
*/
-#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000003)
+#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000004)
+
+/** Forward declaration */
+struct Dart_CodeObserver;
+
+/**
+ * Callback provided by the embedder that is used by the VM to notify on code
+ * object creation, *before* it is invoked the first time.
+ * This is useful for embedders wanting to e.g. keep track of PCs beyond
+ * the lifetime of the garbage collected code objects.
+ * Note that an address range may be used by more than one code object over the
+ * lifecycle of a process. Clients of this function should record timestamps for
+ * these compilation events and when collecting PCs to disambiguate reused
+ * address ranges.
+ */
+typedef void (*Dart_OnNewCodeCallback)(struct Dart_CodeObserver* observer,
+ const char* name,
+ uintptr_t base,
+ uintptr_t size);
+
+typedef struct Dart_CodeObserver {
+ void* data;
+
+ Dart_OnNewCodeCallback on_new_code;
+} Dart_CodeObserver;
/**
* Describes how to initialize the VM. Used with Dart_Initialize.
@@ -736,6 +760,8 @@
* \param get_service_assets A function to be called by the service isolate when
* it requires the vmservice assets archive.
* See Dart_GetVMServiceAssetsArchive.
+ * \param code_observer An external code observer callback function.
+ * The observer can be invoked as early as during the Dart_Initialize() call.
*/
typedef struct {
int32_t version;
@@ -752,6 +778,7 @@
Dart_EntropySource entropy_source;
Dart_GetVMServiceAssetsArchive get_service_assets;
bool start_kernel_isolate;
+ Dart_CodeObserver* code_observer;
} Dart_InitializeParams;
/**
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 78c8eb0..ee36e81 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -7,6 +7,7 @@
#include "platform/assert.h"
#include "vm/bootstrap.h"
#include "vm/class_id.h"
+#include "vm/code_observers.h"
#include "vm/compiler/backend/code_statistics.h"
#include "vm/compiler/relocation.h"
#include "vm/dart.h"
@@ -1572,6 +1573,19 @@
code->ptr()->state_bits_ = d->Read<int32_t>();
}
}
+
+#if !(defined(DART_PRECOMPILED_RUNTIME) || defined(PRODUCT))
+ void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
+ if (!CodeObservers::AreActive()) return;
+ Code& code = Code::Handle(zone);
+ Function& function = Function::Handle(zone);
+ for (intptr_t id = start_index_; id < stop_index_; id++) {
+ code ^= refs.At(id);
+ function = code.function();
+ Code::NotifyCodeObservers(function, code, code.is_optimized());
+ }
+ }
+#endif // !DART_PRECOMPILED_RUNTIME
};
#if !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/code_observers.cc b/runtime/vm/code_observers.cc
index 7832900..6aab6e7 100644
--- a/runtime/vm/code_observers.cc
+++ b/runtime/vm/code_observers.cc
@@ -15,6 +15,30 @@
intptr_t CodeObservers::observers_length_ = 0;
CodeObserver** CodeObservers::observers_ = NULL;
+class ExternalCodeObserverAdapter : public CodeObserver {
+ public:
+ explicit ExternalCodeObserverAdapter(Dart_CodeObserver* delegate)
+ : delegate_(delegate) {}
+
+ virtual bool IsActive() const { return true; }
+
+ virtual void Notify(const char* name,
+ uword base,
+ uword prologue_offset,
+ uword size,
+ bool optimized,
+ const CodeComments* comments) {
+ return delegate_->on_new_code(delegate_, name, base, size);
+ }
+
+ private:
+ Dart_CodeObserver* delegate_;
+};
+
+void CodeObservers::RegisterExternal(Dart_CodeObserver* observer) {
+ if (observer != nullptr) Register(new ExternalCodeObserverAdapter(observer));
+}
+
void CodeObservers::Register(CodeObserver* observer) {
observers_length_++;
observers_ = reinterpret_cast<CodeObserver**>(
diff --git a/runtime/vm/code_observers.h b/runtime/vm/code_observers.h
index aa6221d..3664a02 100644
--- a/runtime/vm/code_observers.h
+++ b/runtime/vm/code_observers.h
@@ -8,6 +8,8 @@
#include "vm/allocation.h"
#include "vm/globals.h"
+#include "include/dart_api.h"
+
namespace dart {
#ifndef PRODUCT
@@ -55,6 +57,8 @@
public:
static void Init();
+ static void RegisterExternal(Dart_CodeObserver* observer);
+
static void Register(CodeObserver* observer);
// Notify all active code observers about a newly created code object.
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index c0543df..e43381d 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -140,7 +140,8 @@
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets,
- bool start_kernel_isolate) {
+ bool start_kernel_isolate,
+ Dart_CodeObserver* observer) {
CheckOffsets();
// TODO(iposva): Fix race condition here.
if (vm_isolate_ != NULL || !Flags::Initialized()) {
@@ -194,6 +195,7 @@
set_entropy_source_callback(entropy_source);
OS::Init();
NOT_IN_PRODUCT(CodeObservers::Init());
+ NOT_IN_PRODUCT(CodeObservers::RegisterExternal(observer));
start_time_micros_ = OS::GetCurrentMonotonicMicros();
VirtualMemory::Init();
OSThread::Init();
diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h
index aba4a28..64d10f9 100644
--- a/runtime/vm/dart.h
+++ b/runtime/vm/dart.h
@@ -38,7 +38,8 @@
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets,
- bool start_kernel_isolate);
+ bool start_kernel_isolate,
+ Dart_CodeObserver* observer);
// Returns null if cleanup succeeds, otherwise returns an error message
// (caller owns error message and has to free it).
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 5e84bd0..caa2de9 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1009,7 +1009,7 @@
params->thread_exit, params->file_open, params->file_read,
params->file_write, params->file_close,
params->entropy_source, params->get_service_assets,
- params->start_kernel_isolate);
+ params->start_kernel_isolate, params->code_observer);
}
DART_EXPORT char* Dart_Cleanup() {
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 4841f0d..d00f89b 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -57,6 +57,32 @@
EXPECT(Dart_Cleanup() == NULL);
}
+UNIT_TEST_CASE(DartAPI_DartInitializeCallsCodeObserver) {
+ EXPECT(Dart_SetVMFlags(TesterState::argc, TesterState::argv) == NULL);
+ Dart_InitializeParams params;
+ memset(¶ms, 0, sizeof(Dart_InitializeParams));
+ params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
+ params.vm_snapshot_data = TesterState::vm_snapshot_data;
+ params.create = TesterState::create_callback;
+ params.shutdown = TesterState::shutdown_callback;
+ params.cleanup = TesterState::cleanup_callback;
+ params.start_kernel_isolate = true;
+
+ bool was_called = false;
+ Dart_CodeObserver code_observer;
+ code_observer.data = &was_called;
+ code_observer.on_new_code = [](Dart_CodeObserver* observer, const char* name,
+ uintptr_t base, uintptr_t size) {
+ *static_cast<bool*>(observer->data) = true;
+ };
+ params.code_observer = &code_observer;
+
+ // Reinitialize and ensure we can execute Dart code.
+ EXPECT(Dart_Initialize(¶ms) == NULL);
+ EXPECT(was_called);
+ EXPECT(Dart_Cleanup() == NULL);
+}
+
TEST_CASE(DartAPI_ErrorHandleBasics) {
const char* kScriptChars =
"void testMain() {\n"