收集器可收集三种不同类型的数据:分析数据、跟踪数据和全局数据。
可通过以固定的间隔记录分析事件来收集分析数据。该间隔可以是使用系统时钟获取的时间间隔,也可以是特定类型硬件事件的数目。间隔时间结束时,会向系统传送一个信号,并在下一个间隔记录数据。
可通过在各种系统函数上插入包装函数来收集跟踪数据,以便拦截对系统函数的调用,并记录有关调用的数据。
可通过调用各种系统例程以获取信息来收集全局数据。全局数据包称为样本。
分析数据和跟踪数据都包含有关特定事件的信息,并且这两种类型的数据都会转换为性能度量。全局数据不会转换为度量,而是用于提供标记器,这些标记器可用于将程序执行划分为很多时间段。通过全局数据,可以了解该时间段内程序执行的总体情况。
每个分析事件或跟踪事件收集的数据包都包含以下信息:
标识数据的数据包头
高精度的时间戳
线程 ID
轻量级进程 (lightweight process, LWP) ID
处理器 (CPU) ID,在操作系统中可用时
调用栈的副本。对于 Java 程序,将记录两个调用堆栈:计算机调用堆栈和 Java 调用堆栈。
对于 OpenMP 程序,还会收集当前并行区域的标识符和 OpenMP 状态。
有关线程和轻量级进程的更多信息,请参见第 6 章。
除了通用数据外,每个事件特定的数据包还包含特定于数据类型的信息。收集器可以记录的五种数据类型为:
时钟分析数据
硬件计数器溢出分析数据
同步等待跟踪数据
堆跟踪(内存分配)数据
MPI 跟踪数据
以下几个小节将介绍这五种数据类型(度量即是根据这五种数据类型得出的),以及如何使用这些数据类型。第六种数据类型(全局抽样数据),无法转换为度量,因为它不包含调用栈信息。
进行基于时钟的分析时,收集的数据取决于操作系统所提供的度量。
在 Solaris OS 下基于时钟的分析中,将按固定的时间间隔存储每个 LWP 的状态。这种时间间隔称为分析间隔。这些信息存储在一个整数数组中:数组的一个元素用于内核维护的十个微记帐状态中的每一个状态。收集的数据通过性能分析器转换为每个状态所用的时间和分析间隔的精度。缺省分析间隔约为 10 毫秒 (10 ms)。收集器提供的高精度分析间隔大约为 1 ms,低精度分析间隔大约为 100 ms,如果 OS 允许,则可使用任意的间隔。运行 collect 命令(不带任何参数)可列显运行该命令的系统所允许的范围和精度。
表 2–1 Solaris 计时度量
度量 |
定义 |
---|---|
用户 CPU 时间 |
在 CPU 中按用户模式运行所用的 LWP 时间。 |
挂钟时间 |
LWP 1 中所用的 LWP 时间。该时间通常称为“挂钟时间”。 |
总 LWP 时间 |
全部 LWP 时间的总和。 |
系统 CPU 时间 |
在 CPU 中或陷阱状态下按内核模式运行所用的 LWP 时间。 |
等待 CPU 时间 |
等待 CPU 所用的 LWP 时间。 |
用户锁定时间 |
等待锁定所用的 LWP 时间。 |
文本缺页时间 |
等待文本页所用的 LWP 时间。 |
数据缺页时间 |
等待数据页所用的 LWP 时间。 |
其他等待时间 |
等待内核页所用的 LWP 时间,或休眠/ 停止所用的时间。 |
对于多线程实验,将计算所有 LWP 的时间(挂钟时间除外)的总和。所定义的挂钟时间对于多程序多数据 (multiple-program multiple-data, MPMD) 程序没有意义。
计时度量按多种类别说明程序消耗时间的位置,并且可用于改善程序的性能。
高用户 CPU 时间说明程序处理大部分工作的位置。此外还可用于查找重新设计算法后可能受益最多的程序部分。
高系统 CPU 时间说明程序在对系统例程的调用中消耗了大量时间。
高等待 CPU 时间说明准备运行的线程数比可用的 CPU 多,或其他进程正在使用 CPU。
高用户锁定时间说明线程无法获得其请求的锁定。
高文本缺页时间意味着链接程序要求的代码会在内存中进行组织,所以很多调用或分支会导致装入新的页面。
高数据缺页时间表明对数据的访问会导致新的页面被装入。重新组织程序的数据结构或算法可以修复此问题。
在 Linux OS 下,唯一可用的度量是用户 CPU 时间。虽然报告的总 CPU 占用时间是准确的,但分析器不可能像在 Solaris OS 中那样准确地确定实际系统 CPU 时间的时间比例。虽然分析器显示的信息好像是轻量级进程 (lightweight process, LWP) 数据,但实际上 Linux OS 中没有 LWP 的数据;所显示的 LWP ID 实际上是线程 ID。
可以在用 Oracle Message Passing Toolkit(以前称为 Sun HPC ClusterTools)运行的 MPI 实验上收集时钟分析数据。Oracle Message Passing Toolkit 必须至少为版本 8.1。
如果将 Linux 与 Oracle Message Passing Toolkit 8.2 或 8.2.1 一起使用,可能另外需要一种解决方法。对于版本 8.1 或 8.2.1c,不需要解决方法;或者如果使用 Oracle Solaris Studio 编译器,对于任何版本都不需要使用解决方法。有关解决方法,请参见 docs.sun.com 上 Oracle Solaris Studio 12.2 Collection - Simplified Chinese 中的文档《Oracle Solaris Studio 12.2 的新增功能》。
在 MPI 实验上收集时钟分析数据时,可以显示两个其他度量:
MPI 工作,这种度量将在进程正在 MPI 运行时内执行工作(如处理请求或消息)时进行累积。
MPI 等待,这种度量将在进程正在 MPI 运行时内等待事件、缓冲区或消息时进行累积。
在 Solaris OS 上,以串行或并行方式执行工作时,“MPI 工作”会累积。在以下情况下“MPI 等待”会累积:MPI 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上调度线程时。
在 Linux OS 上,仅当进程在用户模式或系统模式下处于活动状态时,“MPI 工作”和“MPI 等待”才会累积。除非您已指定 MPI 应执行忙等待,否则,Linux 上的“MPI 等待”将没有用处。
如果对 OpenMP 程序执行基于时钟的分析,将提供以下两种附加度量:“OpenMP 工作”和“OpenMP 等待”。
在 Solaris OS 上,以串行或并行方式执行工作时,“OpenMP 工作”会累积。在以下情况下“OpenMP 等待”会累积:OpenMP 运行时正在等待进行同步时、该等待正在使用 CPU 时间或正在休眠时,以及正在以并行方式执行工作,但未在 CPU 上安排线程时。
在 Linux OS 上,仅当进程在用户模式或系统模式下处于活动状态时,“OpenMP 工作”和“OpenMP 等待”才会累积。除非您已指定 OpenMP 应执行忙等待,否则,Linux 上的“OpenMP 等待”将没有用处。
硬件计数器可跟踪诸如高速缓存未命中次数、高速缓存停止周期、浮点运算、分支误预测、CPU 周期以及执行指令之类的事件。在硬件计数器溢出分析中,当运行 LWP 的 CPU 的指定硬件计数器溢出时,收集器会记录分析数据包。计数器将重置并继续进行计数。分析数据包中包括溢出值和计数器类型。
各种 CPU 系列支持同时存在二到十八个硬件计数器寄存器。收集器可收集一个或多个寄存器上的数据。对于每个寄存器,收集器都允许您选择计数器的类型来监视溢出,并设置计数器的溢出值。有些硬件计数器可以使用任意寄存器,而有些计数器仅可以使用特定的寄存器。因此,在一个实验中并非可以选择所有的硬件计数器组合。
硬件计数器溢出分析数据由性能分析器转换为计数度量。对于以循环方式计数的计数器,所报告的度量会转换为次数;而对于不以循环方式计数的计数器,所报告的度量为事件计数。在具有多个 CPU 的计算机上,用于转换度量的时钟频率为各个 CPU 时钟频率的调和平均数。因为每种类型的处理器都有其自己的一组硬件计数器,并且硬件计数器的数目庞大,因此,此处未列出硬件计数器的度量。下一小节讲述如何找出可用的硬件计数器。
硬件计数器的一个用途是可诊断进出 CPU 的信息流问题。例如,高速缓存未命中次数计数较高表明,重新组织程序的结构来改进数据或文本的位置或提高高速缓存的重用率可以改善程序性能。
某些硬件计数器与其他计数器相互关联。例如,分支误预测和指令高速缓存未命中次数通常是相关的,因为分支误预测会导致将错误的指令装入到指令高速缓存,而这些指令必须替换为正确的指令。这种替换会导致指令高速缓存未命中,或指令转换后备缓冲器 (instruction translation lookaside buffer, ITLB) 未命中,或甚至缺页。
通常会在导致事件和相应事件计数器溢出的指令之后,向硬件计数器溢出传送一条或多条指令:这称为“失控 (skid)”,它会使计数器溢出分析数据难以解释。如果缺少对精确识别因果指令的硬件支持,可以对候选的因果指令尝试合适的回溯搜索。
收集期间支持和指定这种回溯时,硬件计数器分析数据包还包括适用于硬件计数器事件的候选内存引用指令的 PC(program counter,程序计数器)和 EA(effective address,有效地址)。(在分析期间需要进行后续处理来验证候选事件 PC 和 EA)。关于内存引用事件的这一附加信息为各种面向数据的分析(又称为数据空间分析)提供了方便。仅在运行 Oracle Solaris 操作系统的基于 SPARC 的平台上支持回溯。
也可以为时钟分析指定候选事件 PC 和 EA 的回溯和记录,尽管这可能难以解释。
由于硬件计数器是特定于处理器的,因此可以选用的计数器取决于所使用的处理器。性能工具为许多可能常用的计数器提供了别名。通过在特定系统上的终端窗口中键入不带任何参数的 collect,您可从收集器获得该系统上可用的硬件计数器列表。如果处理器和系统支持硬件计数器分析,则 collect 命令会列显两个包含有关硬件计数器信息的列表。第一个列表包含别名为通用名称的硬件计数器;第二个列表包含原始硬件计数器。如果性能计数器子系统和 collect 命令都不知道特定系统上的计数器名称,这些列表将为空。但是,在大多数情况下,可以用数值指定计数器。
以下示例显示了计数器列表中的条目。有别名的计数器将首先显示在列表中,然后是原始硬件计数器列表。该示例中的每一行输出都按打印格式显示。
Aliased HW counters available for profiling: cycles[/{0|1}],9999991 (’CPU Cycles’, alias for Cycle_cnt; CPU-cycles) insts[/{0|1}],9999991 (’Instructions Executed’, alias for Instr_cnt; events) dcrm[/1],100003 (’D$ Read Misses’, alias for DC_rd_miss; load events) ... Raw HW counters available for profiling: Cycle_cnt[/{0|1}],1000003 (CPU-cycles) Instr_cnt[/{0|1}],1000003 (events) DC_rd[/0],1000003 (load events) |
在有别名的硬件计数器列表中,第一个字段(例如,cycles)提供可以在 collect 命令的 -h counter... 参数中使用的别名。此别名还是在 er_print 命令中使用的标识符。
第二个字段列出计数器的可用寄存器,例如 [/{0|1}]。
第三个字段(例如 9999991)是计数器的缺省溢出值。对于有别名的计数器,选择的缺省值可提供合理的样本率。由于实际样本率变化相当大,因此可能需要指定缺省值以外的值。
第四个字段(在圆括号中)包含类型信息。它提供简短描述(例如 CPU Cycles)、原始硬件计数器名称(例如 Cycle_cnt)以及计数单位类型(例如 CPU-cycles)。
如果类型信息的第一个单词是:
load、store 或 load-store,则表明计数器与内存相关。您可以在 collect -h 命令中的计数器名称前放置一个 + 号(例如 +dcrm),以请求搜索引发事件的准确指令和虚拟地址。+ 号还可以启用数据空间分析;有关详细信息,请参见“数据对象”标签、“数据布局”标签和“内存对象”标签。
not-program-related,计数器会捕获由其他某个程序启动的事件,例如 CPU 到 CPU 的高速缓存嗅探。使用计数器进行分析时将生成警告,并且分析不记录调用栈。
如果类型信息的第二个单词或仅有的单词是:
CPU-cycles,则计数器可用于提供基于时间的度量。针对此类计数器报告的度量在缺省情况下会转换为独占时间和包含时间,但是也可以显示为事件计数。
events,则度量是包含和独占事件计数,且无法转换为时间。
在示例中的有别名的硬件计数器列表中,类型信息包含一个单词的,如第一个计数器的 CPU-cycles 和第二个计数器的 events。类型信息包括两个单词的,如第三个计数器的 load events。
原始硬件计数器列表中包含的信息是有别名的硬件计数器列表中信息的子集。原始硬件计数器列表中的每行包括由 cpu-track(1) 使用的内部计数器名称、可以在其上使用计数器的寄存器编号、缺省溢出值和计数器单位(可以是 CPU-cycles 或 Events)。
如果计数器度量与运行的程序无关的事件,则类型信息的第一个单词是 not-program-related。对于这样的计数器,分析不会记录调用栈,而是显示人工函数 collector_not_program_related 中所用的时间。线程和 LWP ID 会被记录,但没有任何意义。
原始计数器的缺省溢出值为 1000003。对于大多数原始计数器来说,此值并非理想值,所以您应在指定原始计数器时指定溢出值。
在多线程程序中,不同线程执行的任务同步会导致应用程序的执行延迟,例如,一个线程要访问已被其他线程锁定的数据时就不得不等待。这些事件称为同步延迟事件,并通过跟踪对 Solaris 或 pthread 线程函数的调用来收集这些事件。收集和记录这些事件的过程称为同步等待跟踪。等待锁定花费的时间称为等待时间。
只有等待时间超过阈值(单位为微秒)时,才会记录事件。阈值为 0 表示跟踪所有的同步延迟事件,而不管等待时间为何。缺省阈值通过运行校准测试决定,在该测试中对线程库的调用不会出现任何同步延迟。阈值是这些调用的平均时间与某个因子(当前为 6)的乘积。该过程可防止对此类事件进行记录:即等待时间仅在于调用本身,而与实际的延迟无关。因此,数据量会大大减少,但同步事件的计数可能会被明显低估。
Java 程序不支持同步跟踪。
表 2–2 同步等待跟踪度量
度量 |
定义 |
---|---|
对等待时间超过指定阈值的同步例程的调用数目。 |
|
超过指定阈值的等待时间的总和。 |
通过该信息,您可以确定函数或装入对象对同步例程进行调用时是会经常被阻塞还是会经历很长时间的等待。高同步等待时间表示线程间的争用。您可以通过重新设计算法,尤其是重新组织锁的结构,以便仅包含需要锁定的每个线程的数据来减少争用。
对未正确管理的内存分配和解除分配函数进行调用可能会造成数据的使用效率降低,从而导致应用程序的性能降低。在堆跟踪中,收集器通过插入 C 标准库内存分配函数 malloc、realloc、valloc 和 memalign 以及解除分配函数 free 跟踪内存分配和解除分配请求。对 mmap 的调用被视为内存分配,它允许记录 Java 内存分配的堆跟踪事件。Fortran 函数 allocate 和 deallocate 调用 C 标准库函数,因此会间接跟踪这些例程。
不支持对 Java 程序的堆分析。
表 2–3 内存分配(堆跟踪)度量
度量 |
定义 |
---|---|
分配数 |
对内存分配函数的调用数。 |
分配的字节 |
每次调用内存分配函数时分配的字节总数。 |
泄漏数 |
调用内存分配函数(未对解除分配函数进行相应的调用)的数量。 |
泄漏的字节 |
已分配但未解除分配的字节数。 |
收集堆跟踪数据有助于识别程序中的内存泄漏,或定位内存分配效率不高的位置。
内存泄漏的另一个常用定义(例如在 dbx 调试工具中)为:内存泄漏是一个动态分配的内存块,在程序的数据空间中没有任何指向它的指针。此处所使用的泄漏定义包括这种替换的定义,但也包括存在指针的内存。
收集器可以收集有关对消息传递接口 (Message Passing Interface, MPI) 库的调用的数据。
使用开源 VampirTrace 5.5.3 发行版来实现 MPI 跟踪。该跟踪可识别以下 VampirTrace 环境变量:
VT_STACKS | |
VT_BUFFER_SIZE | |
VT_MAX_FLUSHES |
控制在终止 MPI 跟踪前刷新缓冲区的次数。缺省值是 0,这表示只要缓冲区满了就允许刷新到磁盘。将 VT_MAX_FLUSHES 设置为正数将为刷新缓冲区的次数设置限制。 |
VT_VERBOSE |
有关这些变量的更多信息,请参见 Technische Universität Dresden Web 站点上的 Vampirtrace 用户手册。
在达到缓冲区限制之后发生的 MPI 事件将不会写入跟踪文件,这将导致跟踪不完整。
要去掉限制并获取应用程序的完整跟踪,请将 VT_MAX_FLUSHES 环境变量设置为 0。该设置将导致 MPI API 跟踪收集器在缓冲区已满时刷新磁盘的缓冲区。
要更改缓冲区大小,请设置 VT_BUFFER_SIZE 环境变量。该变量的最佳值取决于要跟踪的应用程序。设置较小的值将增加应用程序可以使用的内存,但是将触发 MPI API 跟踪收集器频繁进行缓冲区刷新。这些缓冲区刷新可能会显著改变应用程序的行为。另一方面,设置较大的值(如 2 G)可以使 MPI API 跟踪收集器刷新缓冲区的次数降至最低,但是将减少应用程序可以使用的内存。如果没有足够的内存可用来容纳缓冲区和应用程序数据,应用程序的某些部分可能会交换至磁盘,从而导致应用程序的行为发生显著改变。
MPI_Abort |
MPI_Accumulate |
MPI_Address |
MPI_Allgather |
MPI_Allgatherv |
MPI_Allreduce |
MPI_Alltoall |
MPI_Alltoallv |
MPI_Alltoallw |
MPI_Attr_delete |
MPI_Attr_get |
MPI_Attr_put |
MPI_Barrier |
MPI_Bcast |
MPI_Bsend |
MPI_Bsend-init |
MPI_Buffer_attach |
MPI_Buffer_detach |
MPI_Cancel |
MPI_Cart_coords |
MPI_Cart_create |
MPI_Cart_get |
MPI_Cart_map |
MPI_Cart_rank |
MPI_Cart_shift |
MPI_Cart_sub |
MPI_Cartdim_get |
MPI_Comm_compare |
MPI_Comm_create |
MPI_Comm_dup |
MPI_Comm_free |
MPI_Comm_group |
MPI_Comm_rank |
MPI_Comm_remote_group |
MPI_Comm_remote_size |
MPI_Comm_size |
MPI_Comm_split |
MPI_Comm_test_inter |
MPI_Dims_create |
MPI_Errhandler_create |
MPI_Errhandler_free |
MPI_Errhandler_get |
MPI_Errhandler_set |
MPI_Error_class |
MPI_Error_string |
MPI_File_close |
MPI_File_delete |
MPI_File_get_amode |
MPI_File_get_atomicity |
MPI_File_get_byte_offset |
MPI_File_get_group |
MPI_File_get_info |
MPI_File_get_position |
MPI_File_get_position_shared |
MPI_File_get_size |
MPI_File_get_type_extent |
MPI_File_get_view |
MPI_File_iread |
MPI_File_iread_at |
MPI_File_iread_shared |
MPI_File_iwrite |
MPI_File_iwrite_at |
MPI_File_iwrite_shared |
MPI_File_open |
MPI_File_preallocate |
MPI_File_read |
MPI_File_read_all |
MPI_File_read_all_begin |
MPI_File_read_all_end |
MPI_File_read_at |
MPI_File_read_at_all |
MPI_File_read_at_all_begin |
MPI_File_read_at_all_end |
MPI_File_read_ordered |
MPI_File_read_ordered_begin |
MPI_File_read_ordered_end |
MPI_File_read_shared |
MPI_File_seek |
MPI_File_seek_shared |
MPI_File_set_atomicity |
MPI_File_set_info |
MPI_File_set_size |
MPI_File_set_view |
MPI_File_sync |
MPI_File_write |
MPI_File_write_all |
MPI_File_write_all_begin |
MPI_File_write_all_end |
MPI_File_write_at |
MPI_File_write_at_all |
MPI_File_write_at_all_begin |
MPI_File_write_at_all_end |
MPI_File_write_ordered |
MPI_File_write_ordered_begin |
MPI_File_write_ordered_end |
MPI_File_write_shared |
MPI_Finalize |
MPI_Gather |
MPI_Gatherv |
MPI_Get |
MPI_Get_count |
MPI_Get_elements |
MPI_Get_processor_name |
MPI_Get_version |
MPI_Graph_create |
MPI_Graph_get |
MPI_Graph_map |
MPI_Graph_neighbors |
MPI_Graph_neighbors_count |
MPI_Graphdims_get |
MPI_Group_compare |
MPI_Group_difference |
MPI_Group_excl |
MPI_Group_free |
MPI_Group_incl |
MPI_Group_intersection |
MPI_Group_rank |
MPI_Group_size |
MPI_Group_translate_ranks |
MPI_Group_union |
MPI_Ibsend |
MPI_Init |
MPI_Init_thread |
MPI_Intercomm_create |
MPI_Intercomm_merge |
MPI_Irecv |
MPI_Irsend |
MPI_Isend |
MPI_Issend |
MPI_Keyval_create |
MPI_Keyval_free |
MPI_Op_create |
MPI_Op_free |
MPI_Pack |
MPI_Pack_size |
MPI_Probe |
MPI_Put |
MPI_Recv |
MPI_Recv_init |
MPI_Reduce |
MPI_Reduce_scatter |
MPI_Request_free |
MPI_Rsend |
MPI_rsend_init |
MPI_Scan |
MPI_Scatter |
MPI_Scatterv |
MPI_Send |
MPI_Send_init |
MPI_Sendrecv |
MPI_Sendrecv_replace |
MPI_Ssend |
MPI_Ssend_init |
MPI_Start |
MPI_Startall |
MPI_Test |
MPI_Test_cancelled |
MPI_Testall |
MPI_Testany |
MPI_Testsome |
MPI_Topo_test |
MPI_Type_commit |
MPI_Type_contiguous |
MPI_Type_extent |
MPI_Type_free |
MPI_Type_hindexed |
MPI_Type_hvector |
MPI_Type_indexed |
MPI_Type_lb |
MPI_Type_size |
MPI_Type_struct |
MPI_Type_ub |
MPI_Type_vector |
MPI_Unpack |
MPI_Wait |
MPI_Waitall |
MPI_Waitany |
MPI_Waitsome |
MPI_Win_complete |
MPI_Win_create |
MPI_Win_fence |
MPI_Win_free |
MPI_Win_lock |
MPI_Win_post |
MPI_Win_start |
MPI_Win_test |
MPI_Win_unlock |
表 2–4 MPI 跟踪度量
度量 |
定义 |
---|---|
MPI 发送数 |
已启动 MPI 点对点发送数 |
发送的 MPI 字节 |
“MPI 发送”中的字节数 |
MPI 接收数 |
已完成 MPI 点对点接收数 |
接收的 MPI 字节 |
“MPI 接收”中的字节数 |
MPI 时间 |
对 MPI 函数的所有调用所花费的时间 |
其他 MPI 事件 |
对既没有发送也没有接收点对点消息的 MPI 函数的调用数 |
MPI 时间是 MPI 函数中所用的总 LWP 时间。如果还收集了 MPI 状态时间,则除 MPI_Init 和 MPI_Finalize 之外的所有 MPI 函数的 MPI 工作时间加上 MPI 等待时间应大约等于 MPI 工作时间。在 Linux 上,MPI 等待和工作基于用户 CPU 时间加系统 CPU 时间,而 MPI 时间基于实际时间,所以这些数值将不匹配。
当前,仅针对点对点消息收集 MPI 字节和消息计数;不针对集体通信函数记录 MPI 字节和消息计数。“接收的 MPI 字节”度量会计算所有消息中接收的实际字节数。“发送的 MPI 字节”会计算所有消息中发送的实际字节数。“MPI 发送数”会计算发送的消息数,“MPI 接收数”会计算接收的消息数。
收集 MPI 跟踪数据有助于标识 MPI 程序中可能因 MPI 调用而产生性能问题的位置。可能发生的性能问题的例子有负载平衡、同步延迟和通信瓶颈。
全局数据由收集器以称为样本数据包的数据包来记录。每个包中都包含一个数据包头、时间戳、内核的执行统计数据(如缺页和 I/O 数据)、上下文切换以及各种页面驻留(工作集和分页)统计数据。记录在样本包中的数据对程序来说是全局的,且不会转换为性能度量。记录样本包的过程称为抽样。
当程序在 IDE 中进行调试期间或在 dbx 中因任何原因停止时(如在断点处,前提是设置了有关在断点处停止的选项)
使用 dbx collector sample record 命令手动记录样本时
如果代码中包含对 collector_sample 的调用,则在调用该例程时(请参见 数据收集的程序控制)
如果将 -l 选项与 collect 命令一起使用,则在传送指定信号时(请参见 collect(1) 手册页)
开始和终止收集时
使用 dbx collector pause 命令暂停收集时(就在暂停之前)和使用 dbx collector resume 命令恢复收集时(就在恢复之后)
创建子孙进程前后
性能工具使用记录在样本包中的数据,按时间周期将数据分组,这称为样本。您可以通过选择一组样本过滤特定于事件的数据,以便只查看这些特定时间周期的信息。您也可以查看每个样本的全局数据。
性能工具不对不同种类的样本点进行区分。要利用样本点进行分析,您应只选择一种点进行记录。具体地说,如果要记录与程序结构或执行序列有关的样本点,则应关闭定期抽样,并使用在 dbx 停止进程时,或将信号传送到正使用 collect 命令记录数据的进程时,或调用收集器 API 函数时记录的样本。