《性能之巅:洞悉系统、企业与云计算》第一章(绪论)和第二章(方法)的笔记,请参考Part 1,第三章(操作系统)的笔记,请参考Part 2,本文是第四章——观测工具。

在实践中,工具不能覆盖所有方面,系统性能专家利用推论和解释:用间接的工具和统计来弄清楚系统的活动。

工具类型

工具的一种分类:

《性能之巅:洞悉系统、企业与云计算》-观测工具-笔记_性能优化


有些工具不止适合一个象限,top(1)有一个系统级别的视图,DTrace也有进程级别的能力。还有一些性能工具是基于剖析(profiling)的,对系统或进程做一系列快照,以此来进行观测。

计数器

内核维护各种统计数据,称为计数器,用于对事件计数。计数器的使用可认为是零开销的,因为它们默认就是开启的,而且始终由内核维护。唯一的使用开销是从用户空间读取它们的时候(可忽略不计)。

系统级别工具:

  • vmstat:虚拟内存和物理内存的统计,系统级别;
  • mpstat:每个CPU的使用情况;
  • iostat:每个磁盘I/O的使用情况,由块设备接口报告;
  • netstat:网络接口的统计,TCP/IP栈的统计,以及每个连接的一些统计信息;
  • sar:各种各样的统计,能归档历史数据。

进程级别工具:

  • ps:进程状态,显示进程的各种统计信息,包括内存和CPU;
  • top:按一个统计数据(如CPU使用)排序,显示排名高的进程。基于Solaris的系统对应的工具是prstat(1M)
  • pmap:将进程的内存段和使用统计一起列出。

进程工具以进程为导向,使用内核为每个进程维护的计数器,从/proc目录读取统计信息。

跟踪

跟踪收集每一个事件的数据以供分析。跟踪框架一般默认是不启用的,因为跟踪捕获数据会有CPU开销,另外还需要不小的存储空间来存放数据。这些开销会拖慢所跟踪的对象,在解释测量时间时需要加以考虑。

日志,包括系统日志,可以认为是一种默认开启的低频率跟踪。日志包括每一个事件的数据,虽然通常只针对偶发事件,如错误和警告。

系统级别工具:

  • tcpdump:网络包跟踪(用libpcap库);
  • snoop:为基于Solaris的系统打造的网络包跟踪工具;
  • blktrace:块I/O跟踪(Linux);
  • iosnoop:块I/O跟踪(基于DTrace);
  • execsnoop:跟踪新进程(基于DTrace);
  • dtruss:系统级别的系统调用缓冲跟踪(基于DTrace);
  • DTrace:跟踪内核的内部活动和所有资源的使用情况(不仅仅是网络和块I/O),支持静态和动态的跟踪;
  • SystemTap:跟踪内核的内部活动和所有资源的使用情况,支持静态和动态的跟踪;
  • perf:Linux性能事件,跟踪静态和动态的探针。

进程级别工具:

  • strace:基于Linux系统的系统调用跟踪;
  • truss:基于Solaris系统的系统调用跟踪;
  • gdb:源代码级别的调试器,广泛应用于Linux系统;
  • mdb:Solaris系统的一个具有可扩展性的调试器。

剖析

剖析,profiling,通过对目标收集采样或快照来归纳目标特征。剖析工具,剖析器profiler,有时会稍微改变这一频率,避免采样与目标活动同一步调,因为这样可能会导致多算或少算。

基于时间+硬件缓存的剖析器:

  • oprofile:Linux系统剖析;
  • perf:Linux性能工具集,包含有剖析的子命令;
  • DTrace:程序化剖析,基于时间的剖析用自身的profile provider,基于硬件事件的剖析用cpc provider;
  • SystemTap:程序化剖析,基于时间的剖析用自身的timer tapset,基于硬件事件的剖析用自身perf tapset;
  • cachegrind:源自valgrind工具集,能对硬件缓存的使用做剖析,也能用kcachegrind做数据可视化;
  • Intel VTune Amplifier XE:Linux和Windows的剖析,拥有包括源代码浏览在内的图形界面;
  • Oracle Solaris Studio:用自带的性能分析器对Solaris和Linux做剖析,拥有包括源代码浏览在内的图形界面。

监视sar

最广泛用于监视单一操作系统的工具是sar(1),基于计数器的,在预定的时间(通过cron)执行以记录系统计数器的状态。读取自己统计信息的归档数据(若开启)来打印历史统计信息。

在Linux中,sar(1)是通过sysstat包提供的。

类似工具:System Data Recorder和Collectl。

观测来源

系统性能统计的主要来源是:/proc/sys和kstat。一份比较完整的观测来源清单如下表:

Type

Linux

Solaris

进程级计数器

/proc

/proc,lxproc

系统级计数器

/proc,/sys

kstat

设备驱动和调试信息

/sys

kstat

进程级跟踪

ptrace,uprobes

procfs,dtrace

性能计数器

perf event

libcpc

网络跟踪

libpcap

libdlpi,libpcap

进程级延时指标

延时核算

微状态核算

系统级跟踪

tracepoints,kprobes,ftrace

dtrace

/proc

一个提供内核统计信息的文件系统接口。/proc由内核动态创建,不需要任何存储设备(在内存中运行)。多数文件是只读的,为观测工具提供统计数据。一部分文件是可写的,用于控制进程和内核的行为。

Linux中/proc的文件系统类型是proc,Solaris则是procfs

Linux中与进程性能观测相关的文件:

  • limits:实际的资源限制;
  • maps:映射的内存区域;
  • sched:CPU调度器的各种统计;
  • schedstat:CPU运行时间、延时和时间分片;
  • smaps:映射内存区域的使用统计;
  • stat:进程状态和统计,包括总的CPU和内存的使用情况;
  • statm:以页为单位的内存使用总结;
  • status:stat和statm的信息,用户可读;
  • task:每个任务的统计目录。

Linux中与性能观测相关的系统级别的文件:

  • cpuinfo:物理处理器信息,包含所有虚拟CPU、型号、时钟频率和缓存大小;
  • diskstats:对于所有磁盘设备的磁盘I/O统计;
  • interrupts:每个CPU的中断计数器;
  • loadavg:负载平均值;
  • meminfo:系统内存使用明细;
  • net/dev:网络接口统计;
  • net/tcp:活跃的TCP套接字信息;
  • schedstat:系统级别的CPU调度器统计;
  • self:关联当前进程ID路径的符号链接,为了使用方便;
  • slabinfo:内核slab分配器缓存统计;
  • stat:内核和系统资源的统计,CPU、磁盘、分页、交换区、进程;
  • zoneinfo:内存区信息。

Solaris下,/proc只有进程状态的统计。系统级别的观测采用其他框架,主要是kstat。与性能观测相关的文件:

  • map:虚拟地址空间映射;
  • psinfo:进程的各种信息,包括CPU和内存的使用;
  • status:进程状态信息;
  • usage:扩展的进程活动统计,包括进程微状态、错误、块、上下文切换,以及系统调用计数;
  • lstatus:与status相似,但包含的是每一个线程的统计;
  • lpsinfo:与psinfo相似,但包含的是每一个线程的统计;
  • lusage:与usage相似,但包含的是每一个线程的统计;
  • lwpsinfo:针对代表性LWP(目前最活跃)的轻量级进程(线程)统计,还有lwpstatus和lwpsinfo文件;
  • xmap:扩展的内存映射统计(尚无文档);

sys

kstat

Solaris上有一个为系统级别的观测工具所用的内核统计框架(kstat)。kstat包含绝大多数资源的统计,一个典型的系统在kstat里能有上万计的可用统计。与/proc/sys不同,kstat没有伪文件系统,要用ioctl()从/dev/kstat读取。一般是调用libkstat库里的函数来执行这一操作,或用Sun::Solaris::Kstat,该Perl库具有同样的功能(虽然一些偏好libkstat的发行版里取消对该Perl库的支持)。命令行工具kstat(1M)也能提供统计数据,还能用在shell脚本里。

kstat是四元组的结构:
module:instance:name:statistic

  • module:一般指的是创建统计数据的内核模块,如sd指SCSI磁盘驱动,zfs指的是ZFS;
  • instance:某些模块是以多个实例的形式存在的,例如对每一个SCSI磁盘都有一个sd模块。instance是一个枚举值;
  • name:一组统计数据的名字;
  • statistic:单个的统计值名;

延时核算

开启CONFIG_TASK_DELAY_ACCT选项的Linux系统按以下状态跟踪每个任务的时间:

  • 调度器延时:等待轮到上CPU;
  • 块I/O:等待块I/O完成;
  • 交换:等待换页(内存压力);
  • 内存回收:等待内存回收例程;

微状态核算

Solaris上有线程级别和CPU级别的微状态核算(microstate accounting),针对预先定义好的状态可以记录高精度的时间。相较基于tick的指标,精确度有很大的提升,还提供一些附加的状态,用于性能分析。

CPU级别的指标是通过kstat暴露给用户空间工具,而进程级别的指标是通过/proc

mpstat(1M)可用于输出CPU的微状态,如usr、sys和idl列,分别对应内核代码中的CMS_USER、CMS_SYSTEM和CMS_IDLE。

prstat -m可用于打印线程的微状态,如USR、SYS。

其他观测源

其他:

  • CPU性能计数器:可编程的硬件寄存器,提供低层级的性能信息,包括CPU周期计数、指令计数、停滞周期等。在Linux上是通过perf_events接口,或系统调用perf_event_open(),或perf(1)来访问这些计数器。Solaris里是通过libcpc,或包括cpustat(1M)在内的工具来访问的;
  • 进程级别跟踪:跟踪的是用户级别软件事件,如系统调用和函数调用。一般执行的代价较高,会拖慢跟踪的目标。Linux上有系统调用ptrace()来控制进程跟踪,strace(1)用来跟踪系统调用,uprobes来做用户级别的动态跟踪。Solaris的系统用procfs和truss(1)来跟踪系统调用,DTrace做动态跟踪;
  • 内核跟踪:Linux中,tracepoints提供静态的内核探针(原先叫做内核标记,kernel markers),kprobes提供动态探针。工具ftrace、perf(1)、DTrace和SystemTap都用到这两项。Solaris系统,静态和动态的探针都由dtrace内核模块提供。DTrace、SystemTap都用到内核跟踪;
  • 网络嗅探:网络嗅探提供一种从网络设备上抓包的方法,能对数据包和协议的性能做详细的调查。在Linux上,嗅探的功能是通过libpcap库和/proc/net/dev提供的,命令行工具则有tcpdump(8)。Solaris上,嗅探功能是通过libdlpi库和/dev/net提供的,命令行工具是snoop(1M)。往Solaris系统移植的libpcap库和tcpdump(8)还在开发中。捕获和检查所有的数据包其实无论对于CPU还是存储都是有开销的;
  • 进程核算:进程核算可以追溯到大型机时代,那时候要对使用计算机的部门和用户收费,是基于进程的执行和运行时间计费的。现在它以某种形式存在于Linux和基于Solaris的系统上,有时能在进程级别对性能分析有所帮助。工具atop(1)用进程核算能捕捉到短暂存活的进程并显示其信息,而用/proc快照的办法很可能无法觉察到这件事;
  • 系统调用:一些可用的系统调用和库函数调用能提供某些性能指标。其中包括getrusage(),这个函数调用是为进程拿到自己资源的使用统计,包括用户时间、系统时间、错误、消息,以及上下文切换。Solaris用的是swapctl(),这个系统函数用于swap 设备的管理和统计(Linux对应的是/proc/swap)。

其他:

  • Linux:I/O核算、blktrace、timer_stats、lockstat、debugfs;
  • Solaris:扩展核算(extended accounting)、流核算(flow accounting)、Solaris审计。

最后,可用工具打开/dev/mem//dev/kmem直接读取内核内存。

DTrace

DTrace的设计是生产环境安全的,拥有最小的性能开销。

静态和动态跟踪

静态跟踪:在编译之前加进代码里的静态探针
动态跟踪:在编译之后软件运行时加入的动态探针

int指令引发一个软中断,该软中断已经接到指示执行动态跟踪的action。当动态跟踪被禁用时,指令会回到原来的状态。这是内核地址空间的现场修改(live patching),所采用的技术会因处理器类型的不同而有所不同。

只有当动态跟踪开启后,才能插入指令。没开启时,是没有附加指令的,因此也没有任何探针效果。这就是不使用时零开销(zero overhead when not in use)。使用时来自附加指令的开销是与探针触发的频率成比例的:跟踪事件的频率和所执行的action。

DTrace能动态跟踪函数的入口和返回,以及任何在用户空间的指令。由于这是在CPU指令上动态建立探针,而CPU指令在软件不同版本时会发生变化,所以这是一个不稳定接口(unstable interface)。在跟踪的软件版本更新时可能需要变更所有的Dtrace单行命令和脚本。

探针

DTrace探针是以四元组命名的,provider:module:function:name,provider是相关探针的集合。module和function是动态产生的,标记探针指示的代码位置。name是探针的名字。

provider

可用的DTrace provider取决于你的DTrace和操作系统的版本,包括:

  • syscall:系统调用自陷表;
  • vminfo:虚拟内存统计;
  • sysinfo:系统统计;
  • profile:任意频率的采样;
  • sched:内核调度事件;
  • proc:进程级别事件,创建、执行、退出;
  • io:块设备接口跟踪(磁盘I/O);
  • pid:用户级别动态跟踪;
  • tcp:TCP协议事件,连接、发送和接收;
  • ip:IP协议事件,发送和接收;
  • fbt:内核级别动态跟踪。

很多provider都是用静态跟踪来实现的,好处是接口稳定,代价(缺点)是监测视野有点限制。为了确保对目标软件的不同版本都能适用,尽量用静态provider来编写脚本。

参数

探针通过一组称为参数的变量来提供数据。参数的使用取决于provider。系统调用的provider给每一个系统调用都做入口和返回的探针。

D语言

D语言与awk类似,能用作单行命令也能写脚本。语句形式如下:
probe_description /predicate/ {action} action是一系列以分号间隔的语句,当探针触发时执行。predicate是可选的过滤表达式。

例句:proc:::exec-success /execname=="httpd"/{trace(pid);}

解读:如果进程名是"httpd",会跟踪proc provider中的exec-success探针并执行trace(pid)这一action。exec-success探针常常用于跟踪新进程的创建和系统调用exec()的执行。当前的进程名是用内置变量execname检索出来的,而当前的进程ID则是通过pid。

内置变量

内置变量是用来做计算和判断的,可以通过action打印出来。常用内置变量如下表所示。

变量

描述

execname

执行在CPU上进程的名字(字符串)

uid

执行在CPU上的用户ID

pid

执行在CPU上的进程ID

timestamp

当前时间,自启动以来的纳秒数

vtimestamp

CPU上的线程时间,单位是纳秒

arg0..N

探针参数(uint64_t)

args[0]..[N]

探针参数(类型化的)

curthread

指向当前线程内核结构的指针

probefunc

探针描述(字符串)的函数组件

probename

探针描述(字符串)的命名组件

curpsinfo

当前进程信息

action

常用的action见下表

action

描述

trace(arg)

打印arg

printf(format, arg, ...)

打印格式化的字符串

stringof(addr)

返回来自内核空间的字符串

copyinstr(addr)

返回来自用户空间地址的字符串(需要内核执行一次从用户空间到内核空间的复制操作)

stack(count)

打印内核级别的栈跟踪,如果有count,按count截断

ustack(count)

打印用户级别的栈跟踪,如果有count,按count截断

func(pc)

从内核程序计数器(pc),返回内核函数名

ufunc(pc)

从用户程序计数器(pc),返回用户函数名

exit(status)

退出DTrace并返回状态

trunc(@agg, count)

截断聚合变量,或是全部(删除所有的键),或按照指定键的数目(count)做截断

clear(@agg)

删除聚合变量的值(键保留)

printa(format, @agg)

格式化地打印聚合变量

最后三个action使用一种特殊的称为聚合型(aggregation)的变量类型。

变量类型

变量类型表如下,按照使用偏好排列(先是聚合变量,然后按开销从低到高)。

类型

前缀

作用域

开销

多CPU安全

赋值示例

聚合变量

@

全局



@x=count();

带键的聚合变量

@[]

全局



@x[pid]=count();

从句局部变量

this->

从句实例

非常低


this->x=1;

线程局部变量

self->

线程内

中等


self->x=1;

标量


全局

中下


x=1;

关联数组


全局

中上


x[y]=1;

解读:

  • 线程局部变量的作用域是在线程内;
  • 子句局部变量用于中间计算,只在针对同一探针描述的action子句中有效;
  • 多个CPU同时对同一个标量做写入会损坏变量状态,这不大可能,但确实会发生,对于字符串标量也要同样小心(字符串损坏)。

聚合变量(aggregation)是一类特殊的变量类型,可以由CPU单独计算汇总之后再传递到用户空间。该变量类型拥有最低的开销,是另一种数据汇总的方法。

用于填充聚合变量的action见下表

聚合Action

描述

count()

发生计数

sum(value)

对value求和

min(value)

记录value的最小值

max(value)

记录value的最大值

quantize(value)

用2的幂次方直方图记录value

lquantize(value, min, max, step)

用给定最小值、最大值和步进值做线性直方图记录value

llquantize(value, factor, min_magnitude, max_magnitude, steps)

用混合对数/线性直方图记录value

单行命令

跟踪系统调用open(),打印进程名和文件路径名:
dtrace -n 'syscall::open:entry { printf("%s %s", execname, copyinstr(arg0)); }'

Oracle Solaris 11很大程度上修改系统调用自陷表(系统调用的provider是构建在此之上的),这样在该系统上跟踪open()就变成:
dtrace -n 'syscall::openat:entry { printf("%s %s", execname, copyinstr(arg1)); }'

按进程名归纳所有CPU的交叉调用:
dtrace -n 'sysinfo:::xcalls { @[execname] = count(); }'

按99Hz采样内核级栈:
dtrace -n 'profile:::profile-99 { @[stack()] = count(); }'

脚本

例如来自DTraceToolkit脚本集合的脚本bitesize.dGitHub,根据进程名显示请求的磁盘I/O大小:

#!/usr/sbin/dtrace -s  /* 解释器行,表示脚本文件可从命令行执行 */
#pragma D option quiet  /* 设置安静模式,压缩DTrace的默认输出 */
/* 打印头 */
dtrace:::BEGIN
{
   printf("Tracing... Hit Ctrl-C to end.\n");
}
/* 进程io启动 */
io:::start
{
   /* fetch details */
   this->size = args[0]->b_bcount;
   /* store details */
   @Size[pid, curpsinfo->pr_psargs] = quantize(this->size);
}
/* 打印最终报告 */
dtrace:::END
{
   printf("\n%8s %s\n", "PID", "CMD");
   printa("%8d %S\n%@d\n", @Size);
}

开销

DTrace通过利用每个CPU的内核缓冲区和内核的聚合总结,减小跟踪的开销。默认状态下,DTrace以每秒一次这样一个温和的频率,从内核空间往用户空间传递数据。还有其他减小开销和提高安全性的各种功能,比如有的例程如果发现系统不能响应就会终止跟踪。

跟踪执行的开销是与跟踪的频率和所执行的action息息相关的。跟踪块设备I/O的频率通常不高(1000 I/O每秒或更少),开销是可以忽略的。另一方面,跟踪网络I/O时,当包的速率达到每秒百万次时,就会引起显著的开销。

action也是有代价的。

把数据存入变量也是有开销的,特别是关联数组。

文档和资源

书:

  • Dynamic Tracing Guide
  • DTrace: Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD

资源:

基于DTrace封装的工具:

  • execsnoop
  • Oracle的ZFS Appliance Analytics
  • Joyent公司的Cloud Analytics

SystemTap

SystemTap是专为Linux打造的,对用户级和内核级的代码都提供静态和动态跟踪的功能,采用其他的内核框架做源:静态探针用tracepoints、动态探针用kprobes、用户级别的探针用uprobes。

不足:稳定性问题,一些版本会导致内核崩溃或挂起;启动较慢、错误信息难懂、隐式功能无文档、语言还不够精炼。

将DTrace移植到Linux:Oracle的Oracle Enterprise Linux。

探针

探针:由句号分隔,可选的内置选项(放在括号中),如:

  • begin:程序开始;
  • end:程序结束;
  • syscall.read:系统调用read()的开始;
  • syscall.read.return:系统调用read()的结束;
  • kernel.function(“sys_read”):内核函数sys_read()的开始;
  • kernel.function(“sys_read”).return:内核函数sys_read()的结束;
  • socket.send:发送包;
  • timer.ms(100):对单一CPU每100ms触发一次的探针;
  • timer.profile:按内核时钟频率对所有的CPU都触发的探针,用于采样/剖析;
  • process(“a.out”).statement(“*@main.c:100”):跟踪目标进程,可执行文件a.outmain.c的第100行。

tapset

tapset:一组相关的探针。tapset还能附带用来执行action。tapset举例:

  • syscall:系统调用;
  • ioblock:块设备接口和I/O调度器;
  • scheduler:内核CPU调度器事件;
  • memory:进程和虚拟内存的使用;
  • scsi:SCSI目标的事件;
  • networking:网络设备事件,包括接收和传输;
  • tcp:TCP协议事件,包括发送和接收事件;
  • socket:套接字事件。

action和内置变量

SystemTap还提供许多的action和内置变量:

  • execname():可获取进程名;
  • pid():可获取当前进程ID;
  • print_backtrace():可打印内核栈的回溯信息。

示例

stap -ve 'global stats; probe syscall.read.return { stats <<< $return; }
   probe end { printf("\n\trval (bytes)\n"); print(@hist_log(stats)); }'

解读:

  • 单行命令跟踪系统调用read(),将返回读取的大小结果保存成一张2的幂次方的直方图;
  • 选项-v会打印出编译阶段的详细信息,通知用户跟踪已经开启;
  • 单行命令以声明全局变量stats作为开始,这是SystemTap所要求的预声明;
  • 探针的定义用关键词probe作为开头,匹配系统调用read()的返回;
  • action是用变量stats记录返回值$return的,用的统计操作符是<<<。采用这样通用的数值记录方式,之后可以用于不同的数据汇总中;
  • end探针把统计数据作为直方图打印出来;
  • 如果不输出直方图,SystemTap在退出时也会打印一个基本的数字总结;
  • read()的$return值有些是负值,这代表的是返回的错误码errno。

SystemTap对比DTrace

两条等价的单行命令:

stap -e 'global stats; probe syscall.read.return { stats <<< $return; }
   probe end { printf("\trval (bytes)\n"); print(@hist_log(stats)); }'
dtrace -n 'syscall::read:return { @["rval(bytes)"] = quantize(arg0); }'

SystemTap脚本举例:

stap -e 'global s; probe syscall.read.return {
   if ($return >= 0) { s[execname()] <<< $return; }
}
probe end {
   printf("\n%-36s %8s %8s %10s\n", "EXEC", "CALLS", "AVGSZ", "TOTAL");
   foreach(k in s+) {
      printf("%-36s %8d %8d %10d\n", k, @count(s[k]), @avg(s[k]), @sum(s[k]));
   }
} '

解读:这个单行命令根据进程名给出读操作返回大小的统计数据。用三个不同的函数给调用次数CALLS、平均大小AVGSZ(字节)和总大小TOTAL提供数据。若用DTrace来做这件事,需要三个不同的聚合变量,一种类型一个。

区别:

  • DTrace不提供if,用谓词作为分支;
  • DTrace当前没有循环能力,除了展开循环,出于安全考虑不执行回跳;SystemTap 为循环设置上界,脚本里的无限循环不会在内核上下文里挂起;
  • SystemTap可直接访问统计数值,如s[k],而DTrace的聚合变量的打印只能依靠自己或用聚合函数来处理。

开销

当程序首次执行时,SystemTap在编译阶段会消耗几秒CPU资源。SystemTap会将程序缓存下来,这样开销不会每次使用时都发生。还可以在不同的系统上编译SystemTap程序,然后将缓存的结果传输给目标系统。

另一个额外开销是内核分析的内核调试信息,通常不包括在Linux的发行版中(可能是几百兆字节大小)。

文档和资源

书:SystemTap Language Reference。

perf

LPE,Linux Performance Events,Linux性能事件,简称perf,现在所支持的性能观测的范围已相当宽泛。虽然没有DTrace和SystemTap那样的实时编程能力,但perf可执行静态和动态跟踪(基于tracepoint、kprobe和uprobe)、profiling、检查栈跟踪、局部变量和数据类型。已成为Linux内核主线的一部分,最容易使用的(如果已安装),所提供的观测能力足够满足大多数问题排查。

观测工具的观测

观测工具和构建其上的统计都是由软件实现的,而所有的软件都是潜在有Bug的。存在以下问题:

  • 工具不总是正确的;
  • Man手册页不总是正确的;
  • 能用的指标可能不完整;
  • 能用的指标可能设计得很差;

当多个观测工具覆盖的范围有重叠时,就能用它们来互相检查。

另一个验证的技术是施加已知的负载,看看观测工具表现得是否与你预计的结果相同。可以用微基准测试工具,用它们的报告结果做比较。

缺少指标比用不合适的指标更难发现。

练习

  • 什么是剖析?
  • 什么是跟踪?
  • 静态跟踪和动态跟踪有什么区别?