好了,最为关键的一章来了,分析完了怎么获取分析的结果呢?OProfile为了保持了极低的系统开销,在运行过程中不会有数据送到分析器中,因此如果你需要获取分析数据时,第一步就是强制分析数据转存:
opcontrol --dump
进行完数据转存,接下来就该opreport、opannotate和opgprof上场了。
所有的分析工具都需要一个分析规格(profilespecification),这个规格定义了那些分析结果需要检查,比方说,我只需要查看/root/a.out的分析结果,如果使用opreport工具来查看,则使用下面的命令:
opreport -limage:/root/a.out
上面的命令中,“image:/root/a.out”部分就是规格参数部分,这个规格说明了只需要针对/root/a.out这个映像进行分析。规格参数都是name:value[,value]的形式。
opreport还有一个强大的功能就是定义两个分析规格,来比较这两次分析的差别,示例如下:
opreport -l /bin/bash {archive:./orig } { archive:./new }
上面的命令说明了“存在两个archive中的同一个映像的差别分析”。
opreport的规格参数如下:
archive:archivepath
oparchive生成的archive文件路径,如果没有这个标签,意味着当前系统,和“archive:”是相同的效果。
session:sessionlist
涉及到的会话名称列表,用逗号分隔。如果没有这个标签,意味着当前会话,和“seesion:current”是相同的效果。
session-exclude:sessionlist
排除在外的会话列表,用逗号分隔。
image:imagelist
涉及的映像名称列表,用逗号分隔。每个条目(entry)可能是相对路径,glob类型的名称或者绝对路径。
比如:
opreport'image:/usr/bin/oprofiled,*op*,./opreport'
image-exclude:imagelist
与“image:”相同,但是匹配的映像将不被分析。
lib-image:imagelist
和“image:”类似,但仅仅对一个应用程序的映像(包括应用程序本身和库)有效。当使用“--separate”参数时才有效,当使用“--separate=kernel”时使用这个参数就包括内核模块和内核。
lib-image-exclude:imagelist
与“lib-image:”类似,当时匹配到的映像将被排除。
event:eventlist
需要匹配的符号事件名称,比如:“event:DATA_MEM_REFScount:30000”。注意这个值对应于opcontrol的相应设置,对于在分析数据中的采样数量没有影响。当使用计时器中断模式时,这个值要为0(表示它不能被设置)。
cpu:cpulist
只分析给定序号的CPU,在使用CPU分析隔离的情况下有效。
unit-mask:masklist
需要匹配的事件掩码,例如:“unit-mask:1”。可以传递一个事件列表进行比较。
tgid:pidlist
仅考虑针对任务组的分析。除非有程序正在使用线程,否则一个进程的任务组号和进程ID相同。这个选项对应POSIX的线程组标识。当针对进程隔离分析时这个参数才有效。
tid:tidlist
仅考虑针对给定线程的分析。当使用最近线程库时,所有在同一个进程中的线程共享相同的任务组ID,但是有不同的线程ID。你可以使用这个参数时加上“tgid:”参数来只针对一个进程中的特定线程。当针对进程隔离分析时这个参数才有效。
每个会话的采样数据文件都在“$SESSION_DIR/samples/directory”(默认:/var/lib/oprofile/samples/)中,它们和二进制映像文件一起使用才能产生可供分析者分析的数据。有一些情况,OProfile不能找到二进制映像文件。所有的工具都有一个“--image-path”参数,你可以传递一个逗号分隔的路径列表,这些工具可以在这个列表中的路径中进行查找。
需要注意以下两种情况:
当采样数据已经创建,所分析的二进制映像却发生了变化,这种情况下你不会得到有效的符号数据;
如果你替换了一个二进制映像,如果你要做对比分析,你必须要把老的映像保存。
如果你使用opreport获取分析结果时却看到了下面的错误提示:
error: no sample filesfound: profile specification too strict ?
这句话的意思是使用你设置的规格参数,在采样数据中没有匹配到任何数据。这个问题的原因可能有以下几种:
a) 拼写错误
指定了一个二进制映像,却拼错了(囧)。
b) 分析器没有运行
确保当你运行你的程序时OProfile真正运行起来了。
c) 程序运行时间太短
OProfile是一个统计分析器,如果你的程序运行时间太短,OProfile就有可能采不到任何有效样本。这时你需要使用一个更低的事件数量给性能计数器,这样在一秒钟可以有更多的样本。
d) 程序大部分时间运行在库中
同样的,如果你的程序只有很少的时间运行在它本身,大部分时间运行在共享库中,你可能看不到任何针对程序的采样数据。如果是这种情况,你可以在分析时使用“opcontrol --separate=lib”设置。
e) 规格设置太严格
如果设置的规格太严格,那自己看着办吧:)
f) 程序本身没有产生任何事件
如果你使用了一个特定的事件计数器,比方说对MMX操作进行计数,在代码中可能没有产生任何相应的事件。检查一下你的代码,看看是不是你确实希望它这样做。
g) 没有指定正确的内核模块名称
如果使用的是2.6内核,如果需要获得内核模块的分析报告,确保使用了“-p”参数并且内核模块的名称是“.ko”扩展名。检查一下内核模块hi从initrd加载的。
--accumulated /-a
在符号列表中对采样和百分比进行累加。
--callgraph / -c
显示调用图。
--debug-info /-g
对每个符号显示源代码文件和代码行号。
--demangle / -Dnone|normal|smart
none: 不消除混淆;normal: 默认消除混淆器; smart: 使用模式匹配的方式使C++的符号变为可读(C++编译会对函数名称进行改编)
--details / -d
针对所选符号显示每条指令的细节,注意,如果所选的二进制映像没有符号信息,VMA值会显示原始的文件偏移。
--exclude-dependent/ -x
对库、内核模块和内核不包含特定程序,这个参数仅仅在分析会话使用“--separate”时有效。
--exclude-symbols/ -e [symbols]
排除所有在列表总的符号,这些符号以逗号分隔。
--global-percent/ -%
所有的百分比都相对于整个分析结果。
--help / -? /--usage
显示帮助信息。
--image-path /-p [paths]
针对二进制映像增加的查找路径,在2.6内核(或以上)中查找模块是非常必要的。
--root / -R[path]
针对增加的二进制映像指定查找路径。
--include-symbols/ -i [symbols]
仅包含列表中指定的符号,符号以逗号分隔。
--long-filenames/ -f
输出中路径为绝对路径(不仅仅是文件名)
--merge / -m[lib,cpu,tid,tgid,unitmask,all]
对分散在会话中的分析结果进行合并。
--no-header
不输出详细分析参数的头信息。
--output-file /-o [file]
把输出重定向到文件中,默认是标准输出(stdout)。
--reverse-sort /-r
与默认排序相反的排序方式
--session-dir=dir_path
使用dir_path中的样本数据库,默认的是(/var/lib/oprofile)
--show-address /-w
显示每个符号的VMA地址(默认是偏移)
--sort / -s [vma,sample,symbol,debug,image]
按照哪种方式对符号进行排序,分别是:符号地址、采样号、符号名称、调试文件名和行号、二进制映像文件名。
--symbols / -l
列出每个符号的统计信息,而不是针对整个二进制文件映像的总数。
--threshold / -t[percentage]
仅输出百分比大于percentage的采样符号数据(占比太小的就不输出啦)。
--verbose / -V[options]
打印大量的调试信息。
--version / -v
显示版本信息。
--xml / -X
产生XML格式输出。
opannotate工具非常强大,可以在源代码上直接标注相应的采样分析数据,通过这些数据分析者就可以很快定位哪些代码有问题,输出标记的源代码的前提是被分析的程序需要含有调试信息(GCC的-g参数)。
可以通过opannotate将所有的分析结果都输出到一个文件中,也可以使产生的标注源文件保持原来的文件结构。
下面是一个标注源文件的示例:
在每一行的第一个数字表示改行采样数量,第二个数字是改行的采样数量相对于所有采样数的百分比。
opannotate可以产生标记源文件的前提是必须能把二进制映像和源代码对应起来,一些二进制映像含有的调试信息中源代码的路径是相对路径而不是绝对路径,我们可以使用“--search-dirs”指定查找源文件的路径。
还有一种情况,二进制映像中的调试信息中包含有源代码的绝对路径,但是和这个映像对应的源代码却放在另外一个地方,这个时候,你需要使用--base-dirs参数让OProfile去另外的地方查找源代码。比方说你有一个二进制文件,它的调试信息中显示源代码路径为“/tmp/build/libfoo/foo.c”,但是和这个二进制文件对应的源代码路径却在“/home/user/libfoo/”,你可以通过以下命令让OProfile去“/home/user/libfoo/”下查找:可以用逗号分隔多个路径,组成路径列表。
--assembly / -a
输出带有标注的汇编指令,如果与“--source”合用,则输出标注的汇编+源代码。
--base-dirs / -b [paths]/
在调试信息中如果指定的源代码绝对路径和实际的源代码路径不一致时使用,这个参数指定了调试信息中的源代码绝对路径的前缀,它可以让OProfile去“--search-dirs”指定的绝对路径中查找源代码(图 3‑2)。
--demangle / -D none|normal|smart
none: 不消除混淆; normal: 默认消除混淆器;smart: 使用模式匹配的方式使C++的符号变为可读(C++编译会对函数名称进行改编)
--exclude-dependent / -x
对于共享库、内核模块和内核分析,不包括应用特定的映像。仅在分析会话使用了“--separate”参数时有意义。
--exclude-file [files]
排除匹配以逗号分隔的glob模式列表的所有文件。
--exclude-symbols / -e [symbols]
排除在以逗号分隔的符号列表中的所有符号。
--help / -? / --usage
显示帮助信息。
--image-path / -p [paths]
为二进制映像增加搜索路径,这些路径以逗号分隔,在2.6及以上内核中查找模块时必须使用。
--root / -R [path]
对于增加的二进制映像,添加搜索路径。
--include-file [files]
仅包含匹配以逗号分隔的glob模式列表的文件。
--include-symbols / -i [symbols]
仅包含符号列表(以逗号分隔)中的符号。
--objdump-params [params]
当调用objdump时传递特定的参数给它。
--output-dir / -o [dir]
输出目录,使opannotate对每一个源文件输出一个对应标注的文件。这个参数不能和“--assembly”一起使用。
--search-dirs / -d [paths]
查找源代码文件的路径列表,以逗号分隔。当调试信息中包含的源代码路径为相对路径时这个参数就非常有用了。
--source / -s
输出含标注信息的源代码,前提是调试信息可用。
--threshold / -t [percentage]
仅输出百分比大于percentage的采样符号数据(占比太小的就不输出啦)。--verbose / -V [options]
打印大量调试信息。
--version / -v
显示版本号。
oparchive工具会生成一个目录,这个目录放置了可执行程序、调试信息和OProfile的采样信息文件。这个目录可以拷贝到其他机器上,这样在其他机器上就可以通过这个目录对采样结果进行分析了。
比方说,下面的命令将会把采样信息文件、与采样文件相关的可执行程序以及相应的调试信息文件拷贝的“/tmp/current_data”目录下。
oparchive -o/tmp/current_data
oparchive参数说明
--help / -? / --usage
显示帮助信息。
--exclude-dependent / -x
对于共享库、内核模块和内核分析,不包括应用特定的映像。仅在分析会话使用了“--separate”参数时有意义。
--image-path / -p [paths]
为二进制映像增加搜索路径,这些路径以逗号分隔,在2.6及以上内核中查找模块时必须使用。
--root / -R [path]
对于增加的二进制映像,添加搜索路径。
--output-directory / -o [directory]
输出到指定目录,这个参数没有默认值,必须指定。
--list-files / -l
仅列出将会归档的文件,不拷贝它们。
--verbose / -V [options]
打印大量的调试信息。
--version / -v
显示版本号。