在今天这样干什么都离不开手机的时代里,手机的待机时间太重要了。特别是对于我这个不喜欢带充电宝出门的人来说,一旦看到手机电量低于20%,立刻就精神紧张了,因为一切信息都在手机里,如果手机没电,那么就失联了。
我一直保存着一个小的诺基亚手机,我使用它多年,如今想来,它的最大好处是“一周只需充一次电”。
对于今天我用的手机,必须每个晚上给它充电。即使这样,如果第二天用的比较多,还可能陷入缺电危机。
是什么让手机变得如此耗电呢?我和计算机打交道20多年了,当然很清楚这个问题的答案。
硬件角度看,屏幕和处理器(CPU、GPU等)最费电。而处理器到底费电多少,就要看软件了。
手机厂商当然也知道这些道理,所以他们也一直在想办法。比如,打开我手机里的设置程序,切换到电池功能,它就会显示出一个很不错的耗电排行榜。
在上面这个耗电排行榜上,硬件消耗25%的电量,展开后看到都是屏幕消耗的。
硬件的列表里没有处理器,因为处理器耗的电就是软件消耗的,这样分类很合理。
上图中的“软件(75%)”, 代表软件消耗了75%的电量,是硬件的3倍。
而对于软件消耗的75%电量,百分百都是一个程序消耗的,它就是“微信”。这里的百分百应该是做了取整处理的。
对于“电池”程序给出的这个排行榜,我是不惊讶的,因为我的手机此前多次提示我“微信是高耗电应用”。
那么,微信为何如此耗电呢?
有一天深夜,我在一大桂花树下,与一群人讨论这个话题。一白衣人说是因为微信的功能很多。
我说:功能很多就应该如此费电么?Office的功能也很多啊,但是Office并不很费电。内核的功能更多呢,但内核大多时候默默无闻,静静等待为应用程序服务。
白衣人又说:是因为微信总要检查是否有新的消息来。
我说:很多软件都能自动检查新消息啊,比如邮件客户端,比如微博,比如知乎。
这时,我突然想到千牛, 于是补充说:千牛还24小时帮着看店呢,也没有像微信这样上耗电排行榜的第一名啊?
听我说到千牛,白衣人有些急了,加快语速:因为你总用微信,微信总要开着啊,全天运行。
我说:全天运行的软件多了,千牛不是全天运行么?还有大多数后台服务也都是全天运行的,也并不费电啊。关键是我不用微信时,它也很费电。
白衣人更急了,脸色通红地说:你没有做过微信,你不知道微信有多复杂,微信要支持的平台太多,各种版本的Windows,各种版本的iOS。
我说:这样就应该如此费电么?如果这样,那么gdb应该最费电,因为gdb不只支持无数种操作系统(OS),还支持数不清的古怪处理器(CPU)。
这时白衣人面色难看,一时无话可说了,他身边一穿格子衫者说:因为微信的很多代码是用Java写的,Java虚拟机的很多东西, 上层软件控制不了的。
我听后,觉得有些道理,但很快又反驳说:那你们为啥不把Java代码改成C或者C++呢?苹果不一直坚持用Object C么?
[编者按: 事实上,根据一位使用苹果手机的格友所提供的截图(下图),微信在苹果手机上的耗电情况似乎要好一些,至少在耗电排行榜上不是头名.]
这时白衣人怒了,从桌子上拿起一只茶杯向我投了过来。我赶紧闪身,杯子落在身后的石凳上,击得粉碎。
正在这形势危急之时,树下一端坐者举起手,示意他要说话,但他似乎闭着双眼,双唇只开了窄窄的一道缝,从那道缝里,似乎有气流涌出。但我根本听不见他在说什么,好在他一说话,其他人立刻都闭嘴,恭恭敬敬的看着他。这时端坐者仍双目低垂,面无表情,仅仅从双唇的那条缝里向外吐气,“f,f,f,f, f, f, ......”仿佛像小孩子学拼音那样,一个音一个音的向外发。
渐渐地,我似乎听出了他在发的音,我抬头看天,月朗星稀。环顾四周,山峦起伏, 万籁俱寂。眼前的桂花树也安安静静,每一片树叶都纹丝不动,空气中只有桂花的香气在缓慢地流动。根本没有一丝风啊?
但此时,端坐者身边的两个穿西装者站了起来,我也转过了神,感觉不妙,赶紧站起身,大喊一声:“甚矣哉!老夫...”
此时有人拉我的手,轻轻拍我的胸膛,并且对我说:“calm,calm,calm,在哪里当老夫呢?”,妻把我从梦中叫醒。
俗话说,日有所思,夜有所梦。在很多个白天里,特别是手机电量不足时,我都会产生一个想法:“为什么微信如此费电?”
与其它app不想用就卸载不同,微信太重要了,即使费电,也还必须得开着它。
很多次想起这个问题时,我心里都有一个愿望。那就是上调试器看一下,看它为何如此费电。
这个愿望不是那么好实现,因为微信运行在手机上,手机是个封闭的系统,进入这个系统很难,在里面安装调试工具更难。
有人说,不是可以使用adb(Android Debug Bridge)连接么? 某些手机是可以,但是连上了之后,还有工具链的问题, 手机里一般不会预装gdb, 产品化阶段一般也不会有gdbserver, strace, kernelshark这样的工具就更不会有了。如果从普通Linux系统复制,那么多半无法运行, 因为安卓系统里使用的是bionic, 不是glibc。 于是乎, 就需要使用ndk来构建手机所需的工具,而且还需要构建版本和手机版本一致。
今年8月,格蠹在幽兰代码本上打通了Waydroid环境,让微信可以在幽兰上运行了。看着微信的图标出现在幽兰的任务栏上,我非常开心,忍不住对它说:“Welcome onboard!”
很多兰友也都喜欢这个功能, 因为有了这个功能后, 就可以运行三个微信实例: 在普通电脑上运行Windows版本, 在手机上运行手机版本, 在幽兰代码本上运行平板(tablet)版本。
这样登录后, 手机的微信上会显示"2个设备已登录微信"。点一下,便列出已登录设备。
幽兰上的微信是平板版本,所以屏幕更大,更合适看照片和视频。但对于我来说, 在幽兰上运行微信的最大的好处是提供了调试的便利。
Waydroid是基于容器技术的,使用的是LXC,不是docker,但能让安卓app在幽兰代码本上以容器方式运行就已经足够了。
与虚拟机这样的深度隔离环境不同,容器技术的好处是弱隔离。举例来说,如果在Linux上装个安卓虚拟机,在虚拟机里安装微信,那么在Linux系统中是看不到微信进程的。另一方面,当在Linux系统中运行容器,在容器里运行微信。那么在Linux系统中也是可以看到容器中的所有进程,而且可以用gdb附加上去的(需要sudo)。
比如在幽兰上启动微信后,再执行ps aux | grep tencent就可以看到微信的各个进程。
geduer@ulan:/$ ps aux | grep tencent
geduer 2608 0.0 0.1 353972 28972 ? Sl 10:52 0:00 /usr/bin/python3 /usr/bin/waydroid app launch com.tencent.mm
10140 139020 8.9 4.8 128958040 794620 ? t<l 16:16 13:38 com.tencent.mm
10140 152643 0.3 3.3 339094948 548288 ? S<l 16:46 0:24 com.tencent.mm:appbrand0
10140 152657 0.2 2.4 110599668 406744 ? Sl 16:46 0:17 com.tencent.mm:appbrand1
10140 158875 4.4 2.1 23495372 356752 ? S<l 17:01 4:47 com.tencent.mm:xweb_privileged_process_0
99002 158891 3.7 2.3 234261784 382704 ? S<l 17:01 3:56 com.tencent.mm:xweb_sandboxed_process_0:com.tencent.xweb.pinus.sdk.process.SandboxedProcessService
10140 164644 0.2 1.7 7191804 291696 ? Sl 17:17 0:12 com.tencent.mm:push
geduer 196771 0.0 0.0 3056 1228 pts/4 S+ 18:48 0:00 grep --color=auto tencent
是的,com.tencent.mm就是微信程序,它有名字类似的进程:
com.tencent.mm
com.tencent.mm:appbrand0
com.tencent.mm:appbrand1
com.tencent.mm:xweb_privileged_process_0
om.tencent.mm:xweb_sandboxed_process_0:com.tencent.xweb.pinus.sdk.process.SandboxedProcessService
com.tencent.mm:push
执行top,也可以看到微信的各个进程。下图中排在最上面的和最下面两个都是。
从上图看,微信进程在内存中使用了122.9G的虚拟内存,764M物理内存,产生了1600万次页错误。
从微信能在幽兰上运行的第一天起,我就想上gdb调试它,看看它耗电多的原因。但是一直没有抽出时间。昨天有兰友提出想用幽兰来深入分析安卓系统,他的想法又激发了我调试微信的念头。
于是,今天终于下手做这件事了。
微信有多个进程,选哪个呢?当然就选占用CPU净时间最长的,也就是上图中排在最上面的,名叫com.tencent.mm,进程ID为139020。
打开一个终端窗口,轻敲键盘,发出如下命令,唤出gdb:
gdb --pid 139020
命令发出后