[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:
committed by
Commit Queue
parent
991c48f689
commit
2282387a5b
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user