Чому GDB непередбачувано стрибає між рядками та друкує змінні як «<значення оптимізовано>»?


84

Хто-небудь може пояснити таку поведінку 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змінна -type, так чому вона відображається value optimized out? Я також не можу встановити значення found.

Здається, це оптимізація компілятора (в даному випадку його -O2); як я все ще можу встановити значення found?


8
під час налагодження зазвичай добре компілювати з -O0, оскільки оптимізація призводить до таких проблем.
LiraNuna

Відповіді:


115

Для налагодження оптимізованого коду вивчіть мову складання / машини.

Використовуйте режим GDB TUI. Моя копія GDB дозволяє це робити, коли я ввожу мінус і ввожу. Потім введіть Cx 2 (тобто утримуйте клавішу Control і натисніть X, відпустіть обидва, а потім натисніть 2). Це переведе його в розділений джерело та демонстраційний дисплей. Потім використовуйте stepiі nextiдля переміщення по одній машинній інструкції за раз. Використовуйте Cx o для перемикання між вікнами TUI.

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

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


У мене все ще є проблема "оптимізовано", і значення змінної не відображається в інших вікнах, але, все ж, це чудова інформація, дякую!
Tom Brito

16
@TomBrito: Оптимізоване означає, що змінна відсутня в пам'яті. Ймовірно, це лише в регістрі ЦП, а це означає, що вам потрібно прочитати демонтаж та роздрукувати значення реєстру, щоб його знайти.
Zan Lynx

@Zan Lynx: Я не впевнений, що погоджуюся з вашим аналізом. Символи ГНОК мають достатньо інформації для вилучення значень з реєстрів. Можливо, тут мається на увазі те, що компілятор визначив, що змінну можна безпечно відкинути, коли час виконання досягне поточного рядка. У цьому випадку сховище, де живе змінна, можливо, було повторно використано для чогось іншого. Я думаю, ви праві, що, як правило, це трапляється лише за умови реєстрації змінної.
Ian Ni-Lewis

@ IanNi-Lewis: Я не знаю, яку версію DWARF ви використовуєте, але, на мій досвід, GDB не може надрукувати змінну, яка була збережена в реєстрі.
Zan Lynx

Я впевнена, що ти маєш рацію. Мій досвід роботи з DWARF походить від написання власного аналізатора, а не від використання gdb, тому я насправді не знаю, на що здатна gdb.
Ян Ні-Льюїс

75

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


17
Навіть -O0 може створити оптимізований код (намагаючись боротися з цим прямо зараз), хоча я не впевнений, чому.
Кріс Грегг,

@ChrisGregg У мене така сама проблема! Ви дізналися, в чому проблема?
Паоло М

1
@paolom, схоже, це може бути проблемою, тому я натомість компілюю g ++ для налагодження.
Кріс Грегг,

Часто це не є рішенням, особливо якщо у вас є основний дамп із виробництва та / або ви не можете відтворити проблему в середовищі розробки.
smbear

39

Оголошення визнано "летким". Це повинно сказати компілятору НЕ оптимізувати його.

volatile int found = 0;

1
Навіть коли я оголошую деякі змінні "мінливими" в налагоджувачі gdb, це відображає це як оптимізовану змінну! Це вже в цьому?
М.Рез

11

Компілятор почне робити дуже розумні речі з увімкненими оптимізаціями. Налагоджувач покаже код, який багато стрибає вперед і назад завдяки оптимізованому способу зберігання змінних у регістрах. Це, мабуть, причина, по якій ви не можете встановити свою змінну (або в деяких випадках побачити її значення), оскільки вона розумно розподілена між регістрами за швидкістю, а не має пряме місце в пам'яті, до якого відладчик може отримати доступ.

Скласти без оптимізацій?


6

Як правило, булеві значення, які використовуються у гілках відразу після їх такого обчислення, насправді ніколи не зберігаються у змінних. Натомість компілятор просто розгалужується безпосередньо від кодів умов , встановлених із попереднього порівняння. Наприклад,

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

Зверніть увагу, як "бул" насправді ніколи ніде не зберігається.


4

Ви майже не можете встановити значення знайденого. Налагодження оптимізованих програм рідко вартує клопоту, компілятор може переставити код таким чином, щоб він ні в якому разі не відповідав вихідному коду (крім отримання того самого результату), тим самим заплутуючи налагоджувачі без кінця.


4

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

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

Ви можете вивчити цей реєстр GDBі можете встановити його. Наприклад, про ix86:

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

0

Я використовую QtCreator з gdb.

Додавання

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

Для мене добре працює

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.