From d02e1de5b1e9d260c5ede9e2cd2264e3eb0dbef1 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Thu, 15 Dec 2022 13:45:10 +0000 Subject: [PATCH] [vm] Add Dart_SetDwarfStackTraceFootnoteCallback This API allows embedder to append arbitrary footers to DWARF stack traces. For example, embedder can append a link to a symbolizer service, which could be used to symbolize the stack-trace. TEST=manually tested, hard to create automatic tests because vm/cc tests only support JIT, not AOT. Bug: b/255741575 Change-Id: Id034b9b3194f7b91a8405574ea771c4a06fda2c2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/275700 Reviewed-by: Martin Kustermann Commit-Queue: Slava Egorov --- runtime/include/dart_api.h | 25 +++++++++++++++++++++++++ runtime/vm/dart.cc | 2 ++ runtime/vm/dart.h | 12 ++++++++++++ runtime/vm/dart_api_impl.cc | 5 +++++ runtime/vm/object.cc | 23 +++++++++++++++++++++++ 5 files changed, 67 insertions(+) diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index a6f93b3f95c..5e564c05c58 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -4104,4 +4104,29 @@ DART_EXPORT void Dart_DumpNativeStackTrace(void* context); */ DART_EXPORT void Dart_PrepareToAbort(void); +/** + * Callback provided by the embedder that is used by the VM to + * produce footnotes appended to DWARF stack traces. + * + * Whenever VM formats a stack trace as a string it would call this callback + * passing raw program counters for each frame in the stack trace. + * + * Embedder can then return a string which if not-null will be appended to the + * formatted stack trace. + * + * Returned string is expected to be `malloc()` allocated. VM takes ownership + * of the returned string and will `free()` it. + * + * \param addresses raw program counter addresses for each frame + * \param count number of elements in the addresses array + */ +typedef char* (*Dart_DwarfStackTraceFootnoteCallback)(void* addresses[], + intptr_t count); + +/** + * Configure DWARF stack trace footnote callback. + */ +DART_EXPORT void Dart_SetDwarfStackTraceFootnoteCallback( + Dart_DwarfStackTraceFootnoteCallback callback); + #endif /* INCLUDE_DART_API_H_ */ /* NOLINT */ diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index 41ad53eadd3..7049c13bda2 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc @@ -69,6 +69,8 @@ Dart_FileWriteCallback Dart::file_write_callback_ = NULL; Dart_FileCloseCallback Dart::file_close_callback_ = NULL; Dart_EntropySource Dart::entropy_source_callback_ = NULL; Dart_GCEventCallback Dart::gc_event_callback_ = nullptr; +Dart_DwarfStackTraceFootnoteCallback Dart::dwarf_stacktrace_footnote_callback_ = + nullptr; // Structure for managing read-only global handles allocation used for // creating global read-only handles that are pre created and initialized diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h index f180defe64b..1c407b21e8c 100644 --- a/runtime/vm/dart.h +++ b/runtime/vm/dart.h @@ -141,6 +141,16 @@ class Dart : public AllStatic { } static Dart_GCEventCallback gc_event_callback() { return gc_event_callback_; } + static void set_dwarf_stacktrace_footnote_callback( + Dart_DwarfStackTraceFootnoteCallback cb) { + dwarf_stacktrace_footnote_callback_ = cb; + } + + static Dart_DwarfStackTraceFootnoteCallback + dwarf_stacktrace_footnote_callback() { + return dwarf_stacktrace_footnote_callback_; + } + private: static char* DartInit(const Dart_InitializeParams* params); @@ -163,6 +173,8 @@ class Dart : public AllStatic { static Dart_FileCloseCallback file_close_callback_; static Dart_EntropySource entropy_source_callback_; static Dart_GCEventCallback gc_event_callback_; + static Dart_DwarfStackTraceFootnoteCallback + dwarf_stacktrace_footnote_callback_; }; } // namespace dart diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index b70f8618ecc..46a9ebe1b6c 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -6310,6 +6310,11 @@ DART_EXPORT void Dart_SetGCEventCallback(Dart_GCEventCallback callback) { Dart::set_gc_event_callback(callback); } +DART_EXPORT void Dart_SetDwarfStackTraceFootnoteCallback( + Dart_DwarfStackTraceFootnoteCallback callback) { + Dart::set_dwarf_stacktrace_footnote_callback(callback); +} + DART_EXPORT char* Dart_SetFileModifiedCallback( Dart_FileModifiedCallback file_modified_callback) { #if !defined(PRODUCT) diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index f4b547c0b43..1d1a5bf9feb 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -26508,6 +26508,14 @@ const char* StackTrace::ToCString() const { NoSafepointScope no_allocation; GrowableArray inlined_functions; GrowableArray inlined_token_positions; + +#if defined(DART_PRECOMPILED_RUNTIME) + GrowableArray addresses(10); + const bool have_footnote_callback = + FLAG_dwarf_stack_traces_mode && + Dart::dwarf_stacktrace_footnote_callback() != nullptr; +#endif + ZoneTextBuffer buffer(zone, 1024); #if defined(DART_PRECOMPILED_RUNTIME) @@ -26635,6 +26643,10 @@ const char* StackTrace::ToCString() const { const uword call_addr = is_future_listener ? pc : pc - 1; if (FLAG_dwarf_stack_traces_mode) { + if (have_footnote_callback) { + addresses.Add(reinterpret_cast(call_addr)); + } + // This output is formatted like Android's debuggerd. Note debuggerd // prints call addresses instead of return addresses. buffer.Printf(" #%02" Pd " abs %" Pp "", frame_index, call_addr); @@ -26690,6 +26702,17 @@ const char* StackTrace::ToCString() const { stack_trace = stack_trace.async_link(); } while (!stack_trace.IsNull()); +#if defined(DART_PRECOMPILED_RUNTIME) + if (have_footnote_callback) { + char* footnote = Dart::dwarf_stacktrace_footnote_callback()( + &addresses[0], addresses.length()); + if (footnote != nullptr) { + buffer.AddString(footnote); + free(footnote); + } + } +#endif + return buffer.buffer(); }