Winscope中抓取Transaction相关的原理剖析

背景:

在使用Winscope过程经常会有如下的一些选项供我们进行选择,不同的选项最后就会展示不同的数据项出来。

例如上面勾选了SurfaceFlinger和Transaction后,抓取后就显示了这两个的内容:

但是大家是否有想过,为啥这里一勾选就可以抓取到对应的数据?具体这些数据是哪里来的呢?原理又是什么呢?
这里就拿Transaction这个选项作为案例给大家剖析一下Winscope可以抓取和展示数据的原理

Transaction勾选后抓取命令部分

因为Winscope网页运行抓取,其实主要是靠执行如下的python文件才可以正常运行

python3 $ANDROID_BUILD_TOP/development/tools/winscope/src/adb/winscope_proxy.py

如果没有运行上面的winscope_proxy.py程序,那么整个抓取就无法进行,会显示如下画面。在这里插入图片描述
在这里插入图片描述
也就是考就是靠这个winscope_proxy.py程序去手机上抓取相关的数据。
那么选着Transaction选项后抓取Winscope,看看winscope_proxy.py程序的相关输出,看看有没有线索可以查到是如何获取系统的相关Transaction数据。
抓取过程中,发现有如下打印:

# Do not print anything to stdout/stderr in the handler
function stop_trace() {
  echo "start" >/data/local/tmp/winscope_signal_handler.log

  # redirect stdout/stderr to log file
  exec 1>>/data/local/tmp/winscope_signal_handler.log
  exec 2>>/data/local/tmp/winscope_signal_handler.log

  set -x
  trap - EXIT HUP INT
  su root service call SurfaceFlinger 1041 i32 0 >/dev/null 2>&1
  echo "TRACE_OK" > /data/local/tmp/winscope_status
}

trap stop_trace EXIT HUP INT
echo "Signal handler registered."

su root service call SurfaceFlinger 1041 i32 1
echo "SF transactions recording started."

2024-11-28 10:58:56,743 - ADBProxy - DEBUG - Call: adb -s emulator-5554 shell su root cat /data/local/tmp/winscope_status
2024-11-28 10:58:56,773 - ADBProxy - DEBUG - Call: adb -s emulator-5554 shell su root rm /data/local/tmp/winscope_status
2024-11-28 10:58:56,787 - ADBProxy - DEBUG - Trace finished successfully on emulator-5554
2024-11-28 10:58:56,787 - ADBProxy - DEBUG - Call: adb -s emulator-5554 shell su root cat /data/local/tmp/winscope_signal_handler.log
2024-11-28 10:58:56,799 - ADBProxy - INFO - POST /end/emulator-5554/ HTTP/1.1 HTTPStatus.OK -
2024-11-28 10:58:56,801 - ADBProxy - INFO - OPTIONS /fetch/emulator-5554/transactions/ HTTP/1.1 HTTPStatus.OK -
2024-11-28 10:58:56,802 - ADBProxy - DEBUG - Call: adb -s emulator-5554 shell su root find /data/misc/wmtrace/ -name transactions_trace.winscope
2024-11-28 10:58:56,814 - ADBProxy - DEBUG - Found file ['/data/misc/wmtrace/transactions_trace.winscope']
2024-11-28 10:58:56,815 - ADBProxy - DEBUG - Fetching file /data/misc/wmtrace/transactions_trace.winscope from device to /tmp/tmpf3_8g34b
2024-11-28 10:58:56,833 - ADBProxy - DEBUG - Deleting file /data/misc/wmtrace/transactions_trace.winscope from device
2024-11-28 10:58:56,833 - ADBProxy - DEBUG - Call: adb -s emulator-5554 shell su root rm /data/misc/wmtrace/transactions_trace.winscope
2024-11-28 10:58:56,845 - ADBProxy - DEBUG - Uploading file /tmp/tmpf3_8g34b
2024-11-28 10:58:56,847 - ADBProxy - INFO - GET /fetch/emulator-5554/transactions/ HTTP/1.1 HTTPStatus.OK -

日志中大概可以看得出刚开始抓取使用的是这个命令:

su root service call SurfaceFlinger 1041 i32 1

停止抓取使用是这个命令:

su root service call SurfaceFlinger 1041 i32 0 >/dev/null 2>&1

最后的Transaction相关的数据都保存在手机上然后pull到电脑

Fetching file /data/misc/wmtrace/transactions_trace.winscope from device to /tmp/tmpf3_8g34b

上面几个点就是整个winscope_proxy.py程序可以抓取到手机上的相关数据原理,简单总结如下:
在这里插入图片描述

剖析SurfaceFlinger端相关源码:

首先看看抓取命令部分
su root service call SurfaceFlinger 1041 i32 1 —开始抓
su root service call SurfaceFlinger 1041 i32 0 --结束抓
首先这个service call就是直接调用到SurfaceFlinger这个service的onTransact方法,1041代表是code,i32代表是data的int类型,1和0代表就是值

这个调用后会执行到如下代码:
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp


status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                    uint32_t flags) {
//省略
            case 1041: { // Transaction tracing
                if (mTransactionTracing) {
                    if (data.readInt32()) {
                        // Transaction tracing is always running but allow the user to temporarily
                        // increase the buffer when actively debugging.
                        mTransactionTracing->setBufferSize(
                                TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
                    } else {
                        mTransactionTracing->writeToFile();//停止抓取,把数据写入文件
                        mTransactionTracing->setBufferSize(
                                TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
                    }
                }
                reply->writeInt32(NO_ERROR);
                return NO_ERROR;
            }

//省略
}

这里结合注释可以看出Transaction tracing is always running,即其实一直在记录相关的Transaction,只不过是对应的buffer大小不一样

static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;

即抓取时候设置的buffer是100m,不抓取时候是512k。
停止抓取时候有一个writeToFile方法来写入到文件中
这里因为调用的 mTransactionTracing->writeToFile()说明没有传递参数,但是这个方法本身是要求参数的,这个参数就是对应文件名字。
frameworks/native/services/surfaceflinger/Tracing/TransactionTracing.cpp


status_t TransactionTracing::writeToFile(std::string filename) {
    std::scoped_lock lock(mTraceLock);
    proto::TransactionTraceFile fileProto = createTraceFileProto();
    addStartingStateToProtoLocked(fileProto);
    return mBuffer.writeToFile(fileProto, filename);//把相关的buffer写入到文件
}

那么这里肯定就是有默认参数,看看对应头文件:
在这里插入图片描述
确实看到对应默认文件是/data/misc/wmtrace/transactions_trace.winscope

那么现在再看看收集数据来源是怎么收集的,即mBuffer数据来自哪里?

void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
                                  const std::vector<uint32_t>& destroyedLayers) {
   
        std::string serializedProto;
        entryProto.SerializeToString(&serializedProto);
        entryProto.Clear();
        //添加到mBuffer
        std::vector<std::string> entries = mBuffer.emplace(std::move(serializedProto));
     
}

那么这个addEntry哪里调用呢?
是有一个单独线程一直在loop数据

void TransactionTracing::loop() {
    while (true) {
     //省略部分
        mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
                return mDone || !mUpdates.empty();
            });
            if (mDone) {
                mUpdates.clear();
                mDestroyedLayers.clear();
                break;
            }

            destroyedLayers = std::move(mDestroyedLayers);
            mDestroyedLayers.clear();
            committedUpdates = std::move(mUpdates);//数据主要来自mUpdates
            mUpdates.clear();
        } // unlock mMainThreadLock
        if (!committedUpdates.empty() || !destroyedLayers.empty()) {
            addEntry(committedUpdates, destroyedLayers);
        }
    }
}

那么mUpdates又是哪里来的呢?搜索代码发现是tryPushToTracingThread方法插入数据

void TransactionTracing::tryPushToTracingThread() {
    ATRACE_CALL();
    // Try to acquire the lock from main thread.
    if (mMainThreadLock.try_lock()) {
        // We got the lock! Collect any pending transactions and continue.
        mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
                        std::make_move_iterator(mPendingUpdates.end()));
        mPendingUpdates.clear();
        mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
                                mPendingDestroyedLayers.end());
        mPendingDestroyedLayers.clear();
        mTransactionsAvailableCv.notify_one();
        mMainThreadLock.unlock();
    } else {
        ALOGV("Couldn't get lock");
    }
}

那么tryPushToTracingThread又是谁调用呢?这里有多个地方,主要是addCommittedTransactions

void TransactionTracing::addCommittedTransactions(
        int64_t vsyncId, nsecs_t commitTime, frontend::Update& newUpdate,
        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
        bool displayInfoChanged) {
//省略
    mPendingUpdates.emplace_back(update);
    tryPushToTracingThread();
}

这里的addCommittedTransactions方法就是在SurfaceFlinger的commit方法

bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
 
        if (flushTransactions) {
            updates = flushLifecycleUpdates();
            if (mTransactionTracing) {
                mTransactionTracing->addCommittedTransactions(vsyncId.value, frameTime.ns(),
                                                              updates, mFrontEndDisplayInfos,
                                                              mFrontEndDisplayInfosChanged);
            }
        }

过程还是有点绕,本质就是就是所有传递到sf端的Transaction都会被最后记录到TransactionTracing的mBuffer中,然后转换成写入到/data/misc/wmtrace/transactions_trace.winscope文件
相关perfetto图展示如下:
在这里插入图片描述

更多framework技术干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值