Logo



ftrace源码分析(fragment源码分析)

本文目录一览:

如何选择一个 Linux Tracer

tracer 是一个高级的性能分析和诊断工具,但是不要让这名词唬住你,如果你使用过 strace 和tcpdump,其实你就已经使用过 tracer 了。系统 tracer 可以获取更多的系统调用和数据包。它们通常能跟踪任意的内核和应用程序。

有太多的 linux tracer 可以选择。每一种都有其官方的(或非官方的)的卡通的独角兽吉祥物,足够撑起一台"儿童剧"了。

那么我们应该使用哪个 tracer 呢?

我会为两类读者回答这个问题,大部分人和性能/内核工程师。过一段时间这些可能会发生变化,我会持续跟进并补充,大概会一年更新一次。

多数人

多数人 (开发者,系统管理员,开发管理者,运维人员,评测人员,等等) 不关心系统追踪器的细节。下面是对于追踪器你应该知道和做的:

1. 使用perf_events分析CPU性能

使用 perf_events 做 CPU 性能分析。性能指标可以使用flame graph 等工具做可视化。

git clone --depth 1

perf record -F 99 -a -g -- sleep 30

perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl perf.svg

Linux perf_events (又称 "perf",同命令名) 是 Linux 用户的官方追踪器和性能分析器。内置于内核代码,有很好维护(近来获得快速增强),通常通过 linux 命令行工具包添加。

perf 有很多功能,如果只能推荐一个,我选择 CPU 性能分析。尽管这只是采样,而不是从技术上追踪事件。最难的部分是获取完整的栈和信息,我为 java 和 node.js 做的一个演讲 Linux Profiling at Netflix中已经说过这个问题

2.了解其他的Tracer

正如我一个朋友说的:“你不需要知道如何操作 X 射线机器,但是一旦你吞了一枚硬币,你得知道这得去做 X 射线”,你应该了解各种 tracer 都能做什么,这样就能在你工作中真正需要 tracer 的时候,你既可以选择稍后学习使用,也可以雇相应的人来完成。

简短来说:几乎所有的东西都可以使用 tracer 来进行分析和跟踪。如,文件系统,网络处理器,硬件驱动器,全部的应用程序。可以看一下我的个人网站上关于 ftrace的文章,还有我写的关于perf_events 文档介绍,可以做为一个追踪(或者性能分析)的例子。

3. 寻求前端支持工具

如果你正想买一个能支持跟踪 Linux 的性能分析工具(有许多卖这类工具的公司)。想像一下,只需要直接点击一下界面就能“洞察”整个系统内核,包括隐藏的不同堆栈位置的热图,我在Monitorama talk 中介绍了一个这样带图形界面的工具。

我开源了一些我自己开发的前端工具,尽管只是 CLI (命令行界面)而不是(图形界面)。这些工具也会让人们更加快速容易的使用 tracer。比如下面的例子,用我的 perf_tool,跟踪一个新进程:

# ./execsnoopTracing exec()s. Ctrl-C to end.

PID PPID ARGS

22898 22004 man ls

22905 22898 preconv -e UTF-8

22908 22898 pager -s

22907 22898 nroff -mandoc -rLL=164n -rLT=164n -Tutf8

在 Netflix 上,我们创建了一个 Vector,一个分析工具的实例同时也是 Linux 上的 tracer 的最终前端。

致性能或内核工程师

我们的工作变的越来越困难,很多的人会问我们怎么样去追踪,哪种路径可以用!为了正确理解一个路径,你经常需要花上至少100个小时才能做到。理解所有的 linux 路径去做出理性的决定是一个浩大的工程。(我可能是唯一一个接近做到这件事情的人)

这里是我的建议,可以二选其一:

A) 选中一个全能的路径,并且使它标准化,这将涉及花费大量的时间去弄清楚它在测试环境中的细微差别和安全性。我现在推荐 SystemTap 的最新版本(ie,从源代码构建)。我知道有些公司已经选用 LTTng,而且他们用的很好,尽管它不是非常的强大(虽然它更安全)。Sysdig 可以成为另一个候选如果它可以增加追踪点或者 kprobes。

B) 遵循我上面提供的流程图,它将意味着尽可能更多的使用 ftrace 或者 perf_event, eBPF 会得到整合,之后其他的路径像 SystemTap/LTTng 会去填补这个空白。这就是我目前在 Netflix 做的工作。

tracer 的评论:

1. ftrace

我喜欢用 ftrace,它是内核 hacker 的首选,内置于系统内核,可以使用跟踪点(静态检查点),能调用内核 kprobes 和 uprobes 调试工具。并且提供几个这样的功能:带可选过滤器和参数的事件追踪功能;在内核中进行统计的事件计数和定时功能;还有函数流程遍历的功能。可以看一下内核代码中 ftrace.txt 例子了解一下。ftrace 由 /sys 控制,仅支持单一的 root 用户使用(但是你可以通过缓冲区实例破解以支持多用户)。某些时候 Ftrace 的操作界面非常繁琐,但是的确非常“hack”,而且它有前端界面。Steven Rostedt,ftace 的主要作者,创建了 trace-cmd 命令工具,而我创建了 perf 的工具集。我对这个工具最大的不满就是它不可编程。举例来说,你不能保存和获取时间戳,不能计算延迟,不能把这些计算结果保存成直方图的形式。你需要转储事件至用户级别,并且花一些时间去处理结果。ftrace 可以通过 eBPF 变成可编程的。

2.perf_events

perf_events 是 Linux 用户的主要跟踪工具,它内置在内核源码中,通常通过 linux-tools-commom 加入。也称“perf”,同前端工具名称,通常用来跟踪和转储信息到一个叫做 perf.data 的文件中,perf.data 文件相当于一个动态的缓冲区,用来保存之后需要处理的结果。ftrace 能做到的,perf_events 大都也可以做到,perf-events 不能做函数流程遍历,少了一点儿“hack”劲儿(但是对于安全/错误检查有更好的支持)。它可以进行 CPU 分析和性能统计,用户级堆栈解析,也可以使用对于跟踪每行局部变量产生的调试信息。它也支持多用户并发操作。和 ftrace 一样也不支持可编程。如果要我只推荐一款 tracer,那一定是 perf 了。它能解决众多问题,并且它相对较安全。

3. eBPF

extended Berkeley Packet Filter(eBPF)是一个可以在事件上运行程序的高效内核虚拟机(JIT)。它可能最终会提供 ftrace 和 perf_events 的内核编程,并强化其他的 tracer。这是 Alexei Starovoitov 目前正在开发的,还没有完全集成,但是从4.1开始已经对一些优秀的工具有足够的内核支持了,如块设备I/O的延迟热图。可参考其主要作者 Alexei Starovoitov 的BPF slides和eBPF samples。

4. SystemTap

SystemTap 是最强大的tracer。它能做所有事情,如概要分析,跟踪点,探针,uprobes(来自SystemTap),USDT和内核编程等。它将程序编译为内核模块,然后加载,这是一种获取安全的巧妙做法。它也是从tree发展而来,在过去有很多问题(多的可怕)。很多不是 SystemTap 本身的错——它常常是第一个使用内核追踪功能,也是第一个碰到 bug 的。SystemTap 的最新版本好多了(必须由源代码编译),但是很多人仍然会被早期版本吓到。如果你想用它,可先在测试环境中使用,并与irc.freenode.net上 的 #systemtap 开发人员交流。(Netflix 有容错机制,我们已经使用了 SystemTap,但是可能我们考虑的安全方面的问题比你们少。)我最大的不满是,它似乎认为你有常常没有的内核 debug 信息。实际上没有它也能做很多事情,但是缺少文档和例子(我必须自己全靠自己开始学习)。

5. LTTng

LTTng 优化了事件采集,这比其他 tracers 做得好。它从 tree 发展而来,它的核心很简单:通过一组小规模的固定指令集将事件写入追踪缓冲区,这种方式使它安全、快速,缺点是它没有内核编码的简单途径。我一直听说这不是一个大问题,因为尽管需要后期处理,它也已经优化到可以充分的度量。此外,它还首创了一个不同的分析技术,更多对所有关注事件的黑盒记录将稍后以 GUI 的方式进行研究。我关心的是前期没有考虑到要录制的事件缺失问题如何解决,但我真正要做的是花更多时间来看它在实践中用的怎么样。这是我花的时间最少的一个 tracer(没有什么特殊原因)。

6. Ktap

ktap 在过去是一款前景很好的 tracer,它使用内核中的 lua 虚拟机处理,在没有调试信息的情况下在嵌入式设备上运行的很好。它分为几个步骤,并在有一段时间似乎超过了 Linux 上所有的追踪器。然后 eBPF 开始进行内核集成,而 ktap 的集成在它可以使用 eBPF 替代它自己的虚拟机后才开始。因为 eBPF 仍将持续集成几个月,ktap 开发者要继续等上一段时间。我希??今年晚些时候它能重新开发。

7. dtrace4linux

dtrace4linux 主要是 Paul Fox 一个人在业余时间完成的,它是 Sun DTrace 的 Linux 版本。它引入瞩目,还有一些 provider 可以运行,但是从某种程度上来说还不完整,更多的是一种实验性的工具(不安全)。我认为,顾忌到许可问题,人们会小心翼翼的为 dtrace4linux 贡献代码:由于当年 Sun 开源DTrace 使用的是 CDDL 协议,而 dtrace4linux 也不大可能最终进入 Linux kernel。Paul 的方法很可能会使其成为一个 add-on。我很乐意看到 Linux 平台上的 DTrace 和这个项目的完成,我认为当我加入 Netflix 后将会花些时间来协助完成这个项目。然而,我还是要继续使用内置的 tracers,如 ftrace 和 perf_events。

8.OL DTrace

Oracle Linux DTrace为了将 DTrace 引入 Linux,特别是 Oracle Linux,做出了很大的努力。这些年来发布的多个版本表明了它的稳定进展。开发者们以一种对这个项目的前景看好的态度谈论着改进 DTrace 测试套件。很多有用的 provider 已经完成了,如:syscall, profile, sdt, proc, sched 以及 USDT。我很期待 fbt(function boundary tracing, 用于内核动态跟踪)的完成,它是 Linux 内核上非常棒的 provider。OL DTrace 最终的成功将取决于人们对运行 Oracle Linux(为技术支持付费)有多大兴趣,另一方面取决于它是否完全开源:它的内核元件是开源的,而我没有看到它的用户级别代码。

9. sysdig

sysdig是一个使用类tcpdump语法来操作系统事件的新tracer,它使用lua提交进程。它很优秀,它见证了系统跟踪领域的变革。它的局限性在于它只在当前进行系统调用,在提交进行时将所有事件转储为用户级别。你可以使用系统调用做很多事情,然而我还是很希望它能支持跟踪点、kprobe和uprobe。我还期待它能支持eBPF做内核摘要。目前,sysdig开发者正在增加容器支持。留意这些内容。

延伸阅读

我关于 tracer 的工作包括:

ftrace:我的 perf-tools工具集(参考实例目录);我在 lwn.net 上的 关于ftrace的文章;LISA14的发言;还有帖子:函数计数, iosnoop,opensnoop,execsnoop,TCP转发, uprobes 以及USDT。

perf_evenets:我的网页 perf_events实例;SCALE上的发言Netflix的Linux性能分析;还有帖子CPU采样,静态追踪点,热点图,计数,内核行追踪,off-CPU时间图。

eBPF:帖子eBPF:迈出一小步,和一些BPF工具(我需要发布更多)。

SystemTap:我很久以前写了一篇有点过期的帖子使用SystemTap。最近,我发布了一些工具systemtap-lwtools来演示如何在没有内核诊断信息的情况下使用SystemTap。

LTTng:我只花了一点时间,还不足以发表任何内容。

ktap:我的网页ktap实例包含一些早期版本的单行小程序和脚本。

dtrace4linux:我在系统性能一书中给出了一些实例,并曾经开发了一些小的修复程序,如timestamps。

OL DTrace:由于它直接由DTrace转变而来,很多我早期关于DTrace的工作都有相关性(如果在这里给出链接的话就太多了,可以在我的主页上搜索)。当它更完善时,我会开发一些特殊工具。

sysdig:我向 fileslower 和 subsecond offset spectrogram 贡献了代码。

其他:我写了关于strace 的注意事项。

请不要有更多的 tracer!如果你想知道为什么 Linux 不仅仅只有一个 tracer,或者只用本身的DTrace,你可以在我的演讲稿从DTrace到Linux中找到答案,从28张幻灯片开始。

感谢Deirdré Straughan的编辑,以及与 General Zoi 的小马宝莉创作者一起创作的 tracing 小马。

如何linux内核报告问题

Linux Kernel BUG:soft lockup CPU#1 stuck分析

1.线上内核bug日志

kernel: Deltaway too big! 18428729675200069867 ts=18446743954022816244 write stamp =18014278822746377

kernel:------------[ cut here ]------------

kernel:WARNING: at kernel/trace/ring_buffer.c:1988 rb_reserve_next_event+0x2ce/0x370()(Not tainted)

kernel:Hardware name: ProLiant DL360 G7

kernel:Modules linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]

kernel: Pid:5483, comm: master Not tainted 2.6.32-220.el6.x86_64 #1

kernel: CallTrace:

kernel:[ffffffff81069b77] ? warn_slowpath_common+0x87/0xc0

kernel:[ffffffff81069bca] ? warn_slowpath_null+0x1a/0x20

kernel:[ffffffff810ea8ae] ? rb_reserve_next_event+0x2ce/0x370

kernel:[ffffffff810eab02] ? ring_buffer_lock_reserve+0xa2/0x160

kernel:[ffffffff810ec97c] ? trace_buffer_lock_reserve+0x2c/0x70

kernel:[ffffffff810ecb16] ? trace_current_buffer_lock_reserve+0x16/0x20

kernel:[ffffffff8107ae1e] ? ftrace_raw_event_hrtimer_cancel+0x4e/0xb0

kernel:[ffffffff81095e7a] ? hrtimer_try_to_cancel+0xba/0xd0

kernel:[ffffffff8106f634] ? do_setitimer+0xd4/0x220

kernel:[ffffffff8106f88a] ? alarm_setitimer+0x3a/0x60

kernel:[ffffffff8107c27e] ? sys_alarm+0xe/0x20

kernel:[ffffffff8100b308] ? tracesys+0xd9/0xde

kernel: ---[end trace 4d0a1ef2e62cb1a2 ]---

abrt-dump-oops: Reported 1 kernel oopses to Abrt

kernel: BUG: softlockup - CPU#11 stuck for 4278190091s! [qmgr:5492]

kernel:Modules linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]

kernel: CPU 11

kernel:Modules linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]

kernel:

kernel: Pid:5492, comm: qmgr Tainted: G W ---------------- 2.6.32-220.el6.x86_64 #1 HPProLiant DL360 G7

kernel: RIP:0010:[ffffffff8106f730] [ffffffff8106f730]do_setitimer+0x1d0/0x220

kernel: RSP:0018:ffff88080a661ef8 EFLAGS: 00000286

kernel: RAX:ffff88080b175a08 RBX: ffff88080a661f18 RCX: 0000000000000000

kernel: RDX:0000000000000000 RSI: 0000000000000082 RDI: ffff88080c8c4c40

kernel: RBP:ffffffff8100bc0e R08: 0000000000000000 R09: 0099d7270e01c3f1

kernel: R10:0000000000000000 R11: 0000000000000246 R12: ffffffff810ef9a3

kernel: R13:ffff88080a661e88 R14: 0000000000000000 R15: ffff88080a65a544

kernel: FS:00007f10b245f7c0(0000) GS:ffff88083c4a0000(0000) knlGS:0000000000000000

kernel: CS:0010 DS: 0000 ES: 0000 CR0: 000000008005003b

kernel: CR2:00007ff955977380 CR3: 000000100a80b000 CR4: 00000000000006e0

kernel: DR0:0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000

kernel: DR3:0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400

kernel:Process qmgr (pid: 5492, threadinfo ffff88080a660000, task ffff880809577500)

kernel: Stack:

kernel:00007f10b323def0 00007f10b248ead0 00007f10b26d0f78 00007f10b248ede0

kernel:0 ffff88080a661f68 ffffffff8106f88a 0000000000000000 0000000000000000

kernel:0 000000000000014c 00000000000f423d 0000000000000000 0000000000000000

kernel: CallTrace:

kernel:[ffffffff8106f88a] ? alarm_setitimer+0x3a/0x60

kernel:[ffffffff8107c27e] ? sys_alarm+0xe/0x20

kernel:[ffffffff8100b308] ? tracesys+0xd9/0xde

kernel: Code:89 ef e8 74 66 02 00 83 3d 15 69 b5 00 00 75 37 49 8b 84 24 70 07 00 00 48 0508 08 00 00 66 ff 00 66 66 90 fb 66 0f 1f 44 00 00 31 c0 e9 64 fe ff ff49 8b 84 24 68 07 00 00 48 c7 80 d0 00 00

kernel: CallTrace:

kernel:[ffffffff8106f769] ? do_setitimer+0x209/0x220

kernel:[ffffffff8106f88a] ? alarm_setitimer+0x3a/0x60

kernel:[ffffffff8107c27e] ? sys_alarm+0xe/0x20

kernel:[ffffffff8100b308] ? tracesys+0xd9/0xde

abrt-dump-oops: Reported 1 kernel oopses to Abrt

2.内核软死锁(soft lockup)bug原因分析

Soft lockup名称解释:所谓,soft lockup就是说,这个bug没有让系统彻底死机,但是若干个进程(或者kernel thread)被锁死在了某个状态(一般在内核区域),很多情况下这个是由于内核锁的使用的问题。

Linux内核对于每一个cpu都有一个监控进程,在技术界这个叫做watchdog(看门狗)。通过ps –ef | grep watchdog能够看见,进程名称大概是watchdog/X(数字:cpu逻辑编号1/2/3/4之类的)。这个进程或者线程每一秒钟运行一次,否则会睡眠和待机。这个进程运行会收集每一个cpu运行时使用数据的时间并且存放到属于每个cpu自己的内核数据结构。在内核中有很多特定的中断函数。这些中断函数会调用soft lockup计数,他会使用当前的时间戳与特定(对应的)cpu的内核数据结构中保存的时间对比,如果发现当前的时间戳比对应cpu保存的时间大于设定的阀值,他就假设监测进程或看门狗线程在一个相当可观的时间还没有执。Cpu软锁为什么会产生,是怎么产生的?如果linux内核是经过精心设计安排的CPU调度访问,那么怎么会产生cpu软死锁?那么只能说由于用户开发的或者第三方软件引入,看我们服务器内核panic的原因就是qmgr进程引起。因为每一个无限的循环都会一直有一个cpu的执行流程(qmgr进程示一个后台邮件的消息队列服务进程),并且拥有一定的优先级。Cpu调度器调度一个驱动程序来运行,如果这个驱动程序有问题并且没有被检测到,那么这个驱动程序将会暂用cpu的很长时间。根据前面的描述,看门狗进程会抓住(catch)这一点并且抛出一个软死锁(soft lockup)错误。软死锁会挂起cpu使你的系统不可用。

如果是用户空间的进程或线程引起的问题backtrace是不会有内容的,如果内核线程那么在soft lockup消息中会显示出backtrace信息。

3.根据linux内核源码分析错误

根据我们第一部分内核抛出的错误信息和call trace(linux内核的跟踪子系统)来分析产生的具体原因。

首先根据我们的centos版本安装相应的linux内核源码,具体步骤如下:

(1)下载源码的rpm包kernel-2.6.32-220.17.1.el6.src.rpm

(2)安装相应的依赖库,命令:yuminstall rpm-build redhat-rpm-config asciidoc newt-devel

(3)安装源码包:rpm -ikernel-2.6.32-220.17.1.el6.src.rpm

(4)进入建立源码的目录:cd~/rpmbuild/SPECS

(5)建立生成源码目录:rpmbuild-bp --target=`uname -m` kernel.spec

下面开始真正的根据内核bug日志分析源码:

(1)第一阶段内核错误日志分析(时间在Dec 4 14:03:34这个阶段的日志输出代码分析,其实这部分代码不会导致cpu软死锁,主要是第二阶段错误日志显示导致cpu软死锁)

我们首先通过日志定位到相关源代码:看下面日志:Dec 4 14:03:34 BP-YZH-1-xxxx kernel: WARNING: atkernel/trace/ring_buffer.c:1988 rb_reserve_next_event+0x2ce/0x370() (Not tainted)

根据日志内容我们可以很容易的定位到kernel/trace/ring_buffer.c这个文件的1988行代码如下:WARN_ON(1)。

先简单解释一下WARN_ON的作用:WARN_ON只是打印出当前栈信息,不会panic。所以会看到后面有一大堆的栈信息。这个宏定义如下:

#ifndef WARN_ON

#defineWARN_ON(condition) ({ \

int __ret_warn_on = !!(condition); \

if (unlikely(__ret_warn_on)) \

__WARN(); \

unlikely(__ret_warn_on); \

#endif

这个宏很简单保证传递进来的条件值为0或者1(两次逻辑非操作的结果),然后使用分支预测技术(保证执行概率大的分支紧邻上面的指令)判断是否需要调用__WARN()宏定义。如果满足条件执行了__WARN()宏定义也接着执行一条空指令;。上面调用WARN_ON宏是传递的1,所以会执行__WARN()。下面继续看一下__WARN()宏定义如下:

#define __WARN() warn_slowpath_null(__FILE__,__LINE__)

从接下来的call trace信息中我们也确实发现调用了warn_slowpath_null这个函数。通过在linux内核源代码中搜索这个函数的实现,发现在panic.c(内核恐慌时的相关功能实现)中实现如下:

voidwarn_slowpath_null(const char *file, int line)

warn_slowpath_common(file, line,__builtin_return_address(0),

TAINT_WARN, NULL);

EXPORT_SYMBOL(warn_slowpath_null);//都出这个符号,让其他模块可以使用这个函数

同样的我们看到了warn_slowpath_common这个函数,而在call trace当中这个函数在warn_slowpath_null函数之前打印出来,再次印证了这个流程是正确的。同样在panic.c这个文件中我发现了warn_slowpath_common这个函数的实现如下:

static voidwarn_slowpath_common(const char *file, int line, void *caller,

unsigned taint, struct slowpath_args *args)

const char *board;

printk(KERN_WARNING "------------[ cut here]------------\n");

printk(KERN_WARNING "WARNING: at %s:%d %pS()(%s)\n",

file, line, caller, print_tainted());

board = dmi_get_system_info(DMI_PRODUCT_NAME);//得到dmi系统信息

if (board)

printk(KERN_WARNING "Hardware name:%s\n", board);//通过我们的日志信息可以发现我们硬件名称是ProLiant DL360 G7

if (args)

vprintk(args-fmt, args-args);

print_modules();//打印系统模块信息

dump_stack();//dump信息输出(call trace开始)

print_oops_end_marker();//打印oops结束

add_taint(taint);

分析这个函数的实现不难发现我们的很多日志信息从这里开始输出,包括打印一些系统信息,就不继续深入分析了(请看代码注释,里面调用相关函数打印对应信息,通过我分析这些函数的实现和我们的日志信息完全能够对应,其中dump_stack是与cpu体系结构相关的,我们的服务器应该是属于x86体系)。这里在继续分析一下dump_stack函数的实现,因为这个是与cpu体系结构相关的,而且这个函数直接反应出导致内核panic的相关进程。这个函数实现如下:

* The architecture-independent dump_stackgenerator

void dump_stack(void)

unsigned long stack;

printk("Pid: %d, comm: %.20s %s %s %.*s\n",

current-pid, current-comm,print_tainted(),

init_utsname()-release,

(int

如何查看android systrace 分析

Systrace的介绍

Systrace跟踪代码

Systrace运行方式

Systrace数据分析

Systrace使用示例

TraceView的介绍

TraceView运行方式

TraceView数据分析

TraceView使用示例

总结

内容:

1.Systrace的介绍

Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(如Surfaceflinger、WindowManagerService等Framework部分关键模块、服务)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。

Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成:

1.内核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用Systrace的话,必须开启kernel中和ftrace相关的模块。

2.数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。

3.数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集 ftrace统计数据并生成一个结果网页文件供用户查看。

从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace。

怎样提高android启动速度

首先看一下Android系统的启动流程ftrace源码分析

bootloader

引导程序

kernel

内核

init

init初始化(这个大家都比较熟悉了ftrace源码分析,不要多说)

loads several daemons and services, including zygote

see /init.rc and init.platform.rc

zygote

这个是占用时间最多的ftrace源码分析,重点修理对象

preloads classes

装载了一千多个类,妈呀ftrace源码分析!!!

starts package manager 扫描package(下面详细介绍)

service manager

start services (启动多个服务)

从实际的测试数据来看,有两个地方时最耗时间的,一个是zygote的装载一千多个类和初始化堆栈的过程,用了20秒左右。另一个是扫描

/system/app,

/system/framework,

/data/app,

/data/app-private.

这几个目录下面的package用了大概10秒,所以我们重点能够修理的就是这两个老大的。

一、首先是调试工具的使用,可以测试哪些类和那些过程占用了多少时间,

主要工具为

stopwatch

Message loggers

grabserial

printk times

logcat

Android自带

bootchart

strace

AOSP的一部分(Eclair及以上版本)

使用例子

在init.rc中为了调试zygote

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server改为

service zygote /system/xbin/strace -tt -o/data/boot.strace /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

method tracer*

ftrace*

详细使用可看提供的文档和网页介绍

上面的工具如果不用详细的分析不一定都用到,也可以使用logcat就可以,在代码中加一点计算时间和一些类的调试信息也可以达到很好效果。

二、zygote 装载1千多个类

首先,我们可以添加一点调试信息,以获得具体转载情况。

diff --git a/core/java/com/Android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java

index 404c513..f2b573c 100644

--- a/core/java/com/Android/internal/os/ZygoteInit.java

+++ b/core/java/com/Android/internal/os/ZygoteInit.java

@@ -259,6 +259,8 @@ public class ZygoteInit {

} else {

Log.i(TAG, "Preloading classes...");

long startTime = SystemClock.uptimeMillis();

+ long lastTime = SystemClock.uptimeMillis();

+ long nextTime = SystemClock.uptimeMillis();

// Drop root perms while running static initializers.

setEffectiveGroup(UNPRIVILEGED_GID);

@@ -292,12 +294,24 @@ public class ZygoteInit {

if (Config.LOGV) {

Log.v(TAG, "Preloading " + line + "...");

+ //if (count%5==0) {

+ // Log.v(TAG, "Preloading " + line + "...");

+ Log.v(TAG, "Preloading " + line + "...");

Class.forName(line);

+ nextTime = SystemClock.uptimeMillis();

+ if (nextTime-lastTime 50) {

+ Log.i(TAG, "Preloading " + line + "... took " + (nextTime-lastTime) + "ms.");

+ lastTime = nextTime;

if (Debug.getGlobalAllocSize() PRELOAD_GC_THRESHOLD) {

if (Config.LOGV) {

Log.v(TAG,

" GC at " + Debug.getGlobalAllocSize());

+ Log.i(TAG,

+ " GC at " + Debug.getGlobalAllocSize());

runtime.gcSoftReferences();

runtime.runFinalizationSync();

Debug.resetGlobalAllocSize();

上面+代表添加的代码,这样就可以很容易的得到在装载类的过程中具体装载了哪些类,耗费了多久。具体装载的类在文件platform/frameworks/base/ preloaded-classes

内容类似ftrace源码分析

Android.R$styleable

Android.accounts.AccountMonitor

Android.accounts.AccountMonitor$AccountUpdater

Android.app.Activity

Android.app.ActivityGroup

Android.app.ActivityManager$MemoryInfo$1

Android.app.ActivityManagerNative

Android.app.ActivityManagerProxy

Android.app.ActivityThread

Android.app.ActivityThread$ActivityRecord

Android.app.ActivityThread$AppBindData

Android.app.ActivityThread$ApplicationThread

Android.app.ActivityThread$ContextCleanupInfo

Android.app.ActivityThread$GcIdler

Android.app.ActivityThread$H

Android.app.ActivityThread$Idler

而这个文件是由文件WritePreloadedClassFile.java中的WritePreloadedClassFile类自动生成

* Writes /frameworks/base/preloaded-classes. Also updates

* {@link LoadedClass#preloaded} fields and writes over compiled log file.

public class WritePreloadedClassFile

* Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us.

static final int MIN_LOAD_TIME_MICROS = 1250;//这个代表了装载时间小于1250us即1.25ms的类将不予装载,也许可以改这个参数减少一下类的装载

//这里可以看到什么样的类会被装载

A:启动必须装载的类,比如系统级的类

B:刚才说的装载时间大于1.25ms的类

C:被使用一次以上或被应用装载的类

仔细看看筛选类的具体实现,可以帮助我们认识哪些类比较重要,哪些可以去掉。

筛选规则是

第一 isPreloadable,

/**Reports if the given class should be preloaded. */

public static boolean isPreloadable(LoadedClass clazz) {

return clazz.systemClass !EXCLUDED_CLASSES.contains(clazz.name);

意思是指除了EXCLUDED_CLASSES包含的类之外的所有系统装载的类。

EXCLUDED_CLASSES包含

* Classes which we shouldn't load from the Zygote.

private static final SetString EXCLUDED_CLASSES

= new HashSetString(Arrays.asList(

// Binders

"Android.app.AlarmManager",

"Android.app.SearchManager",

"Android.os.FileObserver",

"com.Android.server.PackageManagerService$AppDirObserver",

// Threads

"Android.os.AsyncTask",

"Android.pim.ContactsAsyncHelper",

"java.lang.ProcessManager"

目前是跟Binders跟Threads有关的不会被预装载。

第二 clazz.medianTimeMicros() MIN_LOAD_TIME_MICROS装载时间大于1.25ms。

第三 names.size() 1 ,既是被processes一次以上的。

上面的都是指的system class,另外还有一些application class需要被装载

规则是fromZygote而且不是服务

proc.fromZygote() !Policy.isService(proc.name)

fromZygote指的除了com.Android.development的zygote类

public boolean fromZygote() {

return parent != null parent.name.equals("zygote")

!name.equals("com.Android.development");

/除了常驻内存的服务

* Long running services. These are restricted in their contribution to the

* preloader because their launch time is less critical.

// TODO: Generate this automatically from package manager.

private static final SetString SERVICES = new HashSetString(Arrays.asList(

"system_server",

"com.google.process.content",

"Android.process.media",

"com.Android.bluetooth",

"com.Android.calendar",

"com.Android.inputmethod.latin",

"com.Android.phone",

"com.google.Android.apps.maps.FriendService", // pre froyo

"com.google.Android.apps.maps:FriendService", // froyo

"com.google.Android.apps.maps.LocationFriendService",

"com.google.Android.deskclock",

"com.google.process.gapps",

"Android.tts"

好了。要转载的就是这些类了。虽然preloaded- classes是在下载源码的时候已经确定了的,也就是对我们来说WritePreloadedClassFile类是没用到的,我们可以做的就是在 preloaded-classes文件中,把不预装载的类去掉,试了把所有类去掉,启动确实很快跳过那个地方,但是启动HOME的时候就会很慢了。所以最好的方法就是只去掉那些没怎么用到的,不过要小心处理。至于该去掉哪些,还在摸索,稍后跟大家分享。有兴趣的朋友可以先把preloaded- classes这个文件里面全部清空,启动快了很多,但在启动apk的时候会慢了点。当然了,也可以把Android相关的类全部去掉,剩下java的类,试过了也是可以提高速度。

三,系统服务初始化和package 扫描

在启动系统服务的init2()时会启动应用层(Java层)的所有服务。

public static void main(String[] args) {

System.loadLibrary("Android_servers");

init1(args); //init1 初始化,完成之后会回调init2()

在init2()中会启动一个线程来启动所有服务

public static final void init2() {

Log.i(TAG, "Entered the Android system server!");

Thread thr = new ServerThread();

thr.setName("Android.server.ServerThread");

thr.start();

class ServerThread extends Thread {

public void run() {

关键服务:

ServiceManager.addService("entropy", new EntropyService());

ServiceManager.addService(Context.POWER_SERVICE, power);

context = ActivityManagerService.main(factoryTest);

ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));

PackageManagerService.main(context,

factoryTest != SystemServer.FACTORY_TEST_OFF);//apk扫描的服务

ServiceManager.addService(Context.ACCOUNT_SERVICE,

new AccountManagerService(context));

ContentService.main(context,

factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);

battery = new BatteryService(context);

ServiceManager.addService("battery", battery);

hardware = new HardwareService(context);

ServiceManager.addService("hardware", hardware);

AlarmManagerService alarm = new AlarmManagerService(context);

ServiceManager.addService(Context.ALARM_SERVICE, alarm);

ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));

WindowManagerService.main(context, power,

factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);

ServiceManager.addService(Context.WINDOW_SERVICE, wm);

  ftrace源码分析 


评论


最新评论