У меня есть очень большой, очень старый, очень запутанный, очень недокументированный набор кода Fortran, который я пытаюсь устранить. Это дает мне проблемы с делением на ноль во время выполнения из-за раздела, который выглядит примерно так:
subroutine badsub(ainput)
implicit double precision (a-h,o-z)
include 'commonincludes2.h'
x=dsqrt((r(6)-r(8))**2+(z(6)-z(8))**2)
y=ainput
w=y+x
v=2./dlog(dsqrt(w/y))
Этот код делит на ноль в последней строке, потому что y
равно w
, потому что x
равно нулю, и, следовательно, dlog(dsqrt(1)
равно нулю.
Включаемый файл выглядит примерно так:
common /cblk/ r(12),z(12),otherstuff
На самом деле есть 3 заголовка включения с объявлением /cblk/
, которые я нашел при запуске grep -in "/cblk/" *.h *.f *.F
: «commonincludes.h», «commonincludes2.h» и «commonincludes3.h». В качестве дополнительного бонуса раздел памяти, соответствующий r
и z
, назван x
и y
в "commonincludes.h", т.е. "commonincludes'h" выглядит так:
common /cblk/ x(12),y(12),otherstuff
Моя проблема в том, что я НЕ ПОНИМАЮ, где установлены r
и z
. Я использовал grep
, чтобы найти все места, где есть каждый из заголовков, и я не вижу места, куда записываются переменные.
Если я проверю фактические значения в r
и z
в gdb, где возникает ошибка, значения выглядят разумными - они ненулевые, не похожие на мусор векторы действительных чисел, просто r(6)
равно r(8)
и z(6)
равно z(8)
, что вызывает проблему.
Мне нужно найти, где пишутся z
и r
, но я не могу найти инструкции в документации gdb для присоединения точки наблюдения к блоку COMMON
. Как я могу найти, где они написаны?
Если я попытаюсь, это не сработает, я думаю, из-за того, как работают обычные блоки. Насколько я понимаю, они буквально представляют собой блок памяти — я могу вызвать common /cblk/ x(12),y(12)
в одной подпрограмме и получить common /cblk/ z(24)
в другом месте, и Фортран не дрогнет на это, пока определения блоков имеют одинаковый размер. Как и в моем примере, одна и та же переменная называется r
и x
в зависимости от того, какой заголовок используется — как мне указать gdb следить за этим?
Внутри любой области у вас может быть не более одной версии этого общего определения блока, поэтому следите за соответствующей локальной переменной в этой области. То есть, если вы просматриваете r
в badsub
, вы будете отслеживать изменения в r
через ассоциацию хранения, независимо от имени другого объекта, связанного с этим r
.
@francescalus Ключевая часть моей проблемы заключается в том, что я не знаю, в какой области на самом деле находится код, который мне нужно отслеживать. Если не считать установки около 30 точек останова для каждой подпрограммы, которая может быть важна, и установки часов всякий раз, когда они вводятся (что не очень практично с этим спагетти-кодом), я не знаю, как это сделать. Можете ли вы указать область, когда вы устанавливаете часы?
Я думаю, что понял, как делать то, что я пытаюсь сделать. Поскольку переменные COMMON
распределяются статически, их адреса не должны меняться от запуска к запуску. Поэтому, когда моя программа останавливается из-за моей ошибки деления на ноль, я могу найти адрес памяти (в этом примере) r(8)
, который является глобальным по объему и не должен меняться при последующих запусках. Затем я могу повторно запустить код с точкой наблюдения по этому адресу, и он будет сигнализировать об изменении значения в любом месте кода.
В моем примере сессия gdb выглядит так, с именами процессов и каталогами, сгруппированными для защиты виновных:
Reading symbols from myprogram...
(gdb) r
Starting program: ************
Program received signal SIGFPE, Arithmetic exception.
0x00000000004df96d in badsub (ainput=1875.0000521766287) at badsub.f:109
109 v=2./dlog(dsqrt(w/y))
(gdb) p &r(8)
$1 = (PTR TO -> ( real(kind=8) )) 0xcbf7618 <cblk_+56>
(gdb) watch *(double precision *) 0x0cbf7618
Hardware watchpoint 1: *(double precision *) 0x0cbf7618
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: *************
Hardware watchpoint 1: *(double precision *) 0x0cbf7618
Old value = 0
New value = 6.123233995736766e-17
0x00007ffff6f2be2d in __memmove_avx_unaligned_erms () from /lib64/libc.so.6
Я подтвердил после запуска обратной трассировки, что это действительно место (предположительно первое место), где устанавливается моя переменная общего блока.
Вместо того, чтобы следить за общим блоком, можете ли вы следить за объектами в общем блоке/локальными переменными, которые вас интересуют?