Почему GDB непредсказуемо перепрыгивает между строками и печатает переменные как "<value optimized out>"?

Кто-нибудь может объяснить это поведение GDB?

900         memset(&new_ckpt_info,'\0',sizeof(CKPT_INFO));
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_** HDR),i_offset);
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_ HDR),i_offset);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
913         found = cpnd_find_exact_ckptinfo(cb , &ckpt_info , bitmap_offset , &offset , &prev_offset);
(gdb)
916         if(!found)
(gdb) p found
$1 = <value optimized out>
(gdb) set found=0
Left operand of assignment is not an lvalue.

Почему после выполнения строки 903 он снова выполняет то же самое для 905 908 910?

Другое дело found это boolпеременная, так почему она показывает value optimized out? Я не могу установить значение found также.

Это похоже на оптимизацию компилятора (в данном случае это -O2); как я все еще могу установить значение found?

8 ответов

Решение

Чтобы отладить оптимизированный код, изучите ассемблер / машинный язык.

Используйте режим GDB TUI. Моя копия GDB включает его, когда я набираю минус и ввод. Затем введите Cx 2 (удерживайте нажатой клавишу "Control" и нажмите X, отпустите обе кнопки и затем нажмите 2). Это поместит его в разделенное отображение источника и разборки. Тогда используйте stepi а также nexti перемещать одну машинную инструкцию за раз. Используйте Cx o для переключения между окнами TUI.

Загрузите PDF о машинном языке вашего процессора и соглашениях о вызовах функций. Вы быстро научитесь распознавать, что делается с помощью аргументов функции и возвращаемых значений.

Вы можете отобразить значение регистра, используя команду GDB, например p $eax

Перекомпилировать без оптимизации (-O0 на gcc).

Объявить найденным как "изменчивым". Это должно сказать компилятору НЕ оптимизировать его.

volatile int found = 0;

Компилятор начнет делать очень умные вещи с включенной оптимизацией. Отладчик покажет код, прыгающий вперед и назад во многом благодаря оптимизированному способу хранения переменных в регистрах. Это, вероятно, причина, по которой вы не можете установить свою переменную (или в некоторых случаях увидеть ее значение), поскольку она была разумно распределена между регистрами по скорости, вместо того, чтобы иметь прямую область памяти, к которой может обращаться отладчик.

Компилировать без оптимизаций?

Вы в значительной степени не можете установить значение найденного. Отладка оптимизированных программ редко стоит того, чтобы компилятор мог перестроить код так, чтобы он никоим образом не соответствовал исходному коду (кроме получения того же результата), таким образом, бесполезно путая отладчики.

Как правило, логические значения, которые используются в ветвях сразу после их вычисления, как это, никогда не хранятся в переменных. Вместо этого компилятор просто разветвляется непосредственно от кодов условий, которые были установлены из предыдущего сравнения. Например,

int a = SomeFunction();
bool result = --a >= 0; // use subtraction as example computation
if ( result ) 
{
   foo(); 
}
else
{
   bar();
}
return;

Обычно компилируется в нечто вроде:

call .SomeFunction  ; calls to SomeFunction(), which stores its return value in eax
sub eax, 1 ; subtract 1 from eax and store in eax, set S (sign) flag if result is negative
jl ELSEBLOCK ; GOTO label "ELSEBLOCK" if S flag is set
call .foo ; this is the "if" black, call foo()
j FINISH ; GOTO FINISH; skip over the "else" block
ELSEBLOCK: ; label this location to the assembler
call .bar
FINISH: ; both paths end up here
ret ; return

Обратите внимание, что "bool" никогда не хранится нигде.

При отладке оптимизированных программ (что может быть необходимо, если ошибка не обнаруживается в отладочных сборках), вам часто приходится разбираться сгенерированным компилятором сборки.

В вашем конкретном случае возвращаемое значение cpnd_find_exact_ckptinfo будет храниться в реестре, который используется на вашей платформе для возврата значений. На ix86, это было бы %eax, На x86_64: %raxи т. д. Вам может понадобиться Google для "соглашения о вызове процедур [вашего процессора]", если это не так.

Вы можете проверить этот регистр в GDB и вы можете установить его. Например, на ix86:

(gdb) p $eax
(gdb) set $eax = 0 

Я использую QtCreator с GDB.

Добавление

QMAKE_CXXFLAGS += -O0
QMAKE_CXXFLAGS -= -O1
QMAKE_CXXFLAGS -= -O2
QMAKE_CXXFLAGS -= -O3

Хорошо работает для меня

Другие вопросы по тегам