[ Profiler ] Avoid building empty CPU profiles when sample streaming is enabled

Building an empty CPU profile is still costly as function tables are
populated whether or not there's samples in the profile. This change
checks to see if any sample in the list of SampleBlocks was collected
under a UserTag with streaming enabled before building the profile.

TEST=CQ, manual testing

Change-Id: Ib526f9999a8b2bf48a7df1488c7dcf959031cf5b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238183
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ben Konyi 2022-03-23 18:30:21 +00:00 committed by Commit Bot
parent 17769033cf
commit d7935c209d
3 changed files with 45 additions and 7 deletions

View file

@ -2422,15 +2422,15 @@ void Isolate::ProcessFreeSampleBlocks(Thread* thread) {
head = next;
} while (head != nullptr);
head = reversed_head;
if (Service::profiler_stream.enabled() && !IsSystemIsolate(this)) {
SampleBlockListProcessor buffer(head);
StackZone zone(thread);
HandleScope handle_scope(thread);
StreamableSampleFilter filter(main_port());
Profile profile;
profile.Build(thread, &filter, &buffer);
if (profile.sample_count() > 0) {
SampleBlockListProcessor buffer(head);
if (buffer.HasStreamableSamples(thread)) {
HandleScope handle_scope(thread);
StreamableSampleFilter filter(main_port());
Profile profile;
profile.Build(thread, &filter, &buffer);
ASSERT(profile.sample_count() > 0);
ServiceEvent event(this, ServiceEvent::kCpuSamples);
event.set_cpu_profile(&profile);
Service::HandleEvent(&event);

View file

@ -713,6 +713,38 @@ ProcessedSampleBuffer* SampleBlockListProcessor::BuildProcessedSampleBuffer(
return buffer;
}
bool SampleBlockListProcessor::HasStreamableSamples(Thread* thread) {
ReusableGrowableObjectArrayHandleScope reusable_array_handle_scope(thread);
Zone* zone = thread->zone();
Isolate* isolate = thread->isolate();
ASSERT(isolate->tag_table() != GrowableObjectArray::null());
GrowableObjectArray& tag_table = reusable_array_handle_scope.Handle();
tag_table ^= isolate->tag_table();
UserTag& tag = UserTag::Handle(zone);
while (head_ != nullptr) {
if (head_->HasStreamableSamples(tag_table, &tag)) {
return true;
}
head_ = head_->next_free_;
}
return false;
}
bool SampleBlock::HasStreamableSamples(const GrowableObjectArray& tag_table,
UserTag* tag) {
for (intptr_t i = 0; i < capacity_; ++i) {
Sample* sample = At(i);
uword sample_tag = sample->user_tag();
for (intptr_t j = 0; j < tag_table.Length(); ++j) {
*tag ^= tag_table.At(j);
if (tag->tag() == sample_tag && tag->streamable()) {
return true;
}
}
}
return false;
}
ProcessedSampleBuffer* SampleBlockBuffer::BuildProcessedSampleBuffer(
SampleFilter* filter,
ProcessedSampleBuffer* buffer) {

View file

@ -756,6 +756,8 @@ class SampleBlock : public SampleBuffer {
virtual Sample* ReserveSampleAndLink(Sample* previous);
protected:
bool HasStreamableSamples(const GrowableObjectArray& tag_table, UserTag* tag);
Isolate* owner_ = nullptr;
bool allocation_block_ = false;
@ -882,6 +884,10 @@ class SampleBlockListProcessor : public ProcessedSampleBufferBuilder {
SampleFilter* filter,
ProcessedSampleBuffer* buffer = nullptr);
// Returns true when at least one sample in the sample block list has a user
// tag with CPU sample streaming enabled.
bool HasStreamableSamples(Thread* thread);
private:
SampleBlock* head_;