Очевидный вопрос заключается в том, является ли ваша программа (и все библиотеки, которые она вызывает) действительно независимой от архитектуры или даже детерминированной (например, у вас может быть нестабильная сортировка или даже где-то потоки), или если у вас просто есть неинициализированная переменная где-то.
Предполагая, что вы полагаете, что ваша программа полностью детерминирована, и вам действительно нужна трассировка данных по каждому назначению, способ сделать это можно с помощью системы преобразования программ . Такой инструмент принимает шаблоны «если вы видите это, замените их», используя синтаксис поверхности вашего целевого языка (в данном случае, C). Вы управляете своей программой, используя правила преобразования, чтобы автоматически находить все места, где требуется инструментарий, и вставляете их туда, компилируете и запускаете инструментированную программу, а затем выбрасываете инструментированную версию, получив ваш след.
Наш инструментарий реинжиниринга программного обеспечения DMS может сделать это. То, что вы хотите сделать, это заменить каждое назначение комбинацией назначения и printf.
Правило перезаписи DMS для достижения этой цели:
rule insert_tracing_printfs(l: lhs, e: expression): expression -> expression =
" \lhs=\e " -> "print_and_assign_int(\tostring\(\lhs\),&\lhs,\e)" if int_type(lhs);
Основной формат правил: ifyouseethis -> заменяется этим , если somecondition . Кавычки на самом деле являются мета-кавычками и позволяют встроить C-синтаксис в язык правил. \ Это побег из синтаксиса C
обратно в правила языка.
Эти правила действуют на абстрактных синтаксических деревьях, сгенерированных DMS в результате анализа (предварительно обработанного) исходного кода на языке C. Этот конкретный шаблон правил точно соответствует исходному коду для точного синтаксиса lhs = * e * для всех допустимых форм lhs и e . Когда он находит такое совпадение, он заменяет присвоение вызовом функции.
это происходит при выполнении задания, а также распечатывает значение вашей трассировки.
Функция \ tostring берет дерево lhs и генерирует исходный текст, соответствующий оригиналу
выражение; это легко сделать с помощью DMS API для красивых печатных AST. Тип int _
Функция опрашивает сгенерированную DMS таблицу символов, чтобы определить, является ли тип lhs int .
Вам понадобится одно правило, подобное каждому типу данных, который вы хотите распечатать. Вам также необходимо правило для каждого типа синтаксиса присваивания (например, "=", "+ =", "% =" ...), который использует ваша программа. Итак, базовые типы данных и несколько типов синтаксиса присваивания предполагают, что вам нужно 1-2 десятка таких правил.
Вам также нужны соответствующие функции C для соответствия различным типам данных:
int print_and_assign_int(char* s, int* t, int v)
{ printf("Integer variable %s changes from %d to %d\n",s,*t,v);
*t=v;
return v;
}
(Если вам нужны номера файлов и строк, вы можете просто добавить их в качестве дополнительных аргументов в функцию печати, используя макросы препроцессора C для номеров файлов и строк.)
Для оператора C, например:
if (x=getc())
{ y="abc";
p=&y;
}
набор правил перезаписи, выполненных таким образом, автоматически выдаст что-то вроде:
if (print_and_assign_char("x", &x,getc()))
{ print_and_assign_charstar("y",&y,"abc");
print_and_assign_ptrtocharstar("p",&p,&y);
}
Вам нужно будет решить, как вы хотите распечатать назначенные значения указателя, потому что вы должны предполагать, что они не имеют эквивалентных адресов, поэтому вам, по сути, нужно напечатать значение, выбранное указателем. Это доставляет вам неприятности всякий раз, когда у вас есть пустота *; но вы можете распечатать то, что вам известно о переменной void *, например, равно NULL или нет, и это все равно будет полезным для трассировки.
Это может стоить хлопот, если вы много отлаживали. ИМХО, вам, вероятно, лучше просто прикусить пулю и отладить свой путь к решению, так как я ожидаю, что вы будете удивлены некоторой зависимостью от архитектуры.