Простой, универсальный и переносимый способ включения трассировки и возврата в программу на Си - PullRequest
3 голосов
/ 18 июля 2010

GNU libc backtrace и Внутрисхемные эмуляторы / отладчики не всегда доступны при переносе кода на новую платформу, особенно когда целью является микро C * 1006.* Компилятор, например, для Z80 .(Как правило, программная ошибка «просто зависает» где-то или приводит к аварийному завершению работы гаджета.)

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

Кстати: вот пара других вопросов по stackoverflow , которые связаны между собой, но они оба используют GNU GLIBC backtrace , а backtrace часто зависит от компилятора / реализации:1019 *

Ответы [ 3 ]

4 голосов
/ 19 июля 2010

Вот ядро ​​ядра моего ответа: напишите какой-нибудь код.

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

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

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 делает немного больше вычислений, чем просто инициализирует переменную.

Создать функцию для просмотра списка BLOB-объектов, проверяющих указатели и магические числа. Он должен сигнализировать об ошибке (возможно, печать в 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 аналогичен EXIT, но имеет возврат до} while (0).

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

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

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

См. Также Доступно ли какое-либо портирование backtrace для uclibc?

2 голосов
/ 20 июля 2010

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

Пример, приведенный следующий классический код C со встроенным « hang »:

#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);
}

# определяет STACK_TRACE_ON и #include "stack_trace.h" из RosettaCode.org , затем вставляет BEGIN (f) / END, где это необходимо:

#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) ---

Хорошо отшлифованная [open source] версия этого метода BEGIN ~ END была бы идеальной. (Esp, если у него есть предложение "FINALLY" для обработки исключений).

Полезные советы / URL.

0 голосов
/ 18 июля 2010

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

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

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

...