Простой, унифицированный и переносимый способ включения трассировки и обратной трассировки в программу на C.

обратная трассировка GNU libc и Встроенные эмуляторы/отладчики не всегда доступны при переносе кода на новую платформу, особенно если целью является микро < компилятор href="http://en.wikipedia.org/wiki/C_%28programming_language%29" rel="nofollow noreferrer">C, такой как для Z80. (Обычно программная ошибка где-то «просто зависала» или приводила к сбою гаджета.)

Есть ли альтернатива классическому "wolf fending" метод ручной вставки printf? Что-то простое и переносимое (без расширений C), которое кодер может сделать при разработке программы, включающей трассировку и обратную трассировку в программу на C?

Кстати: вот еще несколько связанных вопросов по stackoverflow, но оба они используют обратная трассировка GNU GLIBC и обратная трассировка часто зависит от компилятора/реализации:


person NevilleDNZ    schedule 17.07.2010    source источник


Ответы (3)


Вот ядро ​​ядра моего ответа: напишите код.

Суть моего ответа такова: если ваш компилятор всегда размещает локальные переменные в стеке, то...

Добавляйте большие двоичные объекты в стек при каждой записи функции, в которых записано имя функции, добавьте несколько магических чисел, чтобы, возможно, поймать сбой стека.

typedef struct stack_debug_blob_ {
    int magic1;
    const char * function_name;
    int magic2;
    struct stack_debug_blob_ * called_by;
    int magic3;
} stack_debug_blob;

stack_debug_blob * top_of_stack_debug_blobs = 0;

Создайте макрос ENTER(f), взяв имя функции. Макрос должен быть примерно в первой строке кода в каждой функции после открытия {. Он добавляет структуру с указателем на имя функции (const) char *, указатель на предыдущую структуру в стеке и, возможно, некоторые магические числа для проверки работоспособности. Направьте указатель верхней части стека BLOB-объектов на эту новую структуру.

#define ENTER(f)                                                \
stack_debug_blob new_stack_debug_blob = {                       \
    MAGIC1, (f), MAGIC2, top_of_stack_debug_blobs, MAGIC3};     \
stack_debug_blob * evil_hack = (top_of_stack_debug_blobs = (&new_stack_debug_blob))

Чтобы все было как можно более переносимым, все, что может сделать ENTER, — это объявить и инициализировать переменные. Следовательно, evil_hack выполняет немного дополнительных вычислений, а не просто инициализирует переменную.

Создайте функцию для обхода списка больших двоичных объектов, проверяющую указатели и магические числа. Он должен сигнализировать об ошибке (может быть, напечатать в stderr, может быть, заблокировать процессор с помощью while (1) { /* nada */ }, может быть, войти в отладчик... зависит от вашего оборудования), если он обнаружит, что что-то не так.

Создайте макрос EXIT(), который проверяет ваш стек больших двоичных объектов, а затем удаляет связь с самым верхним из связанного списка. Его нужно поставить в точках выхода всех ваших функций.

#define EXIT() do {                                            \
    check_debug_blobs();                                       \
    top_of_stack_debug_blobs = new_stack_debug_blob.called_by; \
    new_stack_debug_blob.magic1 -= 1; /* paranoia */           \
} while (0)

Вероятно, также потребуется заменить все операторы return вызовами макросов RETURN, макрос RETURN аналогичен EXIT, но имеет возврат перед } while (0).

Создайте функцию для просмотра списка больших двоичных объектов, распечатывающую имена функций, назовите ее как-то вроде stacktrace или backtrace.

Напишите программу для оснащения кода C вызовами ENTER(f), EXIT() и RETURN(x).

Опустил несколько деталей, чтобы вы могли повеселиться...

См. также Любой доступный перенос backtrace для uclibc?

person jsl4tv    schedule 19.07.2010
comment
вы можете переименовать ENTER как ENTER_ и #define ENTER() ENTER_(__FUNCTION__). - person John Ledbetter; 20.07.2010

На RosettaCode.org есть реализация, в которой используется та же основная идея, что и в предложении @jsl4tv.

Пример для следующего классического кода C со встроенной функцией "зависание":

#include <stdio.h>
#include <stdlib.h>

void inner(int k)
{
   for(;;){} /* hang */
}

void middle(int x, int y)
{
  inner(x*y);
}

void outer(int a, int b, int c)
{
  middle(a+b, b+c);
}

int main()
{
  outer(2,3,5);
  return(EXIT_SUCCESS);
}

#define STACK_TRACE_ON и #include "stack_trace.h" из RosettaCode.org, затем вставьте BEGIN(f )/ENDs, где требуется:

#include <stdio.h>
#include <stdlib.h>

#define STACK_TRACE_ON /* compile in these "stack_trace" routines */
#include "stack_trace.h"

void inner(int k)
BEGIN(inner)
   print_indent(); printf("*** Now dump the stack ***\n");
   print_stack_trace();
   for(;;){} /* hang */
END

void middle(int x, int y)
BEGIN(middle)
  inner(x*y);
END

void outer(int a, int b, int c)
BEGIN(outer)
  middle(a+b, b+c);
END

int main()
BEGIN(main)
  stack_trace.on = TRUE; /* turn on runtime tracing */
  outer(2,3,5);
  stack_trace.on = FALSE;
  RETURN(EXIT_SUCCESS);
END

Производит:

stack_trace_test.c:19: BEGIN outer[0x80487b4], stack(depth:1, size:60)
stack_trace_test.c:14:   BEGIN middle[0x8048749], stack(depth:2, size:108)
stack_trace_test.c:8:     BEGIN inner[0x80486d8], stack(depth:3, size:156)
stack_trace_test.c:8:       *** Now dump the stack ***
stack_trace_test.c:8:   inner[0x80486d8]        --- stack(depth:4, size:156) ---
stack_trace_test.c:14:  middle[0x8048749]       --- stack(depth:3, size:108) ---
stack_trace_test.c:19:  outer[0x80487b4]        --- stack(depth:2, size:60) ---
stack_trace_test.c:24:  main[0x804882a] --- stack(depth:1, size:0) ---
stack_trace_test.c:8:       --- (depth 4) ---

Хорошо отполированная [с открытым исходным кодом] версия этого метода BEGIN ~ END была бы идеальной. (Особенно, если у него есть предложение «НАКОНЕЦ» для обработки исключений).

Подсказки/URL приветствуются.

person NevilleDNZ    schedule 19.07.2010
comment
@ jsl4tv - я использовал одну реализацию несколько лет назад, она работала даже на K&R C, но она не была с открытым исходным кодом ... следовательно, собака съела исходный код! - person NevilleDNZ; 21.07.2010
comment
Единственное, что мне не хватает в коде rosetta, ну, главное, чего не хватает, так это кода для автоматической обработки/украшения исходного кода. Конечно, тогда мой следующий запрос функции будет сложным ... и параметры тоже. Я думаю, вы могли бы получить это переносимым способом, сделав все функции переменными и используя вежливый/стандартный доступ к аргументам. Большое дело! - person jsl4tv; 21.07.2010
comment
@jsl4tv - 1) можно сделать переработанную версию отступа (1L) для вставки BEGIN/END, но упрощенный инструмент можно сделать в sed||awk||python и т. д.; 2) re: args, возможно, аргументы можно обрабатывать как printf с забавной строкой формата, например BEGIN(outer,%d,%d,%d). Это было бы сложно, но приятно. Однако мой типичный сценарий таков, что где-то - обычно во время переноса - код зависает, и ты не знаешь где!!! Трассировка - действительно хорошее начало. - person NevilleDNZ; 21.07.2010
comment
Я думаю о том, чтобы принять самостоятельный ответ... в комплекте с наградой. Концепция rosettacode [rosettacode.org/wiki/Stack_traces#Using_no_extensions] кажется наиболее завершено до сих пор. - person NevilleDNZ; 28.07.2010
comment
Однако предложение @Jonathan_Fischoff полезно: в основном используйте _penter и _pexit msvc [drdobbs.com/184403601] или опция gcc -finstrument-functions cf stackoverflow.com/questions/3315248/ - person NevilleDNZ; 28.07.2010

на Symbian было создано несколько скриптов для просмотра регистров и стека в поисках вещей, похожих на кодовые адреса.

Это не переносимо, но и не зависит от украшения кода. Это был необходимый компромисс на платформе, где количество байтов имело значение... и она не была так ограничена, как Z80! Но достаточно ограничен для компиляции без указателей фреймов и тому подобного.

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

person Will    schedule 17.07.2010
comment
При наличии выбора оформления кода C или использования другого языка: оформление кода кажется более переносимым. Особенно, если оформление кода аккуратное и его можно вставить с помощью скрипта. - person NevilleDNZ; 18.07.2010