blob: 4e1292c956f81f55e103230bf88d0bd98dc74aa3 [file]
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/Panopticon.h"
#include <trace_categories.h>
#include <atomic>
#include <string_view>
#include "common/trace.h"
#include "perfetto/public/te_macros.h"
namespace android::panopticon {
namespace {
std::string_view validateAndStringify(SliceType type) {
auto name = ftl::enum_name(type);
LOG_ALWAYS_FATAL_IF(!name, "Invalid type: %d", type);
return *name;
}
// lol imagine having to debug this
thread_local ftl::SmallMap<std::string, std::shared_ptr<Panopticon>, 10> sPanopticons;
// Stack of IDs to exclusively trace.
// When this is empty, then all panopticons are traced.
thread_local Ids sExclusiveIdStack;
std::optional<SliceToken> slice(std::string id, SliceType sliceType) {
if (auto panopticon = sPanopticons.get(id.c_str()); panopticon) {
return panopticon->get()->makeSlice(sliceType);
}
return std::nullopt;
}
template <std::invocable<Summary&> F>
void report(std::string id, F reporter) {
if (auto panopticon = sPanopticons.get(id.c_str()); panopticon) {
reporter(panopticon->get()->editSummary());
}
}
template <std::invocable<Summary&> F>
void report(F reporter) {
if (sExclusiveIdStack.size() > 0) {
report(sExclusiveIdStack.back(), reporter);
} else {
for (const auto& [id, _] : sPanopticons) {
report(id, reporter);
}
}
}
} // namespace
Panopticon::Panopticon(Source source, const char* suffix, int64_t vsyncId)
: mSource(source), mVsyncId(vsyncId) {
static std::atomic_int64_t sSeed = 0;
mBaseCookie = static_cast<int16_t>(sSeed++) << 16;
mTrack = std::string(
ftl::Concat(ftl::truncated<32>(getSourceName()), " ", ftl::truncated<32>(suffix))
.c_str());
mKey = std::string(suffix);
sliceForType(topSlice()).init(getTrackName(), topSlice(), mBaseCookie + 1);
mTracingCookie = mBaseCookie + 1;
}
Panopticon::~Panopticon() {
SFTRACE_CALL();
// TODO(alecmouri): Also package this up into a bow and upload to statsd
for (auto& slice : mSlices) {
slice.terminate(getTrackName());
}
PERFETTO_TE(
tracing_perfetto::track_event_categories::rendering,
PERFETTO_TE_INSTANT("WorkloadSummary"),
PERFETTO_TE_PROTO_FIELDS(PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_TrackEvent_surfaceflinger_workload_field_number */ 2007,
PERFETTO_TE_PROTO_FIELD_VARINT(1, static_cast<int32_t>(mSource)),
PERFETTO_TE_PROTO_FIELD_BYTES(2, mKey.data(), mKey.size()),
PERFETTO_TE_PROTO_FIELD_VARINT(3, mVsyncId),
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_summary_field_number
*/
4,
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_timings_field_number
*/
1,
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_Timings_sf_cpu_field_number
*/
1,
PERFETTO_TE_PROTO_FIELD_VARINT(
1,
sliceForType(SliceType::CG_Sf_FrameSignal)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
2,
sliceForType(SliceType::CG_Sf_Commit)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
3,
sliceForType(SliceType::CG_Sf_Composite)
.duration())),
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_Timings_hwc_field_number
*/
2,
PERFETTO_TE_PROTO_FIELD_VARINT(
1,
sliceForType(SliceType::CG_Hwc_Present)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
2,
sliceForType(SliceType::CG_Hwc_Validate)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
3,
sliceForType(
SliceType::CG_Hwc_PresentOrValidate)
.duration())),
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_Timings_re_field_number
*/
3,
PERFETTO_TE_PROTO_FIELD_VARINT(
1,
sliceForType(SliceType::CG_Re_drawLayers)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
2,
sliceForType(SliceType::CG_Re_gpu).duration())),
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_Timings_skia_field_number
*/
4,
PERFETTO_TE_PROTO_FIELD_VARINT(
1,
sliceForType(SliceType::CG_Skia_flush)
.duration()),
PERFETTO_TE_PROTO_FIELD_VARINT(
2,
sliceForType(SliceType::CG_Skia_submit)
.duration()))),
PERFETTO_TE_PROTO_FIELD_NESTED(
/* perfetto_protos_AndroidSurfaceFlingerWorkload_Summary_stats_field_number
*/
2,
PERFETTO_TE_PROTO_FIELD_VARINT(
1,
mSummary.gpuRenderedLayers.load(
std::memory_order_acquire)),
PERFETTO_TE_PROTO_FIELD_VARINT(
2,
mSummary.dpuRenderedLayers.load(
std::memory_order_acquire)))))),
PERFETTO_TE_PROTO_TRACK(
PerfettoTeNamedTrackUuid(getTrackName().data(), 1,
PerfettoTeProcessTrackUuid()),
PERFETTO_TE_PROTO_FIELD_CSTR(
perfetto_protos_TrackDescriptor_atrace_name_field_number,
getTrackName().data()),
PERFETTO_TE_PROTO_FIELD_VARINT(
perfetto_protos_TrackDescriptor_parent_uuid_field_number,
PerfettoTeProcessTrackUuid())));
}
void Slice::init(std::string_view track, SliceType slice, int32_t tracingCookie) {
startTime.store(systemTime(), std::memory_order_release);
isTracing.store(true, std::memory_order_release);
cookie = tracingCookie;
SFTRACE_ASYNC_FOR_TRACK_BEGIN(track.data(), validateAndStringify(slice).data(), tracingCookie);
}
void Slice::terminate(std::string_view track) {
if (isTracing.exchange(false, std::memory_order_acq_rel)) {
endTime.store(systemTime(), std::memory_order_release);
SFTRACE_ASYNC_FOR_TRACK_END(track.data(), cookie);
}
}
SliceToken::SliceToken(SliceType type, const std::shared_ptr<Panopticon>& parent)
: mType(type), mKey(parent->getKey()), mSliceCookie(++parent->mTracingCookie) {
parent->sliceForType(mType).init(parent->getTrackName(), mType, mSliceCookie);
}
SliceToken::~SliceToken() {
if (mKey != "") {
if (auto panopticon = sPanopticons.get(mKey.c_str()); panopticon) {
panopticon->get()->sliceForType(mType).terminate(panopticon->get()->getTrackName());
}
}
}
void PanopticonRegistration::start() {
if (mPanopticon) {
sPanopticons.try_emplace(mId, std::move(mPanopticon));
}
}
PanopticonRegistration::~PanopticonRegistration() {
sPanopticons.erase(mId);
}
ExclusiveToken::ExclusiveToken(std::string id) {
sExclusiveIdStack.emplace_back(std::move(id));
}
void ExclusiveToken::cleanup() {
if (!mMovedFrom) {
sExclusiveIdStack.pop_back();
}
}
void make(std::string id, Source source, int64_t vsyncId) {
if (auto panopticon = sPanopticons.get(id.c_str()); panopticon) {
ALOGV("Clobbering metrics for: %s, %s", id.c_str(),
panopticon->get()->getSourceName().data());
}
auto panopticon = Panopticon::make(source, id.c_str(), vsyncId);
sPanopticons.try_emplace(std::move(id), std::move(panopticon));
}
std::shared_ptr<PanopticonRegistration> share() {
std::string view = "";
if (sExclusiveIdStack.size() > 0) {
view = sExclusiveIdStack.back();
} else if (sPanopticons.size() == 1) {
view = sPanopticons.begin()->first;
}
if (view != "") {
if (auto panopticon = sPanopticons.get(view); panopticon) {
return std::make_shared<PanopticonRegistration>(std::move(view), panopticon->get());
}
}
return std::make_shared<PanopticonRegistration>("", nullptr);
}
PanopticonRegistrations share(Ids ids) {
PanopticonRegistrations registrations;
for (const auto& id : ids) {
if (auto panopticon = sPanopticons.get(id); panopticon) {
registrations.emplace_back(
std::make_shared<PanopticonRegistration>(id, panopticon->get()));
}
}
return registrations;
}
void terminate(std::string id) {
sPanopticons.erase(std::move(id));
}
void terminate() {
sPanopticons.clear();
}
ExclusiveToken exclusive(std::string id) {
return ExclusiveToken(std::move(id));
}
SliceTokens slice(SliceType sliceType) {
SliceTokens tokens;
if (sExclusiveIdStack.size() > 0) {
auto token = slice(sExclusiveIdStack.back(), sliceType);
if (token) {
tokens.push_back(std::move(*token));
}
} else {
for (const auto& [id, _] : sPanopticons) {
auto token = slice(id, sliceType);
if (token) {
tokens.push_back(std::move(*token));
}
}
}
return tokens;
}
void reportGpuRenderedLayers(int32_t value) {
report([&](Summary& summary) {
summary.gpuRenderedLayers.store(value, std::memory_order_release);
});
}
void reportDpuRenderedLayers(int32_t value) {
report([&](Summary& summary) {
summary.dpuRenderedLayers.store(value, std::memory_order_release);
});
}
void make(Ids ids, Source source, int64_t vsyncId) {
for (const auto& id : ids) {
make(id, source, vsyncId);
}
}
} // namespace android::panopticon