跳过导航链接 | |
退出打印视图 | |
Oracle Solaris Studio 12.3:性能分析器 Oracle Solaris Studio 12.3 Information Library (简体中文) |
用 Java 编程语言所编写的应用程序的运行时性能失真和扩大
-h counter_definition_1...[, counter_definition_n]
对于大多数程序来说,您不必为数据收集和分析做任何特殊的准备。如果程序执行下列任一操作,则应当阅读下面的一个或多个小节:
安装信号处理程序
显式动态装入系统库
动态编译函数
创建要分析的后续进程
使用异步 I/O 库
直接使用分析计时器或硬件计数器 API
调用 setuid(2) 或执行 setuid 文件
此外,如果要在运行时控制程序中的数据收集,还应当阅读相关小节。
malloc、valloc 和 alloca (C/C++)
new (C++)
堆栈局部变量 (Fortran)
MALLOC 和 MALLOC64 (Fortran)
必须小心确保程序不依赖于动态分配的内存的初始内容,除非内存分配方法明确地说明要设置初始值:例如,比较 malloc(3C) 手册页中对 calloc 和 malloc 的描述。
偶尔,使用动态分配的内存的程序似乎可以单独地正常运行,但是启用性能数据收集之后就会失败。症状可能包括意外的浮点行为、段故障或特定于应用程序的错误消息。
如果应用程序单独运行时未初始化的内存偶然设置为良性值,但应用程序与性能数据收集工具一起运行时未初始化的内存被设置为其他值,则会出现这种行为。发生这种情况时,问题不出在性能工具上。依赖于动态分配的内存内容的任何应用程序都具有潜在的已知问题:除非明确说明使用其他方式,否则操作系统将为动态分配的内存随机提供任意内容。即使目前操作系统会始终将动态分配的内存设置为某个值,但是将来在使用操作系统的后续修订版或将程序移植到其他操作系统时,这些潜在的已知问题会引起意外的行为。
下列工具可以帮助您找到这些潜在的已知问题:
f95 -xcheck=init_local
有关更多信息,请参见《Fortran 用户指南》或 f95(1) 手册页。
lint 实用程序
有关更多信息,请参见《C 用户指南》或 lint(1) 手册页。
dbx 下的运行时检查
有关更多信息,请参见《使用 dbx 调试程序》手册或 dbx(1) 手册页。
Rational Purify
收集器插入各种系统库的函数,以收集跟踪数据并确保数据收集的完整性。下面的列表描述了收集器插入库函数调用的情况。
收集同步等待跟踪数据。在 Oracle Solaris 上,收集器插入 Oracle Solaris C 库 libc.so 中的函数。
收集堆跟踪数据。收集器插入函数 malloc、realloc、memalign 和 free。这些函数的版本可以在 C 标准库 libc.so 和其他库(如 libmalloc.so 和 libmtmalloc.so)中找到。
确保时钟数据的完整性。收集器插入 setitimer 并阻止程序使用分析计时器。
确保硬件计数器数据的完整性。收集器插入硬件计数器库 libcpc.so 中的函数并阻止程序使用计数器。程序对该库中函数的调用的返回值是 -1。
针对后续进程启用数据收集。收集器插入函数 fork(2)、fork1(2)、vfork(2)、fork(3F)、posix_spawn(3p)、posix_spawnp(3p)、system(3C)、system(3F)、sh(3F)、popen(3C) 和 exec(2) 及其变体。对 vfork 的调用已在内部被替换为对 fork1 的调用。这些插入仅适用于 collect 命令。
保证由收集器处理 SIGPROF 和 SIGEMT 信号。收集器插入 sigaction 以确保其信号处理程序是这些信号的主信号处理程序。
在某些情况下,插入不会成功:
将程序与任何包含被插入的函数的库进行静态链接。
将 dbx 附加到运行中的未预装入收集器库的应用程序。
动态装入其中一个库并通过只在该库中搜索来解析符号。
收集器插入失败可能会导致性能数据丢失或无效。
er_sync.so、er_heap.so 和 er_mpviewn.so(其中 n 表示 MPI 版本)库仅在分别请求同步等待跟踪数据、堆跟踪数据或 MPI 跟踪数据时装入。
收集器使用两个信号来收集分析数据:SIGPROF 用于所有实验;SIGEMT(在 Solaris 平台上)或 SIGIO(在 Linux 平台上)仅用于硬件计数器实验。收集器为其中的每个信号安装一个信号处理程序。该信号处理程序截获并处理它自己的信号,但是会将其他信号传递到所安装的其他信号处理程序。如果程序为这些信号安装其自己的信号处理程序,则收集器会将其信号处理程序作为主处理程序重新安装,以保证性能数据的完整性。
collect 命令还可以将用户指定的信号用于暂停和恢复数据收集以及记录样本。尽管在安装用户处理程序时向实验中写入警告,但这些信号不受收集器保护。确保收集器对指定信号的使用与应用程序对相同信号的使用之间没有冲突是您的责任。
由收集器安装的信号处理程序会设置一个确保系统调用不被信号传送中断的标志。如果程序的信号处理程序将该标志设置为允许中断系统调用,则设置该标志可以更改程序的行为。在异步 I/O 库 libaio.so 中就有一个行为更改的重要示例,它将 SIGPROF 用于异步取消操作,并且中断系统调用。如果已安装收集器库 libcollector.so,则取消信号总是来得太迟,以至于无法取消异步 I/O 操作。
如果在未预装入收集器库的情况下将 dbx 附加到进程并启用性能数据收集,并且程序随后安装其自身的信号处理程序,则收集器不再重新安装其自身的信号处理程序。在这种情况下,程序的信号处理程序必须确保 SIGPROF 和 SIGEMT 信号被传递,以便性能数据不丢失。如果程序的信号处理程序中断系统调用,那么程序行为和分析行为都将与预装入收集器库时不同。
由于动态装入器实施了一定的限制,因此将难以使用 setuid(2) 和收集性能数据。如果您的程序调用 setuid 或执行 setuid 文件,则收集器可能无法写入实验文件,原因是它缺少新用户 ID 的必需权限。
collect 命令通过将共享库 libcollector.so 插入目标的地址空间 (LD_PRELOAD) 来运行。如果对调用 setuid 或 setgid 或创建调用 setuid 或 setgid 的子孙进程的可执行文件调用过的 collect 命令进行调用,可能会出现多个问题。如果您不是超级用户,在运行实验时,收集会因为共享库未安装在可信目录中而失败。解决方法是以超级用户身份运行实验,或使用 crle(1) 授予权限。应对安全障碍时请格外小心,操作风险需自行承担。
运行 collect 命令时,必须为您、由使用 exec() 执行的程序的 setuid 属性和 setgid 属性设置的任何用户或组以及该程序自身设置的任何用户或组,将 umask 设置为允许写权限。如果未正确设置掩码,某些文件可能无法写入实验,并且可能无法处理实验。如果可以写入日志文件,尝试处理实验时将显示错误。
如果目标本身发出了设置 UID 或 GID 的任何系统调用,或者如果目标更改其 umask,然后对其他某个可执行文件派生或运行 exec(),或者 crle 用于配置运行时链接程序如何搜索共享对象,则可能会出现其他问题。
如果在更改其有效 GID 的目标上以超级用户身份启动实验,实验终止时自动运行的 er_archive 进程将失败,原因是它需要未标记为可信的共享库。在这种情况下,您可以在实验终止后立即在记录实验的计算机上明确地手动运行 er_archive 实用程序(或 er_print 实用程序或 analyzer 命令)。
如果要控制程序中的数据收集,收集器共享库 libcollector.so 包含了一些可以使用的 API 函数。这些函数是用 C 语言编写的。另外,也提供 Fortran 接口。C 接口和 Fortran 接口都是在由库所提供的头文件中定义的。
API 函数定义如下所示。
void collector_sample(char *name); void collector_pause(void); void collector_resume(void); void collector_terminate_expt(void);
CollectorAPI 类为 Java 程序提供了类似的功能,Java 接口中对其进行了介绍。
可以通过包括 collectorAPI.h 并与 -lcollectorAPI(包含用于检查底层 libcollector.so API 函数是否存在的实际函数)相链接来访问收集器 API 的 C 和 C++ 接口。
如果没有活动的实验,API 调用将被忽略。
Fortran API libfcollector.h 文件定义了库的 Fortran 接口。要使用该库,必须使用 -lcollectorAPI 链接应用程序。(还提供了该库的替代名称 -lfcollector,目的在于实现向后兼容性。除动态函数、线程暂停和恢复调用等功能外,Fortran API 提供了与 C 和 C++ API 相同的功能。
要使用 Fortran 的 API 函数,请插入下面的语句:
include "libfcollector.h"
注 - 请勿使用 -lcollector 链接任何语言的程序。否则,收集器可能会出现不可预知的行为。
使用以下语句可以导入 CollectorAPI 类并访问 Java API。但是请注意,必须使用指向 /installation_directory/lib/collector.jar 的类路径来调用应用程序,其中 installation_directory 是 Oracle Solaris Studio 软件的安装目录。
import com.sun.forte.st.collector.CollectorAPI;
Java CollectorAPI 方法的定义如下所示:
CollectorAPI.sample(String name) CollectorAPI.pause() CollectorAPI.resume() CollectorAPI.terminate()
除动态函数 API 之外,Java API 包含与 C 和 C++ API 相同的函数。
C 头文件 libcollector.h 包含一些宏,这些宏的作用是如果当时未在收集数据,则跳过对实际 API 函数的调用。在这种情况下,不动态装入函数。但是,由于在某些情况下这些宏不能很好地运行,所以使用这些宏会有风险。使用 collectorAPI.h 较为安全,因为它不使用宏,而是直接引用函数。
如果正在收集性能数据,则 Fortran API 子例程会调用 C API 函数,否则这些子例程将返回。检查的开销很低,不会对程序性能产生太大的影响。
如本章稍后所述,要收集性能数据就必须使用收集器运行您的程序。插入对 API 函数的调用不会启用数据收集功能。
如果要在多线程程序中使用 API 函数,应当确保它们只由一个线程调用。API 函数执行适用于进程(而不是单独的线程)的操作。如果每个线程都调用 API 函数,则记录的数据可能会与预期不同。例如,如果一个线程在其他线程到达程序中的同一点之前调用了 collector_pause() 或 collector_terminate_expt(),则会针对所有线程暂停或终止收集,从而丢失那些正在执行 API 调用之前代码的线程的数据。
对 API 函数的描述如下所示。
C 和 C++:collector_sample(char *name)
Fortran:collector_sample(string name)
Java:CollectorAPI.sample(String name)
记录样本包并用指定的名称或字符串标记该样本。当在“时间线”标签中选择一个样例时,性能分析器将在“时间线详细信息”标签中显示此标签。Fortran 参数 string 的类型为 character。
样本点包含进程(而不是单独的线程)的数据。在多线程应用程序中,如果在 collector_sample() API 函数记录样本时发生另一个调用,则该函数可确保只写入一个样本。所记录的样本数可能会少于发出该调用的线程数。
性能分析器不对由不同机制记录的样本进行区分。如果只想查看 API 调用所记录的样本,则应当在记录性能数据时关闭所有其他抽样模式。
C、C++ 和 Fortran:collector_pause()
Java:CollectorAPI.pause()
停止将特定于事件的数据写入实验。实验将保持打开状态,并将继续写入全局数据。如果没有活动的实验或者已经停止记录数据,则该调用将被忽略。该函数停止写入所有特定于事件的数据,即使它是由 collector_thread_resume() 函数针对特定线程启用的也是如此。
C、C++ 和 Fortran:collector_resume()
Java:CollectorAPI.resume()
在调用 collector_pause() 之后恢复将特定于事件的数据写入实验。如果没用活动的实验或数据记录功能处于活动状态,则该调用将被忽略。
C、C++ 和 Fortran:collector_terminate_expt()
Java:CollectorAPI.terminate
如果 C 或 C++ 程序向程序的数据空间动态编译函数,而且您希望在性能分析器中查看动态函数或模块的数据,那么,您必须向收集器提供信息。该信息由对收集器 API 函数的调用传递。API 函数的定义如下所示。
void collector_func_load(char *name, char *alias, char *sourcename, void *vaddr, int size, int lntsize, Lineno *lntable); void collector_func_unload(void *vaddr);
您不必将这些 API 函数用于由 Java HotSpot 虚拟机编译的 Java 方法,该虚拟机使用的是另一个接口。Java 接口提供已编译到收集器的方法的名称。您可以查看 Java 编译方法的函数数据和带注释的反汇编列表,但不能查看带注释的源代码列表。
将有关动态编译的函数的信息传递到收集器,以便在实验中进行记录。下表对参数列表进行了描述。
表 3-1 collector_func_load() 的参数列表
|
通知收集器位于地址 vaddr 的动态函数已卸载。