Почему некоторые локальные переменные не перечислены в соответствующем кадре стека при проверке с использованием GDB? - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть кусок кода на C, как показано ниже -

В файле .c-

1    custom_data_type2 myFunction1(custom_data_type1 a, custom_data_type2 b)
2    {
3        int c=foo();
4        custom_data_type3 t;
5        check_for_ir_path();
6        ...
7        ...
8    }
9
10    custom_data_type4 myFunction2(custom_data_type3 c, const void* d)
11    {
12        custom_data_type4 e;
13        struct custom_data_type5 f;
14        check_for_ir_path();
15        ...
16        temp = myFunction1(...);
17        return temp;
18    }

В заголовочном файле -

1    void CRASH_DUMP(int *i)
2     __attribute__((noinline));
3    
4    #define INTRPT_FORCE_DUMMY_STACK    3
5    
6    #define check_for_ir_path() { \
7        if (checkfunc1() && !checkfunc2()) { \
8            int sv = INTRPT_FORCE_DUMMY_STACK; \
9            ...
10            CRASH_DUMP(&sv);\
11        }\
12    }\

При неизвестном сценарии происходит сбой. После обработки дампов ядра с помощью GDB мы получаем стек вызовов как -

#0  0x00007ffa589d9619 in myFunction1 [...] 
(custom_data_type1=0x8080808080808080, custom_data_type2=0x7ff9d77f76b8) at ../xxx/yyy/zzz.c:5

        sv = 32761

        t = <optimized out>



#1  0x00007ffa589d8f91 in myFunction2 [...]

(custom_data_type3=<optimized out>, d=0x7ff9d77f7748) at ../xxx/yyy/zzz.c:16

        sv = 167937677

        f = {

          ...

        }

Если вы видите функцию, myFunction1 есть три локальные переменные - c, t, sv (определенные как часть определения макроса). Однако в обратной трассировке в кадре 0 мы видим только две локальные переменные - t и sv. И я не вижу в списке переменной c.

То же самое имеет место, в функции myFunction2 есть три локальные переменные - e, f, sv (определенные как часть определения макроса). Однако из обратной трассировки в кадре 1 мы видим только две локальные переменные - f и sv. И я не вижу в списке переменной e.

Почему такое поведение?

Любая нестатическая переменная, объявленная внутри функции, должна быть помещена в стек вызовов во время выполнения и которая должна была быть указана в полной обратной трассировке, не так ли? Однако некоторые локальные переменные отсутствуют в обратном следе. Может ли кто-нибудь дать объяснение?

1 Ответ

0 голосов
/ 06 сентября 2018

Объекты, локальные для функции C, часто не появляются в стеке, потому что оптимизация во время компиляции часто делает ненужным хранение объектов в стеке. В общем, хотя реализацию абстрактной машины C можно рассматривать как хранение объектов локально для функции в стеке, фактическая реализация на реальном процессоре после компиляции и оптимизации может сильно отличаться. В частности:

  • Объект, локальный для функции, может быть создан и использован только внутри регистра процессора. Когда имеется достаточно регистров процессора для хранения локальных объектов функции или некоторых из них, нет смысла записывать их в память, поэтому оптимизированный код этого не сделает.
  • Оптимизация может полностью исключить локальный объект или сложить его в другие значения. Например, учитывая void foo(int x) { int t = 10; bar(x+2*t); … }, компилятор может просто сгенерировать код, который добавляет непосредственное значение от 20 до x, в результате чего ни 10, ни любое другое создание экземпляра t никогда не появляется в стеке, в регистре или даже в непосредственном операнде инструкции. Он просто не существует в сгенерированном коде, потому что в этом не было необходимости.
  • Объект, локальный для функции, может появиться в стеке в одной точке во время кода функции, но не в других. И места, которые это появляется, могут отличаться от места к месту в коде. Например, с { int t = x*x; … bar(t); … t = x/3; … bar(t); … } компилятор может решить сохранить первое значение t в одном месте в стеке. Но второе значение, присвоенное t, фактически является отдельным временем жизни, и компилятор может хранить его в другом месте в стеке (или не делать вообще, как указано выше). В хорошей реализации отладчик может знать об этих разных местах и ​​отображать сохраненное значение t, пока счетчик программ находится в соответствующем разделе кода. И, хотя счетчик программы не находится в соответствующем разделе кода, t может фактически не существовать, и отладчик может сообщить, что в этот момент он оптимизирован.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...