Linux性能分析工具Perf简介 Linux性能分析工具Perf简介 介绍 Perf是一个基于Linux 2.6 +系统的分析工具,它抽象了在Linux中性能度量中CPU的硬件差异 ,提供一个简单的命令行界面。 Perf基于最新版本Linux内核 的接口。 这篇文章通过示例展示了 Perf工具的使用。 输出结果在Ubuntu 11.04(内核版本2.6.38-8-generic)上获得,硬件是在使用双核英特尔Core2 T7100 CPU的惠普6710 b。 为了可读性,一些输出使用省略号( )。 命令 Perf工具提供了一组丰富的命令来收集和分析性能和跟踪数据。 命令行的用法与类似,通过一个通用的命令Perf,实现了一组子命令: , , ,[…] 支持的命令列表: 某些需要特定内核支持的命令可能无法使用。如果想获得每个子命令的具体选项列表,只需输入命令名紧随其后 : 事件 Perf工具支持一系列的可测量事件。这个工具和底层内核接口可以测量来自不同来源的事件。 例如,一些事件是纯粹的内核计数,在这种情况下的事件被称为软事件 ,例如:context-switches、minor-faults。 另一个事件来源是处理器本身和它的性能监视单(PMU)。它提供了一个事件列表来测量微体系结构的事件,如周期数、失效的指令、一级缓存未命中等等。 这些事件被称为PMU硬件事件或简称为硬件事件。 它们因处理器类型和型号而异。 接口还提供了一组通用的的硬件事件名称。在每个处理器,这些事件被映射到一个CPU的真实事件,如果真实事件不存在则事件不能使用。可能会让人混淆,这些事件也被称为硬件事件或硬件缓存事件 。 最后,还有由内核ftrace基础实现的tracepoint事件。但只有2.6.3x和更新版本的内核才提供这些功能。 可以通过命令获得可支持的事件列表: 一个事件可以有子事件(或掩码)。 在某些处理器上的某些事件,可以组合掩码,并在其中一个子事件发生时进行测量。最后,一个事件还可以有修饰符,也就是说,通过过滤器可以改变事件被计数的时间或方式。 硬件事件 PMU硬件事件取决与特定的CPU,由CPU供应商提供文档。如果将Perf工具与libpfm4库链接,则可以提供事件的一些简短描述。有关Intel和AMD处理器的PMU硬件事件的列表,请参阅 英特尔PMU事件表:手册的附录A 在这里 AMD PMU事件表:手册的3.14节 在这里 使用perf stat进行统计 对于任何支持的事件,Perf可以在进程运行期间持续计数。 在统计模式下,在应用程序运行结束时事件的发生会被简单地汇总并显示在标准输出上。去产生这些统计数据,使用 命令的Perf。 例如: 如果没有指定事件,会收集上面列出的常见事件。一些是软事件例如context-switches,另一些是通用硬件事件例如cycles。在哈希符号之后,可以显示衍生指标,例如“ IPC”(每个周期的指令)。。 事件控制选项 Perf工具可以测量的一个或多个事件。事件使用其符号名称指定,后可选跟随掩码和修饰符。事件名称、掩码和修饰符不区分大小写。 默认情况下,事件同时测量用户和内核级别: 如果测量仅在用户级别,有增加一个修饰词: 同时测量用户和内核(显式): 修饰符 事件可以通过冒号添加一个或多个修饰符。 修饰符允许用户对事件计数进行限制。 测量PMU事件,通过下示修饰符: 在这个例子中,我们测量用户级别的指令数量。 注意,对于真实事件,修饰符取决于底层的PMU模型。 修饰符可以随意组合。 这张简单的表格,总结了用于Intel和AMD x86处理器的最常见的修饰符。 修饰符 描述 例子 u priv 3,2,1级别监控(用户) event:u k priv 0级别监控(内核) event:k h 在虚拟化环境中监视监控程序事件 event:h H 在虚拟化环境中监视主机 event:H G 在虚拟化环境中监视访客机 event:G 以上所有修饰符均视为布尔值(标志)。 硬件事件 要测量硬件供应商文档提供的实际PMU,请传递十六进制参数代码: 多个事件 要测量多个事件,只需提供一个用逗号分隔的列表,其中没有空格: 理论上,对事件的数量是没有限制的。如果事件多余实际硬件计数器时,内核会自动多路复用。软事件的数量没有限制。你可以同时测量来自不同来源的事件。 然而,如果每个事件使用一个文件描述符,在per-thread(per-thread模式)或per-cpu(系统范围)模式下,则可能达到内核限制的每个进程的最大文件描述符数。在这种情况下,perf将报告一个错误。有关此问题的帮助,请参阅故障排除部分。 事件的多路复用和缩放 如果事件多于计数器,则内核会使用时间多路复用(开关频率= HZ,通常为100或1000)为每个事件提供访问监视硬件的机会。复用仅适用于PMU事件。使用多路复用时,不会一直测量事件。运行结束时,该工具会根据启用的总时间与运行时间来缩放计数。实际公式为: 如果在整个运行过程中都对事件进行了测量,则可以估算该计数是多少。理解这是一个估计值而不是实际计数非常重要。工作负载较重时会有测量丢失,这种情况会在缩放时引入错误。 目前事件以循环方式进行管理,因此每个事件最终都将有机会运行。如果有N个计数器,则循环列表中最多前N个事件被编程到PMU中。在某些情况下它可能小于该值,因为某些事件可能无法一起测量或与它们使用同一计数器。此外,接口允许多个工具同时测量同一线程或CPU。每个事件都添加到相同的循环队列中。不能保证工具的所有事件都顺序存储在队列中。 为了避免缩放(在只有一个活动用户),你可以试着减少事件的数量。下表为一些常见的处理器提供计数器的数量: 处理器 通用的计数器 固定的计数器 英特尔酷睿 2 3 英特尔Nehalem 4 3 通用计数器可以测量任何事件,固定计数器只能测量一个事件。一些计数器可能是用于特殊用途,如看门狗定时器。 下面的例子显示缩放的影响: 在这里,没有多路复用,因此没有缩放。 让我们再添加一个事件: 有多路复用,从而进行缩放。尝试保始终将事件A和B一起测量的方式非常有趣。尽管内核接口提供了对事件分组的支持,但当前的Perf工具没有。 重复测量 可以使用多次运行相同的测试工作,并针对每个计数均值的标准偏差。 在这里,运行5次,并打印每个事件的平均计数以及std-dev/mean的比率。 环境控制选项 Perf工具可用于统计每个线程、每个进程、每个cpu或整个系统的事件。在per-thread模式下,计数器只监视指定线程的执行。当线程被调度出时,监视将停止。当线程从一个处理器迁移到另一个处理器时,计数器在当前处理器上保存,并在新处理器上还原。 per-process模式是per-thread的一个变体,进程中的所有 线程都被监控。计数和采样在进程级别被合计。 perf_events接口允许自动继承和。 默认情况下,Perf工具使能继承。 在per-cpu的模式下,指定处理器上所有线程都被监控。计数和采样在每个CPU上合计。一个事件一次只能监视一个CPU。如果跨多个处理器进行监控,则需要创建多个事件。Perf工具可以统计跨多个处理器计数和采样。它也可以只监视一个部分处理器。 计数和继承 默认情况下, 统计进程的所有线程和后续的子进程和线程。这可以使用 选项进行切换。但它无法获得per-thread和per-process的详细计数。 Processor-wide模式 默认情况下, 使用per-thread计数模式。 要按per-cpu计算,请使用选项。当选项被设置时,所有在线处理器都会被监视,并且计数会被合计。例如: 测量收集了所有CPU上的事件周期和指令。测量的持续时间由dd的执行决定。换句话说,此测量捕获dd进程的执行以及所有cpu上在用户级别以外运行的任何内容。 若要计时测量的持续时间而不消耗周期,可以使用命令: 可以使用选项限制监视cpu的一个子集。可以传递要监视的CPU列表。例如,要在CPU0、CPU2和CPU3上测量: 演示机只有两个CPU,但我们可以限制为CPU 1。 计数在所有被监视的CPU上合计。注意,当测量单个CPU时,计数的周期和指令的数量是减半的。 连接到一个正在运行的进程 可以使用Perf连接到已经运行的线程或进程。 这需要具有附加线程或进程ID的权限。若要附加到进程,选项必须是进程ID。若要附加到通常在许多Linux计算机上运行的sshd服务,使用: 决定测量持续时间的是要执行的命令。即使我们附加到进程,我们仍然可以传递命令的名称。它用于计算测量时间。没有它,Perf将一直监视,直到它被杀死。还要注意,附加到进程时,将监视该进程的所有线程。此外,假设继承在默认情况下处于打开状态,子进程或线程也将被监视。要关闭此功能,必须使用选项。可以在进程中附加特定线程。所谓线程,我们指的是内核可见线程。换句话说,通过ps或top命令可见的线程。要附加到线程,必须使用选项。我们看一下rsyslogd,因为它总是在Ubuntu 11.04上运行,有多个线程。 在本例中,线程932在测量的2s期间没有运行。否则,我们将看到一个计数值。附加到内核线程是可能的,但实际上并不推荐这样做。考虑到内核线程倾向于固定到特定的CPU,最好使用cpu-wide模式。 控制输出选项 可以修改输出以满足不同的需求。 大数字输出 对大多数人来说,很难读懂很大的数字。使用,可以使用逗号分隔符打印数千个大数字(美式)。为此,必须设置选项和设置正确的语言环境。如上面的例子所示,Ubuntu已经正确地设置了语言环境信息。显式调用如下所示: 机器可读的输出 还可以打印计数,格式可以很容易地导入到电子表格或由脚本进行解析。选项改变输出的格式,并允许用户传递分隔符。这使得很容易生成CSV样式的输出: 请注意,选项与不兼容。 使用Perf记录采样 perf工具可用于收集per-thread、per-process和per-cpu的性能数据。 有几个与采样相关的命令:record、report、annotate。必须首先使用收集样本。这将生成一个名为的输出文件。然后,可以使用和命令分析该文件(可能在另一台计算机上)。该方式类似于OProfile。 基于事件的采样 Perf_events基于基于事件的采样。周期表示为事件发生的次数,而不是计时器计时的次数。当采样计数器溢出时,即从2^64换回0时,记录采样。PMU没有实现64位硬件计数器,但perf_events在软件中模拟该计数器。 perf_events模拟64位计数器的方式仅限于使用实际硬件计数器中的位数来表示采样周期。在小于64位的情况下,内核会自动截断周期。因此,如果在32位系统上运行,最好周期始终小于2^31。 在计数器溢出时,内核记录有关程序执行的信息,也就是采样。记录的内容取决于测量的类型。这都是由使用者和工具指定的。但一般来说,样本中的关键信息是指令指针,即时程序中断在哪里。 基于中断的采样在现代处理器上引入了skid。这意味着每个采样的指令指针指向程序处理PMU中断的位置,而不是计数器实际溢出的位置,即它在采样周期结束时的位置。在某些情况下,如果有分支,这两个点之间的距离可能是几十条或更多的指令。当程序不再向前运行时,这两个位置确实是相同的。因此,在解释分析数据时必须小心。 默认事件:时钟周期计数 默认情况下, 使用时钟周期事件做为抽样事件。这是由内核映射到特定PMU事件的一个通用的硬件事件。对于英特尔来说,映射到 。在CPU频率扩展的情况下,此事件在时间上不能保持恒定不变。英特尔提供了另一个名为的事件,但此事件当前不适用于perf_events。 在AMD系统中,事件映射到 事件,这个事件也受到频率扩展的影响。 在任何英特尔或AMD处理器,周期事件在处理器空闲时不计数,例如当它调用 。 时间和速度 接口允许两种模式表达采样周期: 事件发生的次数(时间) 样本/秒的平均速率(频率) Perf工具默认使用平均速率。它设置为1000 hz,或1000样本/秒。 这意味着内核会动态调整采样周期以达到目标平均速率。周期内的调整会在原始的分析数据中报告。与此相反,与其他模式相比,采样周期由用户设置,并且在采样之间不发生变化。目前不支持随机采样周期。 收集样本 默认情况下,在运行在per-thread模式下,并且开始继承模式。当执行一个繁忙循环的简单程序时,简单的使用如下: 上面的示例以1000Hz的平均目标速率收集事件周期的样本。生成的示例将保存到文件中。如果文件已经存在,可能会提示您通过覆盖它。要将结果放入特定文件中,请使用选项。 警告:报告的样本数只是估计值。它没有反映实际采集的样本数量。此估计基于写入文件的字节数和最小样本大小。但每个样本的真正大小取决于测量的类型。一些样本由计数器本身生成,而另一些样本则与后处理期间支持符号相关,例如信息。 要文件的准确样本数,可以使用命令: 指可使用 选项自定义采样速度。 例如,仅在用户级别对事件指令进行采样,并且使用250个样本/秒的平均速率: 要指定采样周期,可以使用选项。例如,仅在用户级别收集每2000次事件指令的采样,请执行以下操作: Processor-wide模式 在per-cpu模式下,收集受监控cpu上执行的所有线程的样本。要在per-cpu模式下切换,需要使用选项。默认情况下,在此模式下,所有联机CPU都被监视。正如上面所解释的,可以使用选项限制到CPU的一个子集。 要在所有CPU上以1000个样本/秒的平均目标速率对用户和内核级别的周期采样5秒: 使用perf report分析采样 收集的样本会保存到一个二进制文件中,默认情况下,该文件名为。命令读取此文件并生成简明的执行概要文件。默认情况下,样本按函数排序,样本数最多的优先。可以自定义排序顺序,从而以不同的方式查看数据。 “Overhead”列指示在相应函数中收集的总样本的百分比。第二列显示被收集样本的进程。在per-thread/per-process模式下,这始终是受监视命令的名称。但在cpu-wide模式下,命令可能会有所不同。第三列显示了样本来源的ELF镜像的名称。如果程序是动态链接的,则这可能会显示共享库的名称。当样本来自内核时,使用伪ELF镜像名[kernel.kallsyms]。第四列指示样本运行的级别,也就是程序被中断时正在运行的级别: 最后一列显示了符号名称。 样本可以使用多种方式进行呈现,即使用排序。例如按共享对象进行排序,使用dso: 输出控制选项 为使输出更容易解析,可以修改列分隔符为某一个字符: 内核报告控制选项 Perf工具不知道如何从压缩内核映像(vmlinuz)中提取符号。因此,用户必须将非压缩内核镜像的路径通过 传递给Perf: 当然,内核镜像只有带debug符号编译的才能工作。 Processor-wide模式 在per-cpu的模式中,会从监控CPU上的所有线程上记录的样本。 这样,我们可以收集来自许多不同进程的样本。例如,如果我们监视所有CPU 5s: 当符号打印为十六进制地址时,这是因为ELF镜像没有符号表。当二进制文件被剥离时就会发生这种情况。我们也可以按cpu排序。这可能有助于确定工作负载是否平衡: 计算开销 perf收集调用链时,开销可以在两列中显示为“Children”和“Self”。“self”开销只是通过所有入口(通常是一个函数,也就是符号)的所有周期值相加来计算的。这是perf传统显示方式,所有“self”开销值之和应为100%。 “children”开销是通过将子函数的所有周期值相加来计算的,这样它就可以显示更高级别函数的总开销,即使它们不直接参与更多的执行。这里的“Children”表示从另一个(父)函数调用的函数。 所有“children”开销值之和超过100%可能会令人困惑,因为它们中的每一个已经是其子函数的“self”开销的累积。但是如果启用了这个功能,用户可以找到哪一个函数的开销最大,即使样本分布在子函数上。 考虑下面的例子,有三个函数如下所示。 在本例中,“foo”是“bar”的子级,“bar”是“main”的直接子级,因此“foo”也是“main”的子级。换句话说,“main”是“foo”和“bar”的父级,“bar”是“foo”的父级。 假设所有样本都只记录在“foo”和“bar”中。当使用调用链记录时,输出将在的常规(仅自开销)输出中显示如下内容: 启用选项时,子函数(即’foo’和’bar’)的’self’开销值将添加到父函数中,以计算’children’开销。在这种情况下,报告可以显示为: 在上述输出中,“foo”的“self”开销(60%)被添加到“bar”、“main”和“__libc_start_main”的“children”开销中。同样,“bar”的“self”开销(40%)添加到“main”和“libc”的“children”开销中。 因此,首先显示’__libc_start_main’和’main’,因为它们有相同(100%)的“子”开销(即使它们没有“自”开销),并且它们是’foo’和’bar’的父级。 从v3.16开始,默认情况下会显示“children”开销,并按其值对输出进行排序。通过在命令行上指定选项或在perf配置文件中添加“report.children=false”或“top.children=false”,禁用“children”开销。 使用perf annotate分析源码 可以使用深入到指令级分析。为此,需要使用要解析的命令的名称调用。所有带样本的函数都将被反汇编,每条指令都将报告其样本的相对百分比: 第一列报告在该指令在捕获函数noploop()的样本百分比。如前所述,您应该仔细解读这些信息。 如果使用编译应用程序,可以生成源代码级信息。下面的代码片段显示了在使用此调试信息编译noploop时,同一次执行noploop时的更多信息输出。 使用perf annotate分析内核 Perf工具不知道如何从压缩内核镜像(vmlinuz)中提取符号。正如中的示例,用户必须通过传递非压缩内核镜像的路径: 在一次说明,这只使用带debug符号编译的内核。 使用perf top进行现场分析 perf工具可以以类似于Linux top工具的模式运行,实时打印采样函数。默认的采样事件是cycles,默认的顺序是每个符号的采样数递减,因此显示了花费大部分时间的函数。默认情况下,以processor-wide模式运行,在用户和内核级别监视所有在线的CPU。使用选项可以只监视CPU的一个子集。 默认情况下,第一列显示自运行开始以来的总样本数。通过按“Z”键,可以将其更改为打印自上次刷新以来的样本数。当处理器不处于暂停状态(即不空闲)时,cycle事件也会统计CPU周期。因此,这不等于墙面时间。此外,事件还受频率扩展的影响。 也可以深入到单个函数中,查看哪些指令具有最多的样本。要深入到指定函数,请按“s”键并输入函数名。这里我们选择了顶部函数noploop(上面没有显示): 使用perf bench进行基准测试 命令包含多个多线程微内核基准测试,用于在Linux内核和系统调用中执行不同的子系统。这使得黑客可以轻松地测量更改的影响,从而帮助缓解性能衰退。 它还充当一个通用的基准框架,使开发人员能够轻松地创建测试用例、透明进行整合和使用富性能工具子系统。 sched:调度器基准测试 测量多个任务之间的pipe(2)和socketpair(2)操作。允许测量线程与进程上下文切换的性能。 mem:内存访问基准测试 numa: numa调度和MM基准测试 futex: futex压力基准测试 处理futex内核实现的细粒度方面。它对于内核黑客非常有用。它目前支持唤醒和重新排队/等待操作,并强调私有和共享futexes的哈希方案。下面时nCPU线程运行的一个示例,每个线程处理1024个futex来测量哈希逻辑: 故障诊断和建议 本节列出了很多建议来避免使用Perf时常见的陷阱。 打开文件的限制 Perf工具所使用的内核接口的设计是这样的:它为per-thread或per-cpu的每个事件使用一个文件描述符。 在16-way系统上,当您这样做时: 您实际上创建了16个事件,从而消耗了16个文件描述符。 在per-thread模式下,当您在同一16-way系统上对具有100个线程的进程进行采样时: 然后,一旦创建了所有的线程,您将得到个文件描述符。Perf在每个CPU上创建一个事件实例。只有当线程在该CPU上执行时,事件才能有效地度量。这种方法加强了采样缓冲区的局部性,从而减少了采样开销。在运行结束时,该工具将所有样本合计到一个输出文件中。 如果Perf因“打开的文件太多”错误而中止,有以下几种解决方案: 使用ulimit-n增加每个进程打开的文件数。注意:您必须是root 限制一次运行中测量的事件数 限制正在测量的CPU数量 增加打开文件限制 超级用户可以更改进程打开的文件限制,使用 ulimit shell内置命令: 使用build-id表示二进制文件 命令在中保存者与测量相关的所有ELF镜像的唯一标识符。在per-thread模式下,这包括被监视进程的所有ELF镜像。在cpu-wide模式下,它包括系统上运行的所有进程。如果使用选项,则链接器将生成这些唯一标识符。因此,它们被称为。当将指令地址与ELF映像关联时,是一个非常有用的工具。要提取文件中使用的所有生成id项,请发出: build-id缓存 每次运行结束时,命令都会更新一个缓存,其中包含带有样本的ELF镜像的新条目。缓存包含: 带样本的ELF镜像的 带有样本的ELF镜像的副本 给定的是不可变的,它们唯一地标识二进制文件。如果重新编译二进制文件,将生成新的,并在缓存中保存ELF图像的新副本。缓存保存在磁盘上的默认目录$HOME/.debug中。系统管理员可以使用全局配置文件/etc/perfconfig为缓存指定备用全局目录: 在某些情况下,关掉 缓存更新可能时有益的。为此,你需要使用的 选项 访问控制 对于某些事件,必须是root才能调用perf工具。本文档假定用户具有root权限。如果您试图在权限不足的情况下运行perf,它将报告 其他场景 分析睡眠时间 此功能显示程序在何处睡眠或等待某物的时间和时间。 第一步是收集数据。我们需要收集t和事件。事件是不够的,因为它们是在任务的上下文中生成的,这会唤醒目标任务(例如释放锁)。我们需要相同的事件,但带有目标任务的调用链。此调用链可以从之前的事件中提取。 第二步是合并和事件。这可以通过“perf-inject-s”来完成。 参考文档: Linux kernel profiling with perf
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/47500.html