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

Commit 9c696ed6 authored by Yichi Chen's avatar Yichi Chen
Browse files

SF: Import ring buffer with size control in SurfaceTracing

Instead of disabling SurfaceTracing when its size exceeds the
limitation, we import the ring buffer as its storage and records the
latest status. The size of the ring buffer can be controlled when layer
trace is enabled.

Bug: b/115920963
Test: layer trace can keep at most 100MB without increasing memory usage
      in heap after 10 minutes.
Test: layer trace can keep last result in layers_trace.pb
Test: mTrace.enable can control the size of ring buffer

Change-Id: I8ce4b235b657429b7f1d9229befd509e976329fd
parent 6ab82a83
Loading
Loading
Loading
Loading
+51 −27
Original line number Diff line number Diff line
@@ -26,22 +26,48 @@

namespace android {

void SurfaceTracing::enable() {
    ATRACE_CALL();
void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
    // use the swap trick to make sure memory is released
    std::queue<LayersTraceProto>().swap(mStorage);
    mSizeInBytes = newSize;
    mUsedInBytes = 0U;
}

void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
    auto protoSize = proto.ByteSize();
    while (mUsedInBytes + protoSize > mSizeInBytes) {
        if (mStorage.empty()) {
            return;
        }
        mUsedInBytes -= mStorage.front().ByteSize();
        mStorage.pop();
    }
    mUsedInBytes += protoSize;
    mStorage.emplace();
    mStorage.back().Swap(&proto);
}

void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
    fileProto->mutable_entry()->Reserve(mStorage.size());

    while (!mStorage.empty()) {
        auto entry = fileProto->add_entry();
        entry->Swap(&mStorage.front());
        mStorage.pop();
    }
}

void SurfaceTracing::enable(size_t bufferSizeInByte) {
    std::lock_guard<std::mutex> protoGuard(mTraceMutex);

    if (mEnabled) {
        return;
    }
    mEnabled = true;

    mTrace = std::make_unique<LayersTraceFileProto>();
    mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
                             LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
    mBuffer.reset(bufferSizeInByte);
}

status_t SurfaceTracing::disable() {
    ATRACE_CALL();
    std::lock_guard<std::mutex> protoGuard(mTraceMutex);

    if (!mEnabled) {
@@ -51,7 +77,7 @@ status_t SurfaceTracing::disable() {
    status_t err(writeProtoFileLocked());
    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
    mTrace.reset();
    mBuffer.reset(0);
    return err;
}

@@ -65,32 +91,29 @@ void SurfaceTracing::traceLayers(const char* where, LayersProto layers) {
    if (!mEnabled) {
        return;
    }
    LayersTraceProto* entry = mTrace->add_entry();
    entry->set_elapsed_realtime_nanos(elapsedRealtimeNano());
    entry->set_where(where);
    entry->mutable_layers()->Swap(&layers);

    constexpr int maxBufferedEntryCount = 3600;
    if (mTrace->entry_size() >= maxBufferedEntryCount) {
        // TODO: flush buffered entries without disabling tracing
        ALOGE("too many buffered frames; force disable tracing");
        mEnabled = false;
        writeProtoFileLocked();
        mTrace.reset();
    }
    LayersTraceProto entry;
    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
    entry.set_where(where);
    entry.mutable_layers()->Swap(&layers);

    mBuffer.emplace(std::move(entry));
}

status_t SurfaceTracing::writeProtoFileLocked() {
    ATRACE_CALL();

    if (!mTrace->IsInitialized()) {
        return NOT_ENOUGH_DATA;
    }
    LayersTraceFileProto fileProto;
    std::string output;
    if (!mTrace->SerializeToString(&output)) {

    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
    mBuffer.flush(&fileProto);

    if (!fileProto.SerializeToString(&output)) {
        return PERMISSION_DENIED;
    }
    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
    if (!android::base::WriteStringToFile(output, kDefaultFileName, true)) {
        return PERMISSION_DENIED;
    }

@@ -101,7 +124,8 @@ void SurfaceTracing::dump(String8& result) const {
    std::lock_guard<std::mutex> protoGuard(mTraceMutex);

    result.appendFormat("Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
    result.appendFormat("  number of entries: %d\n", mTrace ? mTrace->entry_size() : 0);
    result.appendFormat("  number of entries: %zu (%.2fMB / %.2fMB)\n", mBuffer.frameCount(),
                        float(mBuffer.used()) / float(1_MB), float(mBuffer.size()) / float(1_MB));
}

} // namespace android
+28 −6
Original line number Diff line number Diff line
@@ -22,32 +22,54 @@

#include <memory>
#include <mutex>
#include <queue>

using namespace android::surfaceflinger;

namespace android {

constexpr auto operator""_MB(unsigned long long const num) {
    return num * 1024 * 1024;
}

/*
 * SurfaceTracing records layer states during surface flinging.
 */
class SurfaceTracing {
public:
    void enable();
    void enable() { enable(kDefaultBufferCapInByte); }
    void enable(size_t bufferSizeInByte);
    status_t disable();
    bool isEnabled() const;

    void traceLayers(const char* where, LayersProto);

    bool isEnabled() const;
    void dump(String8& result) const;

private:
    static constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/layers_trace.pb";
    static constexpr auto kDefaultBufferCapInByte = 100_MB;
    static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";

    class LayersTraceBuffer { // ring buffer
    public:
        size_t size() const { return mSizeInBytes; }
        size_t used() const { return mUsedInBytes; }
        size_t frameCount() const { return mStorage.size(); }

        void reset(size_t newSize);
        void emplace(LayersTraceProto&& proto);
        void flush(LayersTraceFileProto* fileProto);

    private:
        size_t mUsedInBytes = 0U;
        size_t mSizeInBytes = 0U;
        std::queue<LayersTraceProto> mStorage;
    };

    status_t writeProtoFileLocked();

    bool mEnabled = false;
    std::string mOutputFileName = DEFAULT_FILENAME;
    mutable std::mutex mTraceMutex;
    std::unique_ptr<LayersTraceFileProto> mTrace;
    LayersTraceBuffer mBuffer;
};

} // namespace android