SWLU:主核性能采样、调试工具包



  • SWLU 是一个针对国产神威芯片主核的开发者工具包。支持主核调用栈回溯、主核性能采样(对应于gprof)、主核调试(对应于swgdb-bt)等功能。

    目前版本为初版暂不开源,之后的正式版计划开源(因为源代码有点丑,功能也不完善)。

    使用初步:

    本库支持C、C++和Fortran。

    库的链接:

    现在SWLU提供两个库文件:

    • libswlu.a
    • libswlu_mpi.a

    分别对应于串行程序和MPI程序。
    当你使用串行程序的时候,请链接libswlu.a
    当你使用MPI 程序的时候,请链接libswlu_mpi.a
    库的链接方法和x86平台是一致的。

    库的头文件:

    库的头文件仅有swlu.h,包含了所有接口函数的定义,支持c和c++

    库的获取:

    目前位于目录

    // 根目录
    /home/export/online1/swmore/opensource/swlu/
    // 相关文件
    /home/export/online1/swmore/opensource/swlu/lib/libswlu.a
    /home/export/online1/swmore/opensource/swlu/lib/libswlu_mpi.a
    /home/export/online1/swmore/opensource/swlu/include/swlu.h
    

    *符号表处理

    由于SWLU中部分功能依赖于符号表,所以需要显示的符号表。
    SWLU目前获取符号表的方式是通过readelf获得,具体操作如下:

    readelf -s *.exe > swlu_symtable.txt
    

    需要在bsub之前执行。

    其中*.exe是你bsub的可执行文件,swlu_symtatble.txt是SWLU所依赖的符号表文件。
    如过建立符号表的时候没有符号表文件(swlu_symtatble.txt),将会得到提示:

       Error : Can't find symbol table file: swlu_symtable.txt
       Please execute "readelf -s $EXE > swlu_symtable.txt" before running EXE
    

    对于性能采样和调试,初始化过程都会自动尝试建立符号表,用户也可以手动调用swlu_build_symtable()函数显式构建符号表。

    主核性能采样:

    这是该工具包中目前为止最有效的功能,已经在GROMACS、CESM等重量级项目中提供了全程的性能分析指导,并在lammps、wrf、fft、masnum等项目中进行了运行测试。

    工具特点:

    SWLU-prof是一个基于定时器采样的profiling工具。

    • 相比于gptl等插桩工具,可以获得系统库的信息;
    • 相比于神威自带的gprof,可以提供完整的调用栈信息。

    缺点在于现有版本采样粒度较粗,1s只能采样50~100次,导致采样次数不足时结果有一定误差。
    一般认为一个函数采样次数到达5000次时误差趋于稳定,建议采样段至少运行1分钟以上,如果函数杂而多,则需要更长时间的采样。

    接口函数:

    一共有以下四个函数:

    • swlu_prof_init: 初始化函数,在使用采样前必须调用。对于MPI程序,需要在MPI_Init之后调用
    • swlu_prof_print: 结果打印函数,对于MPI程序,需要在MPI_finalize前调用。使用后将结果输出到swlu_prof.txt和swlu_prof.dot中,前者是结果的文本格式,后者用于生成pdf。目前版本原则上你仅应该在程序末尾调用一次swlu_prof_print
    • swlu_prof_start: 开启采样
    • swlu_prof_stop: 停止采样

    以上函数参数均为空。具体示例如下 :

    // c & c++:
    swlu_prof_init();
    
    // fortran:
    call swlu_prof_init
    

    (原有未发布版接口以samprof为前缀,而非swlu_prof。这些接口在初版的库中依旧保留,只是使用是会提示"Warning : samprof_* is deprecated, please use swlu_prof_* instead"。未来的版本将会移除这套接口。)

    swlu_prof函数依赖于符号表,在swlu_prof_init中会运行swlu_build_symtable, 尝试读取符号表信息。
    如果没有符号表文件,最后运行的结果将只能得到一个名为(null)的空函数。

    func-name = (null), func-addr = 0, self = 100.000000%, tot = 100.000000%
    

    结果格式:

    结果文件有两个,一个是swlu_prof.txt,这是一个文本格式的性能结果,一般不常使用,格式如下:

      1  ===========================================
      2
      3  +++++++++++++++++++++++++++++++++++++++++++
      4  - function -
      5 total sample = 572
      6 func-name = _Z2fdv, func-addr = 4ff0416d60, self = 68.881119%, tot = 68.881119%
      7 func-name = main, func-addr = 4ff0416b10, self = 0.000000%, tot = 100.000000%
      8 func-name = __libc_start_main, func-addr = 4ff064f7f0, self = 0.000000%, tot = 100.000000%
      9 func-name = _host_start, func-addr = 4ff0416920, self = 0.000000%, tot = 100.000000%
     10 func-name = _Z2fcv, func-addr = 4ff0416db0, self = 31.118881%, tot = 31.118881%
     11  +++++++++++++++++++++++++++++++++++++++++++
     12  - calls -
     13 main -> _Z2fdv 68.8811%
     14 __libc_start_main -> main 100.0000%
     15 _host_start -> __libc_start_main 100.0000%
     16 main -> _Z2fcv 31.1189%
    

    另一个结果文件是swlu_prof.dot,这是一个用于生成调用关系图的文件。

    dot -Tpdf swlu_prof.dot -o example.pdf
    //   -o 用于指定目标pdf的名称。
    

    (dot是graphviz中的一个指令,关于graphviz画图工具更多功能可见 http://www.graphviz.org

    运行上面这条指令即可得到调用关系图。效果如下:
    0_1541160520888_WX20181102-200830.png

    节点:

    • 第一行:函数名
    • 第二行:该函数占用总时间的百分比,avg/max/min格式。即第一个数是所有进程的平均数,第二个数最大值,第三个数最小值。
    • 第三行:该函数自身指令占用总时间的百分比,avg/max/min格式。即不包括子调用的时间。

    边:

    • 表示一个调用弧占用总时间的百分比。仅有平均数信息。

    以及(lammps运行图):
    0_1541159913168_WX20181102-195825.png

    *注意事项:

    由于该工具会运行时触发系统软中断,可能导致本来能运行的程序运行崩溃。这不是工具的bug,而是被测程序本身有 “使用未定义内存”的bug。
    正确的处理过程应该是找出被测程序的bug并修复。该问题在CESM和GROMACS中出现并定位,并采取了一定方案修复/规避。
    (具体来说,就是工具在采样的时候改变了被测内存段的数值,导致本来不影响结果的未定义内存影响了结果)

    2018.11.27-version 0.5 : 特别鸣谢段晓辉,他发现了linux的altstack。现在时钟中断使用了这个功能,理论上来说不会对程序产生任何影响了。
    2018.12.06 发现了一个在CESM初始化中打开采样会发生的问题。

    主核调试工具(仅backtrace功能):

    这是通过注册几个关键信号(linux软中断机制)实现的自动backtrace工具。
    支持在触发浮点异常(SIGFPE)、段错误(SIGSEGV)以及非法指令(SIGILL)时,先打印函数栈,再进行原本的处理(结束程序等)。

    为何要使用这个功能:

    比swgdb方便,只需要初始化下,程序出问题了自动打印调用栈信息。
    比swgdb准确,swgdb经常调用栈信息错误,无法正确回溯。

    接口函数:

    仅一个初始化函数: swlu_debug_init

    c & c++:
      swlu_debug_init();
    fortran:
      call swlu_debug_init
    

    swlu_debug函数部分依赖于符号表,有符号表的时候输出完整信息,没有符号表的时候仅输出地址信息,没有函数名信息。
    swlu_debug_init也会尝试加载符号表。

    使用方法:

    当程序运行崩溃以后,自动打印调用栈信息,效果如下:
    0_1541158835243_WX20181102-165913.png
    目前只有0号进程有输出信息。

    此外,会在应用执行的文件夹生成swlu_debug[mpi_rank].txt,比如一个16个进程的文件会生成swlu_debug0.txt~swlu_debug15.txt。
    以下是4个进程的应用生成的文件:
    2018.11.27-version 0.5 : 特别鸣谢段晓辉,现在写完文件会有屏幕输出提示
    0_1541160177343_WX20181102-200240.png

    也可以和研究科学家段晓辉的spc工具一样,通过bsignal,提供手动的调用栈打印(目前设置和spc有冲突,后续解决):

    bsignal -s 30 bjobid 
    // 将所有进程的调用栈信息写文件,不写屏幕
    
    bsignal -s 31 bjobid 
    // 屏幕打印主进程(0号进程的)的调用栈,不写文件;屏幕打印效果和运行崩溃时一致
    

    2018.11.27-version 0.5 : 现在程序启动时(MPI_Init前,swlu_debug_init前)有一个自动的debug初始化。可以在MPI初始化前正常使用signo30,但是signo31会打印所有进程号的信息,可能会有错行。

    主核调用栈回溯工具:

    这是SWLU工具包核心技术,就是实现了一个通用的主核backtrace函数,比swgdb自带的backtrace更稳定(用swgdb的都知道backtrace经常跳不出来,没法正确解析函数栈;就是因为太烂了才会重新写一个...)。
    这个backtrace基本可以认为有99.9999%的正确率(新发现的bug也会持续修复),已经经过了大量测试。

    一般来说用户调试测试程序可能不会使用到这个接口,这个接口是提供给二次开发使用的。

    函数接口:

    c & c++ :
        int swlu_backtrace(uint64_t *func_ptr, int depth);
    fortran :
        subroutine swlu_backtrace(func_ptr, depth, ret)
            integer(8), dimension(:), intent(out) :: func_ptr
            integer(4), intent(in) :: depth
    	integer(4), intent(out) :: ret
    

    此函数采用execinfo.h中的backtrace函数的接口。
    http://velep.com/archives/1032.html 这个网页有简单的说明。

    func_ptr是一个uint64_t的数组,用来存调用栈的pc信息,参数depth用于指定回溯的最大深度。
    返回值用来返回实际回溯的调用栈深度。如果函数栈深度 <= depth,将返回函数栈实际深度。
    如果函数栈深度 > depth, 那将只回溯depth深度,并且返回值为depth。

    swlu_backtrace函数不依赖于符号表,无需任何初始化就可以在程序任何地方调用。

    ======================================================================
    2018.12.01 Verson 0.6beta 更新:
    现有接口保持兼容。

    1. 通过int swlu_prof_index支持了多个采样实例(每个实例都需要独立的init, start, stop, print)。
    2. 通过int swlu_prof_set_mpi和MPI_Comm swlu_prof_comm支持了对于特定采样实例,指定通信子(MPI_Comm)。
    3. 略微修改了.txt的内容,并增加了Basic Block级的采样结果。
    4. 通过int swlu_prof_print_every支持了所有进程打印自己的采样结果。
    5. 在找不到swlu_symtable.txt后会尝试自动获取符号表(目前无失败情况)(by 段晓辉)。


  • 经过wrf试用,很好用👍 👍 👍



  • fortran程序调用函数时候在程序的最开始地方直接报错:

    [45954278 ## Tue Jul  2 15:14:26 2019] warning: node [3362]: user's mpe task: tid= 0, pid= 7869, terminated by sig 11
    

    链接命令

    mpiswgfortran -g -fpic -cpp -DSW_slave -DSW_time  -c file -o file
    

    嵌入代码

    call swlu_debug_init
    

    编译链接都通过,但是程序提交运行就会报错,什么都打印不出来,应该是一开始就崩了,去除call swlu_debug_init语句程序可以正常跑



  • 可能是因为gcc的栈帧跟open64的有所区别。。。如果能用mpif90编译的话可能问题会少一些。另外链接命令里有-c是什么情况?



  • @段晓辉 您好,我写了一个小例子来学习swlu的用法
    编译和链接如下

    sw5f90 -host  -c matmul_sw.f90 main.f90
    mpif90 -hybrid  main.o  matmul_sw.o -L/home/export/online1/swmore/opensource/swlu/lib -lswlu  -o main
    

    然后执行

    readelf -s main > swlu_symtable.txt
    

    提交作业成功执行并生成swlu_prof.txt和swlu_prof.dot ,但文件内容为空
    461f6038-ecfe-4640-a8b1-b9647d237ee2-image.png
    请问这是什么原因



  • 跑的时间太短或者没有swlu_prof_start/swlu_prof_stop



  • @段晓辉 您好,我也遇到了和 @lhb8125 类似的情况,C++调用报错:

    [46499699 ## Mon Oct 21 15:02:24 2019] warning: node [10]: user's mpe task: tid= 0, pid= 553, terminated by sig 11
    

    编译链接过程中,系统都没有警告或报错,以下是我的链接命令:

    OBJECTS = mainfunc.o kiss_fft.o
    LIBS	= libswlu.a
    
    kissfftTest: $(OBJECTS)
    	sw5CC -host -o kissfftTest $(OBJECTS) $(LIBS)
    mainfunc.o: mainfunc.cpp
    	sw5CC -host -c mainfunc.cpp
    kiss_fft.o: kiss_fft.c
    	sw5cc -host -c kiss_fft.c
    

    原本我的程序是可以正常运行的,一开始我也担心可能存在您说的使用未定义内存bug,但是我发现只要一加 swlu_prof_init() 就报错,即使不使用 start 和 stop 采样也会报错,感觉应该不是这个问题,不知道您有没有什么建议?感谢。



  • @Jerry 换mpiCC和swlu_mpi试试看


Log in to reply