2015-02-12 3 views
3

Я вижу какую-то ценность в некотором месте, но не знаю, где она возникла в моей программе. Как мне понять, откуда взялось это значение?Как проследить «судьбу» определенного значения?

Я ожидаю, что следующие типы событий после регистрации:

  • Значение происходит от постоянной, арифметического выражения или системного вызова - начальное событие;
  • Значение присваивается (или извлекается) из некоторой переменной;
  • Значение передано в качестве аргумента или возвращено из некоторой функции;
  • Значение хранилось в (или извлекалось) из некоторой структуры;
  • Записывая исходный код с чем-то конкретным, я запускал дамп истории для этого значения.

Например, для этого примера кода:

#include <stdlib.h> 

struct SomeStruct { 
    int a; 
    int b; 
}; 

struct SomeStruct *globalvar; 

int f1(struct SomeStruct* par) { 
    return par->a; 
} 

int f2(struct SomeStruct* par, int q) { 
    par->a = q; 
    return par->b; 
} 

void trace_value(int g) {} /* dummy */ 

int main(void) { 
    int f = 31337; 

    globalvar = malloc(sizeof(*globalvar)); 
    f2(globalvar, f); 
    struct SomeStruct q = *globalvar; 
    int g = f1(&q); 

    trace_value(g); 

    return 0; 
} 

он должен вернуть что-то вроде

value 31337 originated from constant at fate.c:18 
assigned to variable at fate.c:18 
retrieved from variable at fate.c:21 
passed as argument to function at fate.c:21 
received as arument to a function at fate.c:12 
assigned to struct field at fate.c:13 
copied as a part of struct at fate.c:22 
retrieved from struct field at fate.c:9 
returned from function at fate.c:10 
assigned to variable at fate.c:23 
retrieved from variable at fate.c:25 
traced at fate.c:25 

Как мне это сделать или что-то подобное? Я ожидаю, что Valgrind или GDB или какая-то комбинация сможет это сделать.

+0

Не был бы более полезным для этого «обычный» отладчик, такой как GDB? – 5gon12eder

+0

Как это сделать с помощью gdb без O (N) ручного труда, где N - количество таких событий? –

+0

Если вы можете убедиться, что переменная изменяется только через функцию доступа, вы можете установить на ней контрольную точку. Если переменная не задана из множества мест в вашей программе, обертка доступа в такой функции не должна быть слишком большой. – 5gon12eder

ответ

1

Комбинированный idea1 с использованием обратного gdb и идеи2 из комментария MarkPlotnick к использованию точек наблюдения gdb. Вот демо-сессии, более полной, чем в оригинальный ответ:

$ gcc -ggdb -Dtrace_value=exit fate.c -o fate 
$ gdb -quiet -args ./fate 
Reading symbols from /home/vi/code/_/fate...done. 
(gdb) break main 
Breakpoint 1 at 0x8048482: file fate.c, line 18. 
(gdb) r 
Starting program: /home/vi/code/_/fate 
warning: Could not load shared library symbols for linux-gate.so.1. 
Do you need "set solib-search-path" or "set sysroot"? 

Breakpoint 1, main() at fate.c:18 
18  int f = 31337; 
(gdb) record 
(gdb) break 25 
(gdb) # traced at fate.c:25 
Breakpoint 2 at 0x80484d2: file fate.c, line 25. 
(gdb) c 
Continuing. 

Breakpoint 2, main() at fate.c:25 
25  trace_value(g); 
(gdb) # retrieved from variable at fate.c:25 
(gdb) watch g 
Hardware watchpoint 3: g 
(gdb) reverse-continue 
Continuing. 
Hardware watchpoint 3: g 

Old value = 31337 
New value = 134513899 
0x080484ce in main() at fate.c:23 
23  int g = f1(&q); 
(gdb) # assigned to variable at fate.c:23 
(gdb) # returned from function at fate.c:10 
(gdb) reverse-step 
f1 (par=0xffffd670) at fate.c:10 
10 } 
(gdb) list 
5 
6 struct SomeStruct *globalvar; 
7 
8 int f1(struct SomeStruct* par) { 
9  return par->a; 
10 } 
11 
12 int f2(struct SomeStruct* par, int q) { 
13  par->a = q; 
14  return par->b; 
(gdb) # retrieved from struct field at fate.c:9 
(gdb) print par 
$3 = (struct SomeStruct *) 0xffffd670 
(gdb) print ((struct SomeStruct *) 0xffffd670)->a 
$4 = 31337 
(gdb) watch ((struct SomeStruct *) 0xffffd670)->a 
Hardware watchpoint 4: ((struct SomeStruct *) 0xffffd670)->a 
(gdb) reverse-continue 
Continuing. 
Hardware watchpoint 4: ((struct SomeStruct *) 0xffffd670)->a 

Old value = 31337 
New value = -134716508 
0x080484ba in main() at fate.c:22 
22  struct SomeStruct q = *globalvar; 
(gdb) # copied as a part of struct at fate.c:22 
(gdb) print globalvar->a 
$5 = 31337 
(gdb) watch globalvar->a 
Hardware watchpoint 5: globalvar->a 
(gdb) reverse-continue 
Continuing. 
Hardware watchpoint 5: globalvar->a 

Old value = 31337 
New value = 0 
0x0804846f in f2 (par=0x804a008, q=31337) at fate.c:13 
13  par->a = q; 
(gdb) # assigned to struct field at fate.c:13 
(gdb) # received as arument to a function at fate.c:12 
(gdb) list 
8 int f1(struct SomeStruct* par) { 
9  return par->a; 
10 } 
11 
12 int f2(struct SomeStruct* par, int q) { 
13  par->a = q; 
14  return par->b; 
15 } 
16 
17 int main() { 
(gdb) bt 
#0 0x0804846f in f2 (par=0x804a008, q=31337) at fate.c:13 
#1 0x080484b0 in main() at fate.c:21 
(gdb) reverse-finish 
Run back to call of #0 0x0804846f in f2 (par=0x804a008, q=31337) at fate.c:13 
0x080484ab in main() at fate.c:21 
21  f2(globalvar, f); 
(gdb) # passed as argument to function at fate.c:21 
(gdb) # retrieved from variable at fate.c:21 
(gdb) watch f 
Hardware watchpoint 6: f 
(gdb) reverse-finish 
"finish" not meaningful in the outermost frame. 
(gdb) reverse-continue 
Continuing. 
Warning: 
Could not insert hardware watchpoint 6. 
Could not insert hardware breakpoints: 
You may have requested too many hardware breakpoints/watchpoints. 

(gdb) delete 
Delete all breakpoints? (y or n) y 
(gdb) watch f 
Hardware watchpoint 7: f 
(gdb) reverse-continue 
Continuing. 

No more reverse-execution history. 
main() at fate.c:18 
18  int f = 31337; 
(gdb) # assigned to variable at fate.c:18 
(gdb) # value 31337 originated from constant at fate.c:18 

Все ожидаемые сообщения в заявлении вопроса соответствуют некоторой информации вы видели в выходе GdB (как показано в комментариях).

+0

Если вы остановились на 'Вы хотите автоматически удалить предыдущие записи журнала выполнения, когда буфер записи/воспроизведения будет заполнен', тогда обновите gdb + use' set record full insn-number-max unlimited; запись полный'. Будьте готовы к использованию гигабайт виртуальной памяти. –

1

Я считаю, что это может быть выполнено вручную (т. Е. Работает на gdb сеансе) во время исполнения методом «обратная отладка». Я еще не пробовал, но документация GDB версии 7.0 claims, что она поддерживается на некоторых платформах.

метод будет что-то вроде:

  • локализации одного шага, где переменная используется в последнем месте (то есть, ваш «отправной точкой»)
  • анализировать исходный код (так что вы должны отладки символ и доступ к коду) кадра стека (например, list), так что вы получаете, как это значение получается (или, возможно, изменено) witihin (например, из параметра, переданного функции)
  • шаг назад к предыдущей кадре стека и повторение с предыдущего шага если вы не нашли его происхождения

Вот несколько примеров доказательств концепции для вашего образца кода. Я немного отредактировал его, так как функция trace_value была не определена. Обратите внимание: команда record может значительно замедлить выполнение программы.

$ gdb -q a.out 
Reading symbols from /home/grzegorz/workspace/a.out...done. 
(gdb) b main 
Breakpoint 1 at 0x400502: file fate.c, line 22. 
(gdb) run 
Starting program: /home/grzegorz/workspace/a.out 

Breakpoint 1, main() at fate.c:22 
22  int f = 31337; 
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6_6.5.x86_64 
(gdb) record 
(gdb) b trace_value 
Breakpoint 2 at 0x4004f8: file fate.c, line 19. 
(gdb) c 
Continuing. 

Breakpoint 2, trace_value (g=31337) at fate.c:19 
19 void trace_value(int g){} 
(gdb) info args 
g = 31337 
(gdb) reverse-finish 
Run back to call of #0 trace_value (g=31337) at fate.c:19 
0x0000000000400550 in main() at fate.c:29 
29  trace_value(g); 
(gdb) bt 
#0 0x0000000000400550 in main() at fate.c:29 
(gdb) list 29 
24  globalvar = malloc(sizeof(*globalvar)); 
25  f2(globalvar, f); 
26  struct SomeStruct q = *globalvar; 
27  int g = f1(&q); 
28 
29  trace_value(g); 
30 
31  return 0; 
32 } 

Немногие вещи, возможно, требуют некоторого объяснения. Сначала необходимо установить контрольную точку для main, так как это когда начинается выполнение программы, затем включите запись сеанса командой record. Затем установите вторую точку останова в функции trace_value и используйте команду continue (c). Это позволяет записывать все исполнение до момента, когда вводится trace_value. Вы можете подумать об этом как о «начальной точке», описанной выше.

Это, конечно, не полная история. Как я описал ранее, вам нужно проанализировать исходный код текущего кадра стека, а затем решить, что делать дальше.Вы можете использовать команду reverse-step или reverse-finish в соответствии с текущей ситуацией.

+0

Можете ли вы запустить демо-сессию на прилагаемом образце кода и создать что-то вроде прилагаемого ожидаемого результата? –

+0

@Vi .: См. Обновленный ответ. –

+0

Записал еще одну сессию gdb, теперь используя команду 'watch' gdb. –

Смежные вопросы