背景:
在使用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技术干货,请关注下面“千里马学框架”