Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78b977d4 authored by Adithya Srinivasan's avatar Adithya Srinivasan Committed by Android (Google) Code Review
Browse files

Merge "Frame Timeline Perfetto producer"

parents 7e2bbfa2 01189678
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -14,5 +14,8 @@ cc_library_static {
        "libui",
        "libutils",
    ],
    static_libs: [
        "libperfetto_client_experimental",
    ],
    export_include_dirs: ["."],
}
+124 −12
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
namespace android::frametimeline::impl {

using base::StringAppendF;
using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;

void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
               const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
@@ -143,6 +144,32 @@ std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
                           });
}

FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
    if (jankMetadata & EarlyPresent) {
        return FrameTimelineEvent::PRESENT_EARLY;
    }
    if (jankMetadata & LatePresent) {
        return FrameTimelineEvent::PRESENT_LATE;
    }
    return FrameTimelineEvent::PRESENT_ON_TIME;
}

FrameTimelineEvent::JankType JankTypeToProto(TimeStats::JankType jankType) {
    switch (jankType) {
        case TimeStats::None:
            return FrameTimelineEvent::JANK_NONE;
        case TimeStats::Display:
            return FrameTimelineEvent::JANK_DISPLAY_HAL;
        case TimeStats::SurfaceFlingerDeadlineMissed:
            return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
        case TimeStats::AppDeadlineMissed:
        case TimeStats::PredictionExpired:
            return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
        default:
            return FrameTimelineEvent::JANK_UNKNOWN;
    }
}

int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
    ATRACE_CALL();
    std::lock_guard<std::mutex> lock(mMutex);
@@ -177,10 +204,11 @@ void TokenManager::flushTokens(nsecs_t flushTime) {
    }
}

SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName,
SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                           std::string debugName, PredictionState predictionState,
                           frametimeline::TimelineItem&& predictions)
      : mOwnerPid(ownerPid),
      : mToken(token),
        mOwnerPid(ownerPid),
        mOwnerUid(ownerUid),
        mLayerName(std::move(layerName)),
        mDebugName(std::move(debugName)),
@@ -291,17 +319,70 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t
    dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}

void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
    using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
        std::lock_guard<std::mutex> lock(mMutex);
        if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
            ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str());
            return;
        } else if (displayFrameToken == ISurfaceComposer::INVALID_VSYNC_ID) {
            ALOGD("Cannot trace SurfaceFrame  - %s with invalid displayFrameToken",
                  mLayerName.c_str());
            return;
        }
        auto packet = ctx.NewTracePacket();
        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
        packet->set_timestamp(static_cast<uint64_t>(systemTime()));

        auto* event = packet->set_frame_timeline_event();
        auto* surfaceFrameEvent = event->set_surface_frame();

        surfaceFrameEvent->set_token(mToken);
        surfaceFrameEvent->set_display_frame_token(displayFrameToken);

        if (mPresentState == PresentState::Dropped) {
            surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
        } else if (mPresentState == PresentState::Unknown) {
            surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
        } else {
            surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
        }
        surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
        surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
        surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));

        surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
        surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);

        surfaceFrameEvent->set_actual_start_ns(mActuals.startTime);
        surfaceFrameEvent->set_actual_end_ns(mActuals.endTime);

        surfaceFrameEvent->set_layer_name(mDebugName);
        surfaceFrameEvent->set_pid(mOwnerPid);
    });
}

FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
        mMaxDisplayFrames(kDefaultMaxDisplayFrames),
        mTimeStats(std::move(timeStats)) {}

void FrameTimeline::onBootFinished() {
    perfetto::TracingInitArgs args;
    args.backends = perfetto::kSystemBackend;
    perfetto::Tracing::Initialize(args);
    registerDataSource();
}

void FrameTimeline::registerDataSource() {
    perfetto::DataSourceDescriptor dsd;
    dsd.set_name(kFrameTimelineDataSource);
    FrameTimelineDataSource::Register(dsd);
}

FrameTimeline::DisplayFrame::DisplayFrame()
      : surfaceFlingerPredictions(TimelineItem()),
        surfaceFlingerActuals(TimelineItem()),
        predictionState(PredictionState::None),
        jankType(TimeStats::JankType::None),
        jankMetadata(0) {
      : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
    this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}

@@ -310,17 +391,19 @@ std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfa
        std::optional<int64_t> token) {
    ATRACE_CALL();
    if (!token) {
        return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
        return std::make_unique<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
                                                    ownerUid, std::move(layerName),
                                                    std::move(debugName), PredictionState::None,
                                                    TimelineItem());
    }
    std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
    if (predictions) {
        return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
                                                    std::move(debugName), PredictionState::Valid,
        return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
                                                    std::move(layerName), std::move(debugName),
                                                    PredictionState::Valid,
                                                    std::move(*predictions));
    }
    return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
    return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
                                                std::move(debugName), PredictionState::Expired,
                                                TimelineItem());
}
@@ -340,6 +423,7 @@ void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
    ATRACE_CALL();
    const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
    std::lock_guard<std::mutex> lock(mMutex);
    mCurrentDisplayFrame->token = token;
    if (!prediction) {
        mCurrentDisplayFrame->predictionState = PredictionState::Expired;
    } else {
@@ -408,6 +492,7 @@ void FrameTimeline::flushPendingPresentFences() {
            }

            totalJankReasons |= displayFrame->jankType;
            traceDisplayFrame(*displayFrame);

            for (auto& surfaceFrame : displayFrame->surfaceFrames) {
                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
@@ -419,7 +504,6 @@ void FrameTimeline::flushPendingPresentFences() {
                    if (predictionState == PredictionState::Expired) {
                        // Jank analysis cannot be done on apps that don't use predictions
                        surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
                        continue;
                    } else if (predictionState == PredictionState::Valid) {
                        const auto& actuals = surfaceFrame->getActuals();
                        const auto& predictions = surfaceFrame->getPredictions();
@@ -453,6 +537,7 @@ void FrameTimeline::flushPendingPresentFences() {
                        surfaceFrame->setJankInfo(jankType, jankMetadata);
                    }
                }
                surfaceFrame->traceSurfaceFrame(displayFrame->token);
            }

            mTimeStats->incrementJankyFrames(totalJankReasons);
@@ -569,4 +654,31 @@ void FrameTimeline::reset() {
    setMaxDisplayFrames(kDefaultMaxDisplayFrames);
}

void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
        if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
            ALOGD("Cannot trace DisplayFrame with invalid token");
            return;
        }
        auto packet = ctx.NewTracePacket();
        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
        packet->set_timestamp(static_cast<uint64_t>(systemTime()));

        auto* event = packet->set_frame_timeline_event();
        auto* displayFrameEvent = event->set_display_frame();

        displayFrameEvent->set_token(displayFrame.token);
        displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
        displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
        displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
        displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));

        displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
        displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);

        displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
        displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
    });
}

} // namespace android::frametimeline::impl
+42 −8
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@

#include <../TimeStats/TimeStats.h>
#include <gui/ISurfaceComposer.h>
#include <perfetto/trace/android/frame_timeline_event.pbzero.h>
#include <perfetto/tracing.h>
#include <ui/FenceTime.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -128,6 +130,10 @@ public:
    virtual ~FrameTimeline() = default;
    virtual TokenManager* getTokenManager() = 0;

    // Initializes the Perfetto DataSource that emits DisplayFrame and SurfaceFrame events. Test
    // classes can avoid double registration by mocking this function.
    virtual void onBootFinished() = 0;

    // Create a new surface frame, set the predictions based on a token and return it to the caller.
    // Sets the PredictionState of SurfaceFrame.
    // Debug name is the human-readable debugging string for dumpsys.
@@ -191,8 +197,9 @@ private:

class SurfaceFrame : public android::frametimeline::SurfaceFrame {
public:
    SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
                 PredictionState predictionState, TimelineItem&& predictions);
    SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                 std::string debugName, PredictionState predictionState,
                 TimelineItem&& predictions);
    ~SurfaceFrame() = default;

    TimelineItem getPredictions() const override { return mPredictions; };
@@ -202,6 +209,7 @@ public:
    PredictionState getPredictionState() const override { return mPredictionState; };
    pid_t getOwnerPid() const override { return mOwnerPid; };
    TimeStats::JankType getJankType() const;
    int64_t getToken() const { return mToken; };
    nsecs_t getBaseTime() const;
    uid_t getOwnerUid() const { return mOwnerUid; };
    const std::string& getName() const { return mLayerName; };
@@ -216,7 +224,13 @@ public:
    // All the timestamps are dumped relative to the baseTime
    void dump(std::string& result, const std::string& indent, nsecs_t baseTime);

    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
    // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
    // DisplayFrame at the trace processor side.
    void traceSurfaceFrame(int64_t displayFrameToken);

private:
    const int64_t mToken;
    const pid_t mOwnerPid;
    const uid_t mOwnerUid;
    const std::string mLayerName;
@@ -233,6 +247,12 @@ private:

class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
    class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
        void OnSetup(const SetupArgs&) override{};
        void OnStart(const StartArgs&) override{};
        void OnStop(const StopArgs&) override{};
    };

    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
    ~FrameTimeline() = default;

@@ -249,6 +269,14 @@ public:
    void setMaxDisplayFrames(uint32_t size) override;
    void reset() override;

    // Sets up the perfetto tracing backend and data source.
    void onBootFinished() override;
    // Registers the data source with the perfetto backend. Called as part of onBootFinished()
    // and should not be called manually outside of tests.
    void registerDataSource();

    static constexpr char kFrameTimelineDataSource[] = "android.surfaceflinger.frametimeline";

private:
    // Friend class for testing
    friend class android::frametimeline::FrameTimelineTest;
@@ -259,6 +287,8 @@ private:
    struct DisplayFrame {
        DisplayFrame();

        int64_t token = ISurfaceComposer::INVALID_VSYNC_ID;

        /* Usage of TimelineItem w.r.t SurfaceFlinger
         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
         * endTime      Time when SurfaceFlinger sends a composited frame to Display
@@ -270,7 +300,7 @@ private:
        // Collection of predictions and actual values sent over by Layers
        std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;

        PredictionState predictionState;
        PredictionState predictionState = PredictionState::None;
        TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank
        int32_t jankMetadata = 0x0; // Additional details about the jank
    };
@@ -285,6 +315,10 @@ private:
    void dumpAll(std::string& result);
    void dumpJank(std::string& result);

    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
    // enabled.
    void traceDisplayFrame(const DisplayFrame& displayFrame) REQUIRES(mMutex);

    // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
    std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
    std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
@@ -295,10 +329,10 @@ private:
    uint32_t mMaxDisplayFrames;
    std::shared_ptr<TimeStats> mTimeStats;
    static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
    // The initial container size for the vector<SurfaceFrames> inside display frame. Although this
    // number doesn't represent any bounds on the number of surface frames that can go in a display
    // frame, this is a good starting size for the vector so that we can avoid the internal vector
    // resizing that happens with push_back.
    // The initial container size for the vector<SurfaceFrames> inside display frame. Although
    // this number doesn't represent any bounds on the number of surface frames that can go in a
    // display frame, this is a good starting size for the vector so that we can avoid the
    // internal vector resizing that happens with push_back.
    static constexpr uint32_t kNumSurfaceFramesInitial = 10;
    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
    // compared to prediction, we don't treat it as a jank.
+2 −1
Original line number Diff line number Diff line
@@ -331,7 +331,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
        mInterceptor(mFactory.createSurfaceInterceptor()),
        mTimeStats(std::make_shared<impl::TimeStats>()),
        mFrameTracer(mFactory.createFrameTracer()),
        mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats)),
        mEventQueue(mFactory.createMessageQueue()),
        mCompositionEngine(mFactory.createCompositionEngine()),
        mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -621,6 +621,7 @@ void SurfaceFlinger::bootFinished() {

    mFrameTracer->initialize();
    mTimeStats->onBootFinished();
    mFrameTimeline->onBootFinished();

    // wait patiently for the window manager death
    const String16 name("window");
+6 −0
Original line number Diff line number Diff line
@@ -134,6 +134,12 @@ sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args)
std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
    return std::make_unique<FrameTracer>();
}

std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
        std::shared_ptr<TimeStats> timeStats) {
    return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats);
}

} // namespace android::surfaceflinger

// TODO(b/129481165): remove the #pragma below and fix conversion issues
Loading