Download presentation
Presentation is loading. Please wait.
1
NUIST HPC使用讲座 ——代码调试 刘建宇
2
!!!声明!!! ftp://202.195.238.13/Public/debugging.pdf 仅校内可访问
这里讨论的用户环境设置是基于目前(2014年)的NUIST HPC的配置上进行的 相关的用法、设置方式等有一定的普遍性,也有本地局限性 这里讨论的用法、设置方式,在不同的系统及配置下可能不适用 这里讨论的用法、设置方式,可能有不全面或不足之处 仅供参考,谨慎使用 ftp:// /Public/debugging.pdf 仅校内可访问
3
脚本调试 使用shell的执行选项 在shell中输出调试信息 使用“调试钩子” 脚本调试器bashdb
4
脚本调试 ——使用shell的执行选项(-n)
读取脚本但不执行 测试脚本是否存在语法错误 bash -n script.sh ksh -n script.sh csh/tcsh -n script.sh #!/bin/bash if [ "$#" != "1" ]; then echo "usage : $0 filename" elif [ ! -f $1 ] then echo "$1 not exists" else tail $1 fi bash -n ./exp01.bash exp02.bash: line 8: syntax error near unexpected token `else' exp02.bash: line 8: `else'
5
脚本调试 ——使用shell的执行选项(-x)
进入跟踪方式,显示所执行的每一条命令 全局调试 bash -x script.sh ksh -x script.sh csh/tcsh -x script.sh 局部调试(仅bash/ksh) set -x 打开调试信息 set +x 关闭调试信息
6
脚本调试 ——使用shell的执行选项(-x)
#!/bin/bash echo "Hello $USER," echo Today is `date +"%Y-%m-%d"` echo bye-bye bash -x ./exp02.bash + echo 'Hello jliu' Hello jliu + echo Today is `date +%Y-%m-%d` Today is + echo bye-bye bye-bye #!/bin/bash echo "Hello $USER," set -x echo Today is `date +"%Y-%m-%d"` set +x echo bye ./exp03.bash Hello jliu + echo Today is `date +%Y-%m-%d` Today is + set +x bye-bye
7
脚本调试 ——对“-x”的增强选项 BASH KSH
PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}:' KSH PS4='+${LINENO}:'
8
脚本调试 ——对“-x”的增强选项 #!/bin/bash isRoot () { if [ $UID -ne 0 ]; then
return 1 else return 0 fi } isRoot if ["$?" -ne 0 ]; then echo "Must be root to run this script" exit 1 echo "welcome root user"
9
脚本调试 ——对“-x”的增强选项 bash -n ./exp04.bash ./exp04.bash
exp04.bash: line 10: [1: command not found welcome root user export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}:' bash -x ./exp04.bash +exp04.bash:9::isRoot +exp04.bash:3:isRoot:'[' 502 -ne 0 ']' +exp04.bash:4:isRoot:return 1 +exp04.bash:10::'[1' -ne 0 ']' exp04.bash: line 10: [1: command not found +exp04.bash:14::echo 'welcome root user' welcome root user
10
脚本调试 ——使用shell的执行选项(-e)
如果产生错误立即退出 全局影响 bash -e script.sh ksh -e script.sh csh/tcsh -e script.sh 局部影响(仅bash/ksh) set -e 打开退出陷阱 set +e 关闭退出陷阱
11
脚本调试 ——使用shell的执行选项(-e)
#!/bin/bash ls dummy_$$ echo "Hello $USER" echo Today is `date +"%Y-%m-%d"` echo bye-bye bash ./exp05.bash ls: cannot access dummy_25097: No such file or directory Hello jliu Today is bye-bye bash -e ./exp05.bash ls: cannot access dummy_25107: No such file or directory
12
脚本调试 ——在shell中输出调试信息 使用echo输出调试信息 使用 trap命令 echo '|var='$var'|'
其基本的语法是: trap 'command' signal shell伪信号 EXIT 从一个函数中退出或整个脚本执行完毕 ERR 当一条命令返回非零状态时(代表命令执行不成功) DEBUG 脚本中每一条命令执行之前
13
脚本调试 ——使用 trap命令 #!/bin/bash ERRTRAP() {
echo "[LINE:$1] Error: Command or function exited with status $?" } foo() { return 1 trap 'ERRTRAP $LINENO' ERR abc foo echo "End" bash ./exp07.bash exp07.bash: line 11: abc: command not found [LINE:11] Error: Command or function exited with status 127 [LINE:8] Error: Command or function exited with status 1 End
14
脚本调试 ——使用 trap命令 #!/bin/bash
trap 'echo "before execute line:$LINENO, a=$a,b=$b,c=$c"' DEBUG a=1 if [ "$a" -eq 1 ]; then b=2 else b=1 fi c=3 bash ./exp08.bash before execute line:3, a=,b=,c= before execute line:4, a=1,b=,c= before execute line:5, a=1,b=,c= before execute line:9, a=1,b=2,c= before execute line:10, a=1,b=2,c=3 end
15
脚本调试 ——使用“调试钩子” 使用if块 使用命令序列
if [ “$DEBUG” = “true” ]; then #此处可以输出调试信息 fi 使用命令序列 namelist=`ls -l namelist.input` || { echo Error; exit 1; } 使用DEBUG函数 _DEBUG=“on" DEBUG() { [ "$_DEBUG" == "on" ] && } DEBUG echo "hello"
16
脚本调试 ——使用“调试钩子” #!/bin/bash _DEBUG="on" function DEBUG () {
} fun01 () { echo "BUT HERE I am inside the function fun01() body" echo "HERE I am outside the function fun01() body!" sleep 2 debug fun01 echo "End" bash ./exp09.bash HERE I am outside the function fun01() body! BUT HERE I am inside the function fun01() body End
17
程序调试 程序出错了!!!
18
程序调试 ——系统的相关文件 标准输入/标准输出/标准错误 stdin/stdout/stderr 文件描述符 stdin stdout
程序调试 ——系统的相关文件 标准输入/标准输出/标准错误 stdin/stdout/stderr 文件描述符 stdin stdout stderr Unix 1 2 Fortran 通常约定 5 6 F2003 ISO_FORTRAN_ENV INPUT_UNIT OUPUT_UNIT ERROR_UNIT C
19
程序调试 ——输出重定向 PROGRAM TEST WRITE(0,*) "Error" WRITE(6,*) "Good"
WRITE(*,*) "Error Good" END PROGRAM TEST #include <stdio.h> int main() { fprintf(stderr,"Error\n"); fprintf(stdout,"Good\n"); printf("Error Good\n"); return 0; }
20
程序调试 ——输出重定向 ./a.out bash & ksh Error ./a.out 1>out.log Good Error
Error Good a.out > out.log ./a.out >& out.log bash & ksh ./a.out 1>out.log Error ./a.out 2>error.log Good Error Good ./a.out > out.log 2>&1 ./a.out >out.log 2>err.log csh/tcsh (a.out > out.log) >& err.log sh -c 'a.out > out' 2>& err.log
21
程序调试 ——错误信息的查找 应用程序自己输出的日志文件 运行时重定向输出的日志文件 作业调度系统的标准输出和标准错误文件
WRF :rsl 文件 运行时重定向输出的日志文件 >& out.log 作业调度系统的标准输出和标准错误文件 PBS O 文件(标准输出)和E文件(标准错误) 或由 #PBS -j,#PBS -o, #PBS -e 指定的文件 错误的诊断需要综合查看所有的日志文件
22
程序调试 ——错误信息的查找(示例) PBS O 文件 任务脚本中没有重定向的标准输出的内容 并行环境的标准输出的内容
作业调度系统自己的标准输出的内容 =================================================================================== = BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES = EXIT CODE: 9 = CLEANING UP REMAINING PROCESSES = YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
23
程序调试 ——错误信息的查找(示例) PBS E文件 任务脚本中没有重定向的标准错误的内容 并行环境的标准错误的内容
作业调度系统自己的标准错误的内容 HYD_pmcd_pmip_control_cmd_cb (pm/pmiserv/pmip_cb.c:902): assert (!closed) failed HYDT_dmxu_poll_wait_for_event (tools/demux/demux_poll.c:76): callback returned error status main (pm/pmiserv/pmip.c:206): demux engine error waiting for event HYDT_bscu_wait_for_completion (tools/bootstrap/utils/bscu_wait.c:75): one of the processes terminated badly; aborting … … …
24
程序调试 ——错误信息的查找(示例) 应用程序自己的输出文件 WRF RSL文件 rsl.out :WRF输出到标准输出的内容
rsl.err :WRF输出到标准错误的内容 Timing for main: time _11:48:00 on domain 1: elapsed … 1 points exceeded cfl=2 in domain d01 at time _11:48:00 hours MAX AT i,j,k: 39 16 2 vert_cfl,w,d(eta)= … … … 6 points exceeded cfl=2 in domain d01 at time _11:48:00 hours MAX AT i,j,k: 39 16 3 vert_cfl,w,d(eta)= … … … Timing for main: time _11:49:00 on domain 2: elapsed …
25
程序调试 ——异常终止常见错误信息 Signal 11 or Signal 9 Signal 10 Singal 15
通常都是内存访问问题,例如数组下标越界、非法使用指针 或非正常结束并行程序 Singal 11(segmentation fault),多数访问了程序空间以外的地址 Singal 9 ,多数访问了程序内部不该访问的地址 Signal 10 ‘bus error’,不常见,通常由糟糕的代码编写引起或用不正确的方式进行的编译 Singal 15 Killed,进程被强制终止
26
程序调试 ——编译器的调试选项 PGI INTEL GFORTRAN -g -O0 -traceback
-Mbounds -Mchkfpstk -Mchkstk -Mchkptr -Ktrap=fp -Minfo=all INTEL -check all -check bounds -check pointers -check uninit -ftrapuv -warn all GFORTRAN -g -O0 -fbacktrace -ffpe-trap=list -fbounds-check -fcheck-array-temporaries -Wall
27
程序调试 ——定位错误 异常终止运行 去掉所有优化选项 加入调试编译选项进行编译 打开程序编译时的调试代码宏开关 打开程序运行时的调试开关
根据程序输出信息,在代码中添加相应调试输出信息 测试时使相同错误能在3-5分钟内就能触发 如果是并行程序,仅可能先测试串行运行的情况
28
程序调试 ——定位错误 运行异常/结果有问题 去掉所有优化选项,仅使用 加上浮点运算约束编译选项 PGI : -Kieee
-g -O0 加上浮点运算约束编译选项 PGI : -Kieee Intel : -fp-model precise -no-fma GNU : -ffloat-store -fno-fast-math 根据程序输出信息,在代码中添加相应调试输出信息 如果是并行程序,尽可能先测试串行运行的情况
29
程序调试 ——定位错误(示例一) program loop 不加边界检查编译选项 implicit none
real, allocatable :: u(:) integer i allocate(u(10)) do i=1,11 ! off-by-one error u(i)= i * 1.0 enddo print *,"i=",i, "u=",u(i) deallocate(u) end 不加边界检查编译选项 PGI无运行时错误,给出不正确结果 i= u= Intel、Gfotran产生运行时错误 *** glibc detected *** ./a.out: free(): invalid next size (fast): 0x ec5b40 *** 添加边界检查编译选项后的运行情况 At line 6 of file loop.f90 Fortran runtime error: Array reference out of bounds for array 'u', upper bound of dimension 1 exceeded (11 > 10)
30
程序调试 ——定位错误(示例二) 添加-traceback选项编译运行情况 无-traceback选项编译运行情况
interpolation_grib_api]$ ./test.sh Interpolate product number forrtl: severe (174): SIGSEGV, segmentation fault occurred 添加-traceback选项编译运行情况 interpolation_grib_api]$ ./test.sh Interpolate product number forrtl: severe (174): SIGSEGV, segmentation fault occurred Image PC Routine Line Source interpolation_exa AE6 hntfauh_ hntfauh.F interpolation_exa C6 hntfau_ hntfau.F interpolation_exa intf_ intf.F interpolation_exa BD0 Unknown Unknown Unknown interpolation_exa D8 Unknown Unknown Unknown interpolation_exa B37 MAIN__ interpolation_example.F interpolation_exa FC Unknown Unknown Unknown libc.so AE61ECDD Unknown Unknown Unknown interpolation_exa F9 Unknown Unknown Unknown
31
程序调试 ——定位错误(示例二) interpolation_example.F 122 INLEN = IREC
IRET = INTF2(INGRIB,INLEN,NEWFLD,NEWLEN) intf.F IF( LUSEHIR ) THEN IRET = HNTFAU(FLDIN,INLEN) ELSE IRET = INTFAU(FLDIN,INLEN) hntfau.F IRET = HNTFAUH(INGRIB,INLEN) hntfauh.F DO LOOP = 1, INLEN ZNFELDI( LOOP ) = FLDIN( LOOP ) ENDDO
32
程序调试 ——定位错误(示例三) 我已经设置了debug_level=0,为什么生成的rsl.out文件还是这么大,这样我的程序跑不完就断掉了!有没有方法可以继续接着跑么
33
程序调试 ——定位错误(示例三) 使用head/tail查看rsl.out的头/尾信息
包含大量的‘Error in mapping flagsoap to start_ind’ 信息 使用grep在chem中查找包含“mapping flagsoap”的代码 grep -i "mapping flagsoap" chem/*.F 5081 ! acd_alma_bugfix start do iv = start_ind, ngas_ioa + ngas_soa if (flagsoap(iv-start_ind+1).eq.2) then xsumfresh(ibin)= xsumfresh(ibin)+aer(iv,jtotal,ibin) elseif (flagsoap(iv-start_ind+1).eq.1) then xsumaged(ibin)= xsumaged(ibin)+aer(iv,jtotal,ibin) elseif (flagsoap(iv-start_ind+1).eq.0) then < print *, 'Error in mapping flagsoap to start_ind' endif enddo
34
程序调试 ——错误的修正 不要简单的在‘代码层面’做修正 在没有确切弄清楚相关代码的‘实际意义’时 修改前先备份原来的代码
向代码开发人员寻求帮助 或尽量做一些安全、保守的修改 修改前先备份原来的代码 注释掉原来的代码而不是直接修改或删除 修改后做相应注释
35
程序调试 ——错误的修正(示例) 示例三中,假设数据就是会有这种情况,能不能简单注释掉行5088的print语句? 不能
一种安全、保守的修改方式为 elseif (flagsoap(iv-start_ind+1).eq.0) then if ( err_sum .lt. 10 ) & print * ' Error in mapping flagsoap to start_ind ' err_sum = err_sum +1 endif endo if ( err_sum .gt. 10 ) & print * ' Too many errors in mapping flagsoap to start_ind '
36
程序调试 ——其他调试工具 TotalView idb pgdbg gdb 使用版本控制管理
IBM Graphical Symbolic Debugger idb Intel Debugger for Linux pgdbg PGI Graphical Symbolic Debugger gdb GNU Debugger 使用版本控制管理 SVN,CVS等
37
问题或建议
Similar presentations