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>
This commit is contained in:
Clement Skau 2019-06-24 15:42:43 +00:00 committed by commit-bot@chromium.org
parent f03ddc496b
commit 5b113ee193
9 changed files with 103 additions and 5 deletions

View file

@ -322,7 +322,7 @@ static int Main(int argc, const char** argv) {
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);

View file

@ -714,7 +714,31 @@ typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)();
* 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 @@ typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)();
* \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 @@ typedef struct {
Dart_EntropySource entropy_source;
Dart_GetVMServiceAssetsArchive get_service_assets;
bool start_kernel_isolate;
Dart_CodeObserver* code_observer;
} Dart_InitializeParams;
/**

View file

@ -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 @@ class CodeDeserializationCluster : public DeserializationCluster {
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)

View file

@ -15,6 +15,30 @@ Mutex* CodeObservers::mutex_ = NULL;
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**>(

View file

@ -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 @@ class CodeObservers : public AllStatic {
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.

View file

@ -140,7 +140,8 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
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 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
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();

View file

@ -38,7 +38,8 @@ class Dart : public AllStatic {
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).

View file

@ -1009,7 +1009,7 @@ DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) {
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() {

View file

@ -57,6 +57,32 @@ UNIT_TEST_CASE(DartAPI_DartInitializeAfterCleanup) {
EXPECT(Dart_Cleanup() == NULL);
}
UNIT_TEST_CASE(DartAPI_DartInitializeCallsCodeObserver) {
EXPECT(Dart_SetVMFlags(TesterState::argc, TesterState::argv) == NULL);
Dart_InitializeParams params;
memset(&params, 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(&params) == NULL);
EXPECT(was_called);
EXPECT(Dart_Cleanup() == NULL);
}
TEST_CASE(DartAPI_ErrorHandleBasics) {
const char* kScriptChars =
"void testMain() {\n"