前言
在 C++ 中,当一个对象被销毁的时候,会自动调用这个对象的析构函数,无需显示调用。利用这一特性,可以有很多的妙用,本文将介绍在 Android framework 中两个常用的例子。
systrace 中的函数运行时间
我们在看 systrace 的时候,经常能够看到这样的画面:
图中每个矩形的宽度代表的是这个函数运行的时间,那么这是这么做到的呢?
在源码中可以看到,图中 onMessageReceived()
,handleMessageRefresh()
等,这些函数的第一行,都调用了 ATRACE_CALL()
:
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
void SurfaceFlinger::handleMessageRefresh() {
ATRACE_CALL();
这个函数应该就是答案了,看一下实现:
// ATRACE_NAME traces from its location until the end of its enclosing scope.
#define _PASTE(x, y) x ## y
#define PASTE(x, y) _PASTE(x,y)
#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
namespace android {
class ScopedTrace {
public:
inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
atrace_begin(mTag, name);
}
inline ~ScopedTrace() {
atrace_end(mTag);
}
private:
uint64_t mTag;
};
} // namespace android
可以看到,当调用 ATRACE_CALL()
时,相当于定义了一个类型为 ScopedTrace
的变量,这个变量本身没有什么特别的,重要的是这个 ScopedTrace
这个类,其构造函数会去调用 atrace_begin()
,析构函数会去调用 atrace_end()
。也就是说,在函数开头,定义一个 ScopedTrace
类型的变量,经由构造函数去调用 atrace_begin()
;然后等函数结束,前面定义的 ScopedTrace
会被销毁,会经由析构函数调用 atrace_end()
,从而达到了统计一个函数的运行时间的效果。
这里稍微补充一下,在 Android 里,总能听到各种各样的 trace:如 atrace, ftrace, systrace 等,那么这些 trace 究竟有什么区别呢?
- ftrace
ftrace 是 Linux Kernel 自带的调试框架,它在内核态工作,用户可以通过 debugfs 接口来控制和使用 ftrace。目前 debugfs 一般挂载在/sys/kernel/debug/tracing/
。 - atrace
这个是 Android 针对 ftrace 的一个封装,它将 ftrace 的各种 Event 抽象为 tag,一个 atrace tag 对应多个 ftrace Event,例如sched
这个 tag 分别包括了sched/sched_switch
,sched/sched_wakeup
,sched/sched_waking
,sched/sched_blocked_reason
,sched/sched_cpu_hotplug
等这些 Event。 - systrace
systrace 其实是 Android SDK 里的一个工具,是对 atrace 的主机端封装,利用 atrace 来使能 ftrace,然后读取 ftrace 的缓冲区并将其全部封装到一个独立的 HTML 文件。
具体的可以看一下内核工匠的这篇文章,写得很详细。
而我们在 framework 中,Java 代码常用的 Trace.traceBegin()
和 Trace.traceEnd()
;C++ 有 atrace_begin()
和 atrace_end()
以及一系列 ATRACE_
打头的宏,其实都是利用 atrace 来实现 trace 的抓取。
Mutex 锁自动释放
在 framework 还有另外一处也有类似的用法,那就是锁。framework 经常能够看到类似下面的代码:
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
bool secure)
{
sp<BBinder> token = new DisplayToken(this);
// Mutex 是在哪里释放的呢?
Mutex::Autolock _l(mStateLock);
// Display ID is assigned when virtual display is allocated by HWC.
DisplayDeviceState state;
state.isSecure = secure;
state.displayName = displayName;
mCurrentState.displays.add(token, state);
mInterceptor->saveDisplayCreation(state);
return token;
}
这个 Mutex 是在哪里释放的呢?原理其实跟前面的 ATRACE_CALL()
是一样的,在析构函数里面去释放锁,就不赘述了。
Author: simowce
Permalink: https://blog.simowce.com/destrcutor/
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。