`
844604778
  • 浏览: 551741 次
文章分类
社区版块
存档分类
最新评论

使用LTTng链接内核和用户空间应用程序追踪

 
阅读更多

译者注:最近在看Linux内核追踪工具相关的东西,就看到了这篇论文《Combined Tracing of the Kernel and Applications with LTTng》,介绍了使用LTTng实现用户空间追踪,并能与内核空间追踪关联并进行分析。

其实本文更像一篇论文总结博客,不是纯粹的翻译!所以也没有严格的按照原文的句逗,大家学习知识就好。

--------------------------------华丽的分割线-------------------------------------------

零、相关知识介绍

Tracers Timeline(追踪器时间轴)
● 1999 : LTT
● 2005 : LTTng
● 2005 : Dtrace
● 2008 : Ftrace
● 2009 : Perf
● 2012 : LTTng 2.0


LTT:(Linux Trace Toolkit)是linux下一种用于跟踪系统详细运行状态和流程的重要工具,它可以跟踪记录系统中的特定事件。这些事件包括:
* 系统调用的进入和退出
* 陷阱/中断(Trap / Irq)的进入和退出
* 进程调度事件
* 内核定时器
* 进程管理相关事件:创建 ,唤醒,信号处理等等
* 文件系统相关事件:Open / Read / Write / Seek / Ioctl 等等
* 内存管理相关事件:内存分配/释放等
* 其他事件:IPC / Socket/ 网络 等等
此外 Ltt还提供了自定义和记录需要跟踪的事件类型的函数接口。


LTTng: (Linux Trace Toolkit Next Generation),它是用于跟踪 Linux 内核、应用程序以及库的系统软件包。LTTng 主要由内核模块和动态链接库(用于应用程序和动态链接库的跟踪)组成。它由一个会话守护进程控制,该守护进程接受来自命令行接口的命令。babeltrace 项目允许将追踪信息翻译成用户可读的日志,并提供一个读追踪库,即 libbabletrace。
LTTng 不仅使用了 Linux 内核中的追踪点(tracepoint)手段,而且使用了其他各种信息来源,比如kprobes 和 Perf(Linux 中的性能监检测工具)。这对于调试大范围内的bug 是非常有用的,否则这种调试工作将极具挑战性。比如,包括并行系统和实时系统中的性能问题。另外,用户自己定制的工具也可以加入到其中。LTTng 的设计目标是将性能影响最小化,而且在没有跟踪的情况下,对系统的影响应该几乎为零。


LTTng2.0:使用简单的用户接口--lttng命令来整合用户空间和内核空间追踪。还有其他一些特性:
* Multi-distribution & Multi-arch

从支持多个发行版 & 多个架构
* Produces CTF (Common Trace Format) natively (http://www.efficios.com/ctf)

有通用的追踪格式
* Tracepoints, detailed syscall tracing (fast strace replacement), Function tracer, CPU Performance Monitoring Unit (PMU) counters and kprobes support,
支持探测点、详细的系统调用追踪、函数追踪器、CPU性能监控以及kprobe
* Integrated interface for both kernel and userspace tracing

上面已经提到的提供对用户空间和内核的追踪接口
* Have the ability to attach "context" information to events in the trace (e.g. any PMU counter, pid, ppid, tid, comm name, etc). All the extra information fields to be collected with events are optional, specified on a per-tracing-session basis (except for timestamp and event id, which are mandatory).
可以在追踪中的事件附加上下文信息(比如PMU 性能监控单元counter, pid, ppid, tid, comm name等)。所有的额外信息都被收集起来放在一个追踪session的部件里。




一、本文研究背景

现如今,多核、集群以及嵌入式系统等让Linux系统变得越来越复杂,而开发者就面临着越来越复杂的bug,这些bug可能只在产品中出现而在调试时就不见了或者很少发生而且只让系统变得有点慢,这些特性都让传统的调试内核工具变得无效。所以需要有调试工具来应对这些问题。这些工具对系统的性能影响必要要很小,从而可以把他们用在产品上。
内核追踪就是一款这样的工具,它提供了一种很有效的理解内核行为以及调试用户空间程序和内核故障的方法。但是在一些情况下,内核追踪也是不足以应对的。比如,有个程序会发出大量的请求或者它有很多很多个线程从而导致只从内核空间的角度来追踪是非常难的。所以在这种情况下,应用空间的程序也需要追踪。而且要求追踪的用户空间事件与内核事件是相关的。
本文提到的是LTTng的一个用户空间追踪器,可以实现上述功能 -- 同时追踪用户空间应用程序事件和内核事件。


一&番外、What is tracing?

追踪是一门技术用来理解或者监控系统里面是如何运行的。追踪器(tracer)是用来追踪的软件。追踪可以用来调试很多的bug,这些可能包括复杂的并行系统或者实时系统中的性能问题。
追踪其实和记录日志很像:它主要就是记录系统中的事件。但是,与日志相比,它记录的是系统中更为底层以及更频繁的事件。所以追踪其必须被优化从而可以处理大量的数据而不对系统产生很大的影响。追踪其通常一秒钟产生数以千计的事件。它们经常处理数以百万计的事件以及数M到数十G的数据。
追踪可能包括操作系统内核的各种事件(比如中断请求处理函数的进入/退出,系统调用的进入/退出,调度情况以及网络情况等),他们还可能包括应用程序的事件。
事件追踪的列表可能被手动的像读取日志文件那样读取,但是因为工作量太大,所以追踪分析器或者阅读器可以根据这些庞大的数据生成图型或者统计数据。这些分析软件需要特殊设计以便快读处理这些庞大的数据。


二、相关研究

* strace :是一种提供原始的用户空间追踪的工具,它可以追踪系统调用和信号的情况。但是它会引发较大的性能损失。但是它限制到追踪一些系统调用和信号,可以以较小的损失来获得追踪信息。

* DTrace :具有静态定义的跟踪(Statically Defined Tracing,SDT)可以用在用户空间应用中。它用一种在运行时链接中的特殊支持实现的。【6】

* SystemTap :如同DTrace实现了SDT。【2】

* LTTng : 这些年提供了几种不同的用户空间追踪技术:
1)systemcall assisted -- 这个技术声明了两个新的系统调用,第一个用来注册一个事件类型并返回事件ID,第二种用来记录事件,他需要事件ID和负荷
2) userspace tracing -- 它不需要内核的支持。程序在他们自己的地址空间缓存上写事件,追踪器为每个线程穿件一个共享缓存的同伴进程,同伴进程使用一个无锁算法来使用这个缓存。
---- 这两个方法都被遗弃了,现在有个新的替代方案被提出,它是一个简单的使用字符串做参数的系统调用。调用它会产生一个参数就是这个字符串的事件,…… 。这个特性是来自于DebugFS(/debug/ltt/write_event),向这个文件写入一个字符就会创建一个叫做user-space_event的事件,事件的参数是这个字符串。

* Ftrace :有类似的特性,叫做trace_marker.
-----
引用:
【6】 Frank Hofmann. The DTrace backend on Solaris for x86/x64. http://opensolaris.org/os/project/czosug/events_archive/czosug2_dtrace_x86.pdf.
【2】 Systemtap static probes. https://fedoraproject.org/wiki/Features/SystemtapStaticProbes.


三、具体实现 **** 重点

此处主要介绍UST(LTTng Userspace Tracer,LTTng用户空间追踪器)的结构,首先我们看看UST的重要设计目标:
* 需要与内核空间的追踪器完全独立,用户空间追踪和内核空间追踪的结果应该在分析的时候再关联
* 需要是可重入的,并支持多线程&追踪信号处理的事件
* 在快速路径中不能有系统调用
* 追踪结果不能被拷贝
* 需要能追踪共享库中的代码
* 探测点需要支持无穷多的参数
* 对于连接器和编译器不需要特殊的支持
* 追踪格式需要是紧凑的


3.1 追踪库

* libust - 追踪库
* liburcu - 用户空间读/拷贝/更新库,用来在数据结构之间无锁操控【4】
* libkcompat - Linux内核提供用户空间API的库(如院子看哦乃至,连接表机制,kref类型机制等)【5】
-------
引用:
【4】Mathieu Desnoyers and Paul E. McKenney.Userspace Read-Copy-Update Library.http://ltt.polymtl.ca/cgi-bin/gitweb.cgi?p=userspace-rcu.git.
【5】Pierre-Marc Fournier and Jan Blunck. libkcompat. http://git.dorsal.polymtl.ca/?p=libkcompat.git.


3.2 时间

LTTng的用户空间追踪器和内核没有依赖关系,但是为了把内核追踪和用户空间追踪合并分析,所有的追踪器的时间戳必须是一致的(必须来自一样的时钟源)。
一个合适的时钟源必须是高精度的这样才能保证在核之间都是一致的;还要保证用户空间和内核空间访问时的开销要小(因为系统调用的代价以及很高了)。
UST中使用的是内核提供的时钟源,而且直接使用rdtsc指令读取。这个时钟源也是内核追踪器使用的,读取和多核之间同步都很快。


3.3 探测点

探测点是由内核LTTng使用的两个端口组成:marker和tracepoint,它们的使用也与内核中一样。
插入marker就像在事件需要记录的地方插入一行代码一样简单,下面的代码中我们可以看到,mark的使用与printf很类似:
trace_mark(main,myevent,"firstarg %d secondarg %s",v,st);
tracepoint的设计更佳,从下面代码中可以看到,它基本不包括一个格式字符串,但需要一些声明,一般分开放在C文件和头文件中。所以,它更合适放在永久的代码中(需要时,只需要修改头文件中声明即可)。而marker更合适用在调试时添加。(用来调试中打印信息)
trace_main_myevent(v,st);
marker和tracepoint在可执行或者动态对象中的一个特殊字段列举他们自身信息,所有包含探测点的库和可执行文件因此需要通过调用追踪库来注册他们的marker/tracepoint。这是通过唤醒一个添加构造函数的宏来实现的。


3.4 缓存机制

缓存机制是无锁LTTng算法的一个端口,它的设计很多源自于K42操作系统以及Linux内核中的Relay系统【8】事件在一个圆形的、每个进程一个的缓冲区中被写入,这个缓冲区被划分成多个sub-buffer。默认情况下,当一个sub-buffer写满,就会有一个清空守护进程清空它。在另外一种操作模式下(flight recorder),圆形的缓冲区被不断的重写直到这个缓冲区被清除,这对于等待一些罕见的bug出现是很有帮助的。
每个事件都与一个channel(通道?)相关联,对于每个channel每个进程都有个明显的缓冲区。有几个channel允许选择每个channel的sub-buffer大小。在flight recorder模式下,它通过将一些时间放在低速率的缓冲区中实现允许保持这些事件的缓冲区很长的时间。
缓冲区被分配在System V的共享内存字段中,所以清空守护进程可以将它们映射到它自己的地址空间。
写缓冲区时使用了一种已形式化验证过其安全性的无锁算法,所以可以保证可重入、多线程、安全性。
-----
【8】Karim Yaghmour, R Wisniewski, R Moore, and M Dagenais. relayfs: An efficient unified approachfor transmitting data from kernel to user space. In Linux Symposium, Ottawa, Ontario, Canada, 2003.


3.5 追踪控制

用户空间追踪器肯定需要一个控制方法,比如我们需要开始/停止追踪、使能/关闭/列出探测点以及控制追踪参数(比如sub-buffer的大小以及数量)
一个叫做ust的辅助应用被设计用来做这项工作,它与被追踪的程序通过UNIX socket通信,被追踪哦进程中一个特殊的线程来处理这个通信。这个线程并不是当这个应用程序开始时就启动,这使得UST看起来不是那么有攻击性。而是,当追踪库构造函数为一个特别的信号注册一个信号控制器,当那个信号被接收到,这个监听的线程才会启动。这个线程会在一个预先定义好的目录下创建一个socket,socket的名为进程ID。


3.6 数据收集

有一个进程收集系统中所有被追踪的进程的追踪数据,这个进程叫做ustd。收集数据使用一个命名socket,socket会调用ustd 并定位到与被追踪的程序的socket同样的目录下。通过这个socket,ustd才能接受指令并收集给定PID对应的缓存的追踪数据。
收到指令后,ustd会创建一个线程用来链接socket和被追踪的进程。首先,假如socket还没准备好它会发送一个SIGIO信号,然后,它会请求共享内存以求映射缓存。
直到能够使用被追踪应用的socket后,这个线程会发一个请求来获取下一个sub-buffer的许可。当下一个sub-buffer满了后,会发送一个回复。收到回复后,这个线程会将他的数据写到追踪文件中(从共享内存段中读取的)。


3.7 过早和过晚地追踪

UST使用一种特殊的机制用来保证在程序执行时开始追踪:用两个环境变量运行这个程序,这些环境变量被追踪库构造函数解析,设定这些环境变量是为了保证在程序进入他的main()函数时追踪已然开始。
但对于程序结束时如何结束跟踪难度比较大:可能程序崩溃了然后就没办法通知ustd来获取最后一个sub-buffer的信息,更坏的可能是在追踪还没映射它的缓存程序就结束了。对于前面的情况,追踪的最后一部分就丢失了;对于后面的情况,整个追踪就丢失了,因为内核会在使用者与共享内存段断开连接时解除分配这段共享内存。
当程序崩溃时,内核会关闭socket连接,但是ustd能够检测到socket的断开,并在缓存上运行一个崩溃恢复程序,这个程序会辨别哪个sub-buffer有还没记录的数据以及有多少数据可以被恢复,这些数据会被恢复程序追加到追踪文件后。对于如何辨别sub-buffer中的数据是不是有效的,使用的是一些在映射缓存与共享内存段的原子算法中用到的计数器。
当程序的生命周期对于ustd来说太短了,来不及映射内存,新的难题出现了。(目前UST还没支持这种情况下的追踪)但是计划是使用析构函数来处理这个问题,假如追踪库的析构函数检测到一个追踪被记录但是他的缓冲区还未被映射,它会扩展程序的生命周期来给ustd足够的时间映射。


四、性能测试及其他&

论文中一般都要对自己的东西进行测试并证明自己的东西是有价值的,而且性能上面还是很不错的!所以这里我就不翻译了!本文的主要价值是看第三节也就是主要设计以及实现的思路。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics