MapleStory

Statsd In android 9 (1)

android系统中有很多不同功能的日志,如dumpsys dumpstate。anr以及crash时候也有单点的日志。然而,
一些系统的问题,如性能功耗以及稳定性问题是由于不明确的单点的缺陷或者故障扩散导致的。仅靠最后问题发生
时的日志有时难以定位问题,还有一些问题,例如黑屏,原因有很多种,所以能否将可能相关的事件汇聚到一处,
做数据分析也会方便一些。大概基于这种目的,android p版本中新增一种日志上报方式,接口位于:

1
android.util.StatsLog

新增一个用于处理统计数据的常驻进程 statsd
statsd 与logd一样使用sock收集日志
所有日志上报函数均直接使用入参写入sock,没有中间数据接口
statsd的收集在java以及native均有涉及,包含pull 以及 push两个模式

下面部分代码引用自 aosp android-9.0.0_r22

StatsLog接口类

StatsLog类继承于StatsLogInternal,而StatsLogInternal是在编译期由protobuf自动生成的,主要
是一些常量以及数据结构的定义。
StatsLog类中主要有3个接口,是通过binder调用到服务端:

1
2
3
public static boolean logStart(int label) //开始记录过程类事件
public static boolean logStop(int label) //停止记录过程类事件
public static boolean logEvent(int label) //记录结果类事件

还有从Internal类里继承而来的native方法,framework中上报的方法使用的是write,以及 write_non_chained

1
2
3
public static native int write(int code);
... ...
public static native int write_non_chained(int code, int arg1, java.lang.String arg2, int arg3);

其他都是同名的重载函数,
最终调用的函数位于:

out/soong/.intermediates/frameworks/base/tools/stats_log_api_gen/statslog.h/gen/statslog.h
system/core/libstats/include/stats_event_list.h
stats_event_list.write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try_stats_write(int32_t code, char const* arg1, int64_t arg2)
{
if (kStatsdEnabled) {
stats_event_list event(kStatsEventTag);
event << android::elapsedRealtimeNano();
event << code;
if (arg1 == NULL) {
arg1 = "";
}
event << arg1;
event << arg2;
return event.write(LOG_ID_STATS);
} else {
return 1;
}
}

stats_event_list创建一个以事件为id的logger,并把信息写入,缓存,最后使用write_to_logger,
将android_log_context写入logger。

1
2
3
4
5
6
7
8
9
10
11
12
int write(log_id_t id = LOG_ID_EVENTS) {
// facilitate -EBUSY retry
if ((ret == -EBUSY) || (ret > 0)) {
ret = 0;
}
int retval = write_to_logger(ctx, id);
// existing errors trump transmission errors
if (!ret) {
ret = retval;
}
return ret;
}

一开始以为通过binder调用写入事件,这里看起来为了提高性能使用的还是native的方法, 并且通过直接用函数
入参的方式来减少结构封包解包的性能开销。由于没有封包的过程,造成了参数不同的重载函数很多,这里使用
自动生成的方式生成了很多函数。所以这几个类都在 out/soong/.intermediates/里。

systlem/core/libstats/stats_event_list.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int write_to_logger(android_log_context ctx, log_id_t id) {
int retValue = 0;
if (WRITE_TO_LOGD) {
retValue = android_log_write_list(ctx, id);
}
if (WRITE_TO_STATSD) {
// log_event_list's cast operator is overloaded.
int ret = stats_write_list(ctx);
// In debugging phase, we may write to both logd and statsd. Prefer to
// return statsd socket write error code here.
if (ret < 0) {
retValue = ret;
}
}
return retValue;
}

最后可以选择是直接将事件输出到 logd中还是输出到statsd中。当然,无论是logd或是statsd都是通过
sock写入的。

statsd服务

statsd 服务分为两个部分,一部分是java的服务代理,用于在java侧提供接口,并注册一些服务监听。
如应用安装更新等。

StatsCompanionService.java

1
2
3
traceBeginAndSlog("StartStatsCompanionService");
mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
traceEnd();

另一部分运行在native的statsd中。

frameworks/base/cmds/statsd/src/main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Set up the binder
sp<ProcessState> ps(ProcessState::self());
ps->setThreadPoolMaxThreadCount(9);
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->disableBackgroundScheduling(true);
// Create the service
sp<StatsService> service = new StatsService(looper);
if (defaultServiceManager()->addService(String16("stats"), service) != 0) {
ALOGE("Failed to add service");
return -1;
}
service->sayHiToStatsCompanion();
service->Startup();
sp<StatsSocketListener> socketListener = new StatsSocketListener(service);
if (kUseLogd) {
ALOGI("using logd");
// Start the log reader thread
status_t err = start_log_reader_thread(service);
if (err != NO_ERROR) {
return 1;
}
}
if (kUseStatsdSocket) {
ALOGI("using statsd socket");
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (socketListener->startListener(600)) {
exit(1);
}
}
// Loop forever -- the reports run on this thread in a handler, and the
// binder calls remain responsive in their pool of one thread.
while (true) {
looper->pollAll(-1);
}

Comments