Android statsd 埋点简析

源码基于:Android U

0. 前言

最近在研究 Android 自带的系统数据指标采集功能,框架依旧很严谨、完美,这里做个分享。

1. Android S 之后变化

stats 的代码从 framework 或 system/core 中转移到了 packages/modules/StatsD 目录中。

2. 框架图

大的框架分两层:

  • system_sever;

  • statsd;

Java 层创建两个 SystemService:

  • StatsCompanion.Lifecycle:会启动两个Service 用以与 native daemon 通信;

  • StatsPullAtomService:SystemServer 中用来采集数据;

StatsPullAtomService 用以采集数据,并将数据填充到参数中,在StatsManager.PullAtomCallbackInternal 中会转换成 Parcel 格式传入到 native。

frameworks/base/services/core/java/com/android/server/stats/pull/StatsPullAtomService.javaprivate class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {@Overridepublic int onPullAtom(int atomTag, List<StatsEvent> data) {...try {switch (atomTag) {...case FrameworkStatsLog.PROCESS_MEMORY_STATE:case FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK:case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT:case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE:case FrameworkStatsLog.ION_HEAP_SIZE:case FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE:case FrameworkStatsLog.PROCESS_DMABUF_MEMORY:case FrameworkStatsLog.SYSTEM_MEMORY:case FrameworkStatsLog.VMSTAT:...}} finally {}}

该函数是针对不同类型的 puller 的回调处理,在此之前会调用 registerPullers 函数将所有的puller 注册到 StatsManager 中:

    private void registerProcessMemoryHighWaterMark() {int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;mStatsManager.setPullAtomCallback(tagId,null, // use default PullAtomMetadata valuesDIRECT_EXECUTOR,mStatsCallbackImpl);}

StatsCallbackPuller::PullInternal 中会回调StatsManager.PullAtomCallbackInternal 的onPullAtom,该函数中会回调StatsCallbackPuller::PullInternal 中定义的PullResultReceiverpullFinished函数,并将 StatsPullAtomService 端采集的数据存入StatsCallbackPuller::PullInternal的入参中;

packages/modules/StatsD/framework/java/android/app/StatsManager.javapublic void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {final long token = Binder.clearCallingIdentity();try {mExecutor.execute(() -> {List<StatsEvent> data = new ArrayList<>(); //上层的采集数据存入dataint successInt = mCallback.onPullAtom(atomTag, data); //callback,开始采集boolean success = successInt == PULL_SUCCESS;StatsEventParcel[] parcels = new StatsEventParcel[data.size()];for (int i = 0; i < data.size(); i++) {parcels[i] = new StatsEventParcel();parcels[i].buffer = data.get(i).getBytes(); //转换成parcel,传入native}try {resultReceiver.pullFinished(atomTag, success, parcels); //receiver回调} catch (RemoteException e) {...}});} finally {Binder.restoreCallingIdentity(token);}}
packages/modules/StatsD/statsd/src/external/StatsCallbackPuller.cppPullErrorCode StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {...shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =make_shared<vector<shared_ptr<LogEvent>>>();shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>([cv_mutex, cv, pullFinish, pullSuccess, sharedData]( //receiver回调int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {{lock_guard<mutex> lk(*cv_mutex);for (const StatsEventParcel& parcel: output) {shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),parcel.buffer.size());if (valid) {sharedData->push_back(event); //解析上层采集数据,存入sharedData} else {StatsdStats::getInstance().noteAtomError(event->GetTagId(),/*pull=*/true);}}*pullSuccess = success;*pullFinish = true;}cv->notify_one();});// Initiate the pull. This is a oneway call to a different process, except// in unit tests. In process calls are not oneway.Status status = mCallback->onPullAtom(mTagId, resultReceiver); //callback,等待receiver回调...{unique_lock<mutex> unique_lk(*cv_mutex);// Wait until the pull finishes, or until the pull timeout.cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs),[pullFinish] { return *pullFinish; });if (!*pullFinish) {...} else {if (*pullSuccess) {*data = std::move(*sharedData); //数据最终填充到入参data中}...}
}

StatsPullerManager::OnAlarmFired 最终会调用 StatsCallbackPuller::PullInternal,参数也是这里传入的。所以采集的数据最终会回到 StatsPullerManager::OnAlarmFired 中,最终会将这些数据通过注册在StatsPullerManager中的 mReceivers 的回调函数 onDataPulled处理。

packages/modules/StatsD/statsd/src/external/StatsPullerManager.cppvoid StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {std::lock_guard<std::mutex> _l(mLock);int64_t wallClockNs = getWallClockNs();int64_t minNextPullTimeNs = NO_ALARM_UPDATE;vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;for (auto& pair : mReceivers) { //最终数据的处理都是在mReceivers中vector<ReceiverInfo*> receivers;if (pair.second.size() != 0) {for (ReceiverInfo& receiverInfo : pair.second) {sp<PullDataReceiver> receiverPtr = receiverInfo.receiver.promote();const bool pullNecessary = receiverPtr != nullptr && receiverPtr->isPullNeeded();if (receiverInfo.nextPullTimeNs <= elapsedTimeNs && pullNecessary) {receivers.push_back(&receiverInfo);} else {if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {receiverPtr->onDataPulled({}, PullResult::PULL_NOT_NEEDED, elapsedTimeNs);int numBucketsAhead = (elapsedTimeNs - receiverInfo.nextPullTimeNs) /receiverInfo.intervalNs;receiverInfo.nextPullTimeNs +=(numBucketsAhead + 1) * receiverInfo.intervalNs;}minNextPullTimeNs = min(receiverInfo.nextPullTimeNs, minNextPullTimeNs);}}if (receivers.size() > 0) {needToPull.push_back(make_pair(&pair.first, receivers));}}}for (const auto& pullInfo : needToPull) {vector<shared_ptr<LogEvent>> data; //采集的数据根本是存在这里的临时变量PullResult pullResult =PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, elapsedTimeNs, &data)  //调用PullLocked 将数据采集回来? PullResult::PULL_RESULT_SUCCESS: PullResult::PULL_RESULT_FAIL;if (pullResult == PullResult::PULL_RESULT_FAIL) {VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);}// Convention is to mark pull atom timestamp at request time.// If we pull at t0, puller starts at t1, finishes at t2, and send back// at t3, we mark t0 as its timestamp, which should correspond to its// triggering event, such as condition change at t0.// Here the triggering event is alarm fired from AlarmManager.// In ValueMetricProducer and GaugeMetricProducer we do same thing// when pull on condition change, etc.for (auto& event : data) {event->setElapsedTimestampNs(elapsedTimeNs);event->setLogdWallClockTimestampNs(wallClockNs);}for (const auto& receiverInfo : pullInfo.second) {sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();if (receiverPtr != nullptr) {receiverPtr->onDataPulled(data, pullResult, elapsedTimeNs); //发送给receiver处理// We may have just come out of a coma, compute next pull time.int numBucketsAhead =(elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;minNextPullTimeNs = min(receiverInfo->nextPullTimeNs, minNextPullTimeNs);} else {VLOG("receiver already gone.");}}}VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs,(long long)minNextPullTimeNs);mNextPullTimeNs = minNextPullTimeNs; //alarm时间点更新updateAlarmLocked();  //调用updateAlarmLocked 告知Java service 中的AlarmManagerService
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/50351.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IEC104转MQTT网关轻松将IEC104设备数据传输到Zabbix、阿里云、华为云、亚马逊AWS、ThingsBoard、Ignition云平台

随着工业4.0的深入发展和物联网技术的广泛应用&#xff0c;IEC 104&#xff08;IEC 60870-5-104&#xff09;作为电力系统中的重要通信协议&#xff0c;正逐步与各种现代监控、管理和云平台实现深度融合。IEC104转MQTT网关BE113作为这一融合过程中的关键设备&#xff0c;其能够…

动手学深度学习V2每日笔记(模型选择+过拟合和欠拟合)

本文主要参考沐神的视频教程 https://www.bilibili.com/video/BV1K64y1Q7wu/?spm_id_from333.788.recommend_more_video.0&vd_sourcec7bfc6ce0ea0cbe43aa288ba2713e56d 文档教程 https://zh-v2.d2l.ai/ 本文的主要内容对沐神提供的代码中个人不太理解的内容进行笔记记录&a…

通过IEC104转MQTT网关轻松接入阿里云平台

随着智能电网和物联网技术的飞速发展&#xff0c;电力系统中的传统IEC 104协议设备正面临向现代化、智能化转型的迫切需求。阿里云作为全球领先的云计算服务提供商&#xff0c;其强大的物联网平台为IEC 104设备的接入与数据处理提供了强大的支持。本文将深入探讨钡铼网关在MQTT…

UML通信图建模技术及应用例

新书速览|《UML 2.5基础、建模与设计实践》 在对系统的动态行为进行建模时&#xff0c;通信图常被用于按组织结构对控制流进行建模。与顺序图一样&#xff0c;一个单独的通信图只能显示一个控制流。 使用通信图建模时可以遵循如下策略&#xff1a; &#xff08;1&#xff09…

WinDbg用户模式调试基础

WinDbg用户模式调试基础 在前面的文章中&#xff0c;介绍了如何使用WinDbg分析蓝屏原因https://www.cnblogs.com/zhaotianff/p/15150244.html 不过那会都是在网上找的资料&#xff0c;东拼西凑出来&#xff0c;并没有系统的去学习WinDbg。 最近在学习内核开发这一块的内容&…

顺序表的实现和操作

目录 一.前言 二. 顺序表的优缺点 三. 顺序表的定义和初始化 四.顺序表的相关操作 一.前言 首先介绍下线性表的定义&#xff0c;线性表是具有相同特性的数据元素的一个有限序列。而我们的顺序表就是线性表的一种&#xff0c;是线性表的顺序存储结构。所谓顺序存储就是把逻辑…

Photos框架 - 自定义媒体选择器(UI列表)

引言Photos框架 - 自定义媒体资源选择器&#xff08;数据部分&#xff09;-CSDN博客 关于自定义媒体选择器上一篇博客我们已经介绍了使用Photos获取媒体资源数据和处理媒体资源数据&#xff0c;有了数据&#xff0c;UI的实现就比较灵活了&#xff0c;我就以上面的设计样式为例…

LabVIEW操作系列1

系列文章目录 我的记录&#xff1a; LabVIEW操作系列 文章目录 系列文章目录前言五、特殊用法5.1 取值范围表示5.2 对输入值取值范围进行限定5.3 控制多个While循环停止运行。5.4 获取按钮上的文本5.5 获取按钮上的文本【进阶】 六、使用步骤1.引入库2.读入数据 七、其余功能7.…

【Python selenium过极验五子棋】自动化过五子棋人机验证,享受丝滑的落子,秒了

文章日期&#xff1a;2024.07.25 使用工具&#xff1a;Python 文章类型&#xff1a;自动化过极验五子棋 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法…

Spring Boot(八十二):SpringBoot通过rsa实现API加密

项目中使用RSA加密方式对API接口返回的数据加密,让API数据更加安全。别人无法对提供的数据进行破解。Spring Boot接口加密,可以对返回值、参数值通过注解的方式自动加解密 。 下面开始代码演示 1 接口加密 1.1 新建一个springboot项目 1.2 添加依赖 <dependency>&l…

如何做校园圈子小程序,需要哪些功能?可打包APP小程序H5,源码交付,支持二开!

独立学校首页 支持每个学校独立首页!每个学校都可以拥有专属首页&#xff0c;打造不同风格的学校首页展示效果 多业务覆盖 可实现校园内外卖、跑腿、超市、药店水果、快餐店等业务全覆盖!所有配送业务平台都可开展 多站点运营 支持多学校多站点运营&#xff0c;各分站管理员可独…

【Docker】CentOS7环境下的安装

环境展示 安装 配置仓库 sudo yum install -y yum-utils # docker官方key文件下载 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 建议使用阿里云key文件下载 sudo yum-config-manager --add-repo https://mirrors.aliyun.…

Python小工具——监听某网站的数据变化并进行邮件通知

目录 一、需求描述 二、解析 三、实例代码 一、需求描述 监听自考网2024年广东省6月份的毕业生学历注册进度&#xff0c;这是网址&#xff1a;https://www.chsi.com.cn/xlcx/count_zk.jsp&#xff0c; 如上图所示&#xff0c;我们想知道这个红色的空格啥时候被填满&#xf…

【yolov8】|小目标优化|:增加CA机制 运行成功

🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀 你好,我是@努力的小巴掌 之前用baseline跑了yolov8。 为了提升性能,我们需要对yolov8进行优化。 本次的优化,我们从增加注意力机制开始…

无人机组装与操作实训课程详解

一、课程名称与目标 课程名称&#xff1a;无人机组装与操作实训课程 课程目标&#xff1a;本课程旨在培养学员对无人机组装技术的深入理解和实际操作能力&#xff0c;使学员能够独立完成无人机的组装、调试和日常维护工作&#xff0c;并具备一定的无人机操作能力和安全意识。…

Web开发:使用数据库工具Navicat技巧合集

1.EXCEL批量导入数据 打开Navicat准备导入数据&#xff0c;点击导入 选择excel 字段名行应该写0&#xff08;下图错误&#xff09; 下一步&#xff0c;表已经用语法建好了&#xff0c;因此不用打勾 配置好字段&#xff0c;下一步&#xff0c;点击开始即可。 2.数据表从一个服…

【HarmonyOS】HarmonyOS NEXT学习日记:七、页面与组件的生命周期

【HarmonyOS】HarmonyOS NEXT学习日记&#xff1a;七、页面与组件的生命周期 页面和组件 组件&#xff1a;用Component装饰的代码称为自定义组件页面&#xff1a;Entry装饰的组件即页面的根节点 组件生命周期 aboutToAppear&#xff1a;在创建自定义组件的新实例后&#xf…

WPF---Prism视图传参

Prism视图传参方式。 实际应用场景 点击tabitem中的列表数据&#xff0c;同步更新到ListStatic Region对应的界面。目前用两种方式实现了传参数据同步。 第一&#xff0c;事件聚合器&#xff08;EventAggregator&#xff09; 1. 定义事件 创建一个事件类&#xff0c;用于传…

手持式气象检测设备:便携科技,气象探测

一、手持式气象检测设备&#xff1a;小巧身躯&#xff0c;大能量 手持式气象检测设备&#xff0c;顾名思义&#xff0c;是一种可以手持操作的气象监测工具。它集成了温度、湿度、气压、风速风向等多种传感器&#xff0c;能够实时获取气象数据&#xff0c;并通过显示屏或手机APP…

Leetcode—240. 搜索二维矩阵 II【中等】

2024每日刷题&#xff08;149&#xff09; Leetcode—240. 搜索二维矩阵 II 实现代码 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int r 0;int c matrix[0].size() - 1;while(r < matrix.size() &&…