[vm] Fix profiler in PRODUCT mode.

* Make sure to start and stop profiler as mutator pauses and resumes.
* Allow profiler to call Code::GetPrologOffset without crashing
* Make sure to emit mapping with id 0 into the perfetto output if
  necessary, otherwise Perfetto UI can't load the profile

The last item seems a bug in Perfetto UI because 0 is supposed
to mean "value not set".

TEST=tested by running _perf_witness tests

Change-Id: I57578ba3efafd2aaf83c2c41b0e0b1f36a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/459744
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
Slava Egorov
2025-11-21 05:34:55 -08:00
committed by Commit Queue
parent 991c48f689
commit 2282387a5b
7 changed files with 40 additions and 10 deletions
+4 -4
View File
@@ -1577,23 +1577,23 @@ DART_EXPORT void Dart_StopProfiling() {
}
DART_EXPORT void Dart_ThreadDisableProfiling() {
#if !defined(PRODUCT)
#if defined(DART_INCLUDE_PROFILER)
OSThread* os_thread = OSThread::Current();
if (os_thread == nullptr) {
return;
}
os_thread->DisableThreadInterrupts();
#endif // !defined(PRODUCT)
#endif // defined(DART_INCLUDE_PROFILER)
}
DART_EXPORT void Dart_ThreadEnableProfiling() {
#if !defined(PRODUCT)
#if defined(DART_INCLUDE_PROFILER)
OSThread* os_thread = OSThread::Current();
if (os_thread == nullptr) {
return;
}
os_thread->EnableThreadInterrupts();
#endif // !defined(PRODUCT)
#endif // defined(DART_INCLUDE_PROFILER)
}
DART_EXPORT void Dart_AddSymbols(const char* dso_name,
-1
View File
@@ -18737,7 +18737,6 @@ void Code::SetPrologueOffset(intptr_t offset) const {
intptr_t Code::GetPrologueOffset() const {
#if defined(PRODUCT)
UNREACHABLE();
return -1;
#else
const Object& object = Object::Handle(untag()->return_address_metadata());
+5
View File
@@ -133,6 +133,11 @@ class OSThread : public BaseThread {
void DisableThreadInterrupts();
void EnableThreadInterrupts();
bool ThreadInterruptsEnabled();
#else
// Used to temporarily disable or enable thread interrupts.
void DisableThreadInterrupts() {}
void EnableThreadInterrupts() {}
bool ThreadInterruptsEnabled() { return false; }
#endif // defined(DART_INCLUDE_PROFILER)
// The currently executing thread, or nullptr if not yet initialized.
+22
View File
@@ -308,6 +308,8 @@ class InternedDataBuilder : public ValueObject {
private:
using SequenceFlags = perfetto::protos::pbzero::TracePacket_SequenceFlags;
enum class UnknownMappingState { kNotNeeded, kNeeded, kEmitted };
public:
// InternedData contains multiple independent interning dictionaries which
// are used for different attributes.
@@ -331,6 +333,12 @@ class InternedDataBuilder : public ValueObject {
InternedDataBuilder() = default;
void MarkNeedUnknownMapping() {
if (unknown_mapping_ == UnknownMappingState::kNotNeeded) {
unknown_mapping_ = UnknownMappingState::kNeeded;
}
}
// Emit all strings added since the last invocation of |AttachInternedDataTo|
// into |interned_data| of the given |TracePacket|.
//
@@ -372,6 +380,18 @@ class InternedDataBuilder : public ValueObject {
}
});
// Perfetto proto message definition claim that mapping iid 0 means
// the same as frame not having mapping information. However Perfetto UI
// fails to load profiles if they contain any frames without mapping iid or
// with 0 mapping iid - but such mapping (with 0 iid) is not present in
// interned mappings. To work-around this bug we simply emit an empty
// mapping with 0 iid if we need it.
if (unknown_mapping_ == UnknownMappingState::kNeeded) {
auto mapping = interned_data->add_mappings();
mapping->set_iid(0);
unknown_mapping_ = UnknownMappingState::kEmitted;
}
mappings_.FlushNewlyInternedTo([interned_data](const auto& interned) {
auto mapping = interned_data->add_mappings();
mapping->set_iid(interned.iid);
@@ -458,6 +478,8 @@ class InternedDataBuilder : public ValueObject {
uint32_t sequence_flags_ = SequenceFlags::SEQ_INCREMENTAL_STATE_CLEARED |
SequenceFlags::SEQ_NEEDS_INCREMENTAL_STATE;
UnknownMappingState unknown_mapping_ = UnknownMappingState::kNotNeeded;
// These are interned in debug_annotation_string_values space.
IdToIidMap isolate_id_to_iid_of_formatted_string_;
IdToIidMap isolate_group_id_to_iid_of_formatted_string_;
+4
View File
@@ -1976,6 +1976,10 @@ void Profile::PrintProfilePerfettoImpl(
/*length=*/1);
}
if (mapping_iid == 0) {
interned_data_builder.MarkNeedUnknownMapping();
}
// Add a |Frame| to the interned data table that is linked to |function|'s
// name and source location (through the interned data table). A Perfetto
// |Callstack| consists of a stack of |Frame|s, so the |Callstack|s
+3 -3
View File
@@ -651,7 +651,7 @@ void Thread::ResumeThreadInternal(Thread* thread) {
thread->set_os_thread(os_thread);
os_thread->set_thread(thread);
Thread::SetCurrent(thread);
NOT_IN_PRODUCT(os_thread->EnableThreadInterrupts());
os_thread->EnableThreadInterrupts();
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
thread->heap_sampler().Initialize();
@@ -667,7 +667,7 @@ void Thread::SuspendThreadInternal(Thread* thread, VMTag::VMTagId tag) {
OSThread* os_thread = thread->os_thread();
ASSERT(os_thread != nullptr);
NOT_IN_PRODUCT(os_thread->DisableThreadInterrupts());
os_thread->DisableThreadInterrupts();
os_thread->set_thread(nullptr);
OSThread::SetCurrent(os_thread);
thread->set_os_thread(nullptr);
@@ -1711,7 +1711,7 @@ void Thread::VisitMutators(MutatorThreadVisitor* visitor) {
});
}
#if !defined(PRODUCT)
#if defined(DART_INCLUDE_PROFILER)
DisableThreadInterruptsScope::DisableThreadInterruptsScope(Thread* thread)
: StackResource(thread) {
if (thread != nullptr) {
+2 -2
View File
@@ -1782,7 +1782,7 @@ class RuntimeCallDeoptScope : public StackResource {
void WindowsThreadCleanUp();
#endif
#if !defined(PRODUCT)
#if defined(DART_INCLUDE_PROFILER)
// Disable thread interrupts.
class DisableThreadInterruptsScope : public StackResource {
public:
@@ -1796,7 +1796,7 @@ class DisableThreadInterruptsScope : public StackResource {
: StackResource(thread) {}
~DisableThreadInterruptsScope() {}
};
#endif // !defined(PRODUCT)
#endif // defined(DART_INCLUDE_PROFILER)
// Within a NoSafepointScope, the thread must not reach any safepoint. Used
// around code that manipulates raw object pointers directly without handles.