上节中显示了使用 ieee_handler 的示例。通常而言,可以选择使用 ieee_handler 或 sigfpe 时,推荐使用前者。
在要使用的处理程序是 sigfpe 时,有诸如捕获整型运算异常等实例。在基于 SPARC 的系统上,示例 23在整数被零除时自陷。
示例 23 捕获整型异常/* Generate the integer division by zero exception */ #include <signal.h> #include <siginfo.h> #include <ucontext.h> void int_handler(int sig, siginfo_t *sip, ucontext_t *uap); int main() { int a, b, c; /* * Use sigfpe(3) to establish "int_handler" as the signal handler * to use on integer division by zero */ /* * Integer division-by-zero aborts unless a signal * handler for integer division by zero is set up */ sigfpe(FPE_INTDIV, int_handler); a = 4; b = 0; c = a / b; printf("%d / %d = %d\n\n", a, b, c); return 0; } void int_handler(int sig, siginfo_t *sip, ucontext_t *uap) { printf("Signal %d, code %d, at addr %x\n", sig, sip->si_code, sip->__data.__fault.__addr); /* * automatically for floating-point exceptions but not for * integer division by zero. */ uap->uc_mcontext.gregs[REG_PC] = uap->uc_mcontext.gregs[REG_nPC]; }
以下为 C 驱动程序调用 Fortran 子例程的简单示例。 有关使用 C 和 Fortran 的更多信息,请参阅Oracle Developer Studio 12.5:C 用户指南和Oracle Developer Studio 12.5:Fortran 用户指南。以下为 C 驱动程序(将其保存在名为 driver.c 的文件中):
示例 24 从 C 调用 Fortran/* * a demo program that shows: * 1. how to call f95 subroutine from C, passing an array argument * 2. how to call single precision f95 function from C * 3. how to call double precision f95 function from C */ extern int demo_one_(double *); extern float demo_two_(float *); extern double demo_three_(double *); int main() { double array[3][4]; float f, g; double x, y; int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 4; j++) array[i][j] = i + 2*j; g = 1.5; y = g; /* pass an array to a fortran function (print the array) */ demo_one_(&array[0][0]); printf(" from the driver\n"); for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) printf(" array[%d][%d] = %e\n", i, j, array[i][j]); printf("\n"); } /* call a single precision fortran function */ f = demo_two_(&g); printf( " f = sin(g) from a single precision fortran function\n"); printf(" f, g: %8.7e, %8.7e\n", f, g); printf("\n"); /* call a double precision fortran function */ x = demo_three_(&y); printf( " x = sin(y) from a double precision fortran function\n"); printf(" x, y: %18.17e, %18.17e\n", x, y); ieee_retrospective_(); return 0; }
将 Fortran 子例程保存在名为 drivee.f 的文件中:
subroutine demo_one(array) double precision array(4,3) print *, 'from the fortran routine:' do 10 i =1,4 do 20 j = 1,3 print *, ' array[', i, '][', j, '] = ', array(i,j) 20 continue print * 10 continue return end real function demo_two(number) real number demo_two = sin(number) return end double precision function demo_three(number) double precision number demo_three = sin(number) return end
执行编译和链接:
cc -c driver.c f95 -c drivee.f demo_one: demo_two: demo_three: f95 -o driver driver.o drivee.o
输出结果看起来类似于下文:
from the fortran routine: array[ 1 ][ 1 ] = 0.0E+0 array[ 1 ][ 2 ] = 1.0 array[ 1 ][ 3 ] = 2.0 array[ 2 ][ 1 ] = 2.0 array[ 2 ][ 2 ] = 3.0 array[ 2 ][ 3 ] = 4.0 array[ 3 ][ 1 ] = 4.0 array[ 3 ][ 2 ] = 5.0 array[ 3 ][ 3 ] = 6.0 array[ 4 ][ 1 ] = 6.0 array[ 4 ][ 2 ] = 7.0 array[ 4 ][ 3 ] = 8.0 from the driver array[0][0] = 0.000000e+00 array[0][1] = 2.000000e+00 array[0][2] = 4.000000e+00 array[0][3] = 6.000000e+00 array[1][0] = 1.000000e+00 array[1][1] = 3.000000e+00 array[1][2] = 5.000000e+00 array[1][3] = 7.000000e+00 array[2][0] = 2.000000e+00 array[2][1] = 4.000000e+00 array[2][2] = 6.000000e+00 array[2][3] = 8.000000e+00 f = sin(g) from a single precision fortran function f, g: 9.9749500e-01, 1.5000000e+00 x = sin(y) from a double precision fortran function x, y: 9.97494986604054446e-01, 1.50000000000000000e+00
下表显示了 SPARC 体系结构调试命令的示例。
|
显示浮点数时,应该注意,寄存器的大小为 32 位,单精度浮点数将占据 32 位(因此可以填入一个寄存器),而双精度浮点数占据 64 位(因此使用两个寄存器来存放双精度数)。在十六进制表示形式中,32 位对应于 8 个十六进制位。以下 FPU 寄存器快照使用 adb 显示,其排列如下:
<name of fpu register> <IEEE hex value> <single precision> <double precision>
第三列存放第二列中显示的十六进制模式的单精度十进制解释。第四列解释寄存器对。例如,f11 行的第四列将 f10 和 f11 解释为 64 位 IEEE 双精度数。
由于 f10 和 f11 用于存放双精度值,因此在 f10 行上,将该值的第一个 32 位 7ff00000 解释为 +NaN 是没有任何意义的。将全部 64 位 7ff00000 00000000 解释为 +Infinity 就是有意义的转换。
adb 命令 $x 用于显示前 16 个浮点数据寄存器,同时还显示 fsr(浮点状态寄存器):
$x fsr 40020 f0 400921fb +2.1426990e+00 f1 54442d18 +3.3702806e+12 +3.1415926535897931e+00 f2 2 +2.8025969e-45 f3 0 +0.0000000e+00 +4.2439915819305446e-314 f4 40000000 +2.0000000e+00 f5 0 +0.0000000e+00 +2.0000000000000000e+00 f6 3de0b460 +1.0971904e-01 f7 0 +0.0000000e+00 +1.2154188766544394e-10 f8 3de0b460 +1.0971904e-01 f9 0 +0.0000000e+00 +1.2154188766544394e-10 f10 7ff00000 +NaN f11 0 +0.0000000e+00 +Infinity f12 ffffffff -NaN f13 ffffffff -NaN -NaN f14 ffffffff -NaN f15 ffffffff -NaN -NaN
下表显示了 x86 体系结构调试命令的示例:
|
以下示例显示了两种方法,用于在与 adb 中的例程 myfunction 对应的代码开头设置断点。首先可以使用:
myfunction:b
其次,您可以确定与对应于 myfunction 的代码片段开头部分相对应的绝对地址,然后在该绝对地址上设置断点:
myfunction=X 23a8 23a8:b
Fortran 程序中使用 f95 编译的主子例程在 adb 中名为 MAIN_。要在 adb 中的 MAIN_ 处设置断点,请键入:
MAIN_:b
检查浮点寄存器的内容时,dbx 命令 regs –F 显示的十六进制值是 base-16 表示形式,而不是数字的十进制表示形式。对于基于 SPARC 的系统,adb 命令 $x 和 $X 同时显示十六进制表示形式和十进制值。对于基于 x86 的系统,adb 命令 $x 只显示十进制值。对于基于 SPARC 的系统,双精度值显示奇数编号寄存器旁边的十进制值。
由于操作系统在进程首次使用浮点单元之前禁用浮点单元,因此在所调试的程序访问浮点寄存器之前,您无法修改浮点寄存器。
x86 上的对应输出类似于以下内容:
$x 80387 chip is present. cw 0x137f sw 0x3920 cssel 0x17 ipoff 0x2d93 datasel 0x1f dataoff 0x5740 st[0] +3.24999988079071044921875 e-1 VALID st[1] +5.6539133243479549034419688 e73 EMPTY st[2] +2.0000000000000008881784197 EMPTY st[3] +1.8073218308070440556016047 e-1 EMPTY st[4] +7.9180300235748291015625 e-1 EMPTY st[5] +4.201639036693904927233234 e-13 EMPTY st[6] +4.201639036693904927233234 e-13 EMPTY st[7] +2.7224999213218694649185636 EMPTY