So sehen Sie Schritt für Schritt die Ausführung jeder Zeile einer Makroerweiterung mit GDB

Lesezeit: 5 Minuten

mezdas Benutzeravatar
mezda

Ich habe ein Makro, dessen Definition etwa 50 Zeilen umfasst und viele „Wenn-sonst“-Anweisungen enthält. Diese Makrodefinition erscheint in einer .h-Datei. Ich verwende „gdb im TUI-Modus“, aber wenn die Ausführung dieses Makro erreicht, wird das Codefenster leer und kehrt erst zurück, nachdem der Makrocode ausgeführt wurde. Ich möchte die zeilenweise Ausführung des vollständigen Makrocodes sehen. Bitte lassen Sie mich wissen, wie das geht (eine Möglichkeit besteht darin, das Makro durch seine Definition im Code zu ersetzen und es dann neu zu kompilieren. Ich möchte diese Option nicht verwenden, da mein Code mehrere solcher Makros enthält).

Jede Hilfe wird sehr geschätzt. Ich freue mich darauf, die Lösung für dieses Problem zu finden. Bitte lassen Sie mich wissen, ob es für dieses Problem eine andere Möglichkeit als die Verwendung einer vorverarbeiteten Datei gibt? Ich habe einen Code, der mehrere hundert .c- und .h-Dateien umfasst.

Eine Möglichkeit besteht darin, Ihre C-Datei vollständig vorzuverarbeiten, alle darin enthaltenen Makros zu erweitern und dann die resultierende vorverarbeitete Datei zu kompilieren.

Betrachten Sie zum Beispiel dieses einfache C-Programm:

// file: prep.c
#include <stdio.h>

#define MY_BIG_MACRO \
  int i; \
  printf("integers from 0 to 9:\n"); \
  for (i = 0; i < 10; i++) \
    printf("%d ", i); \
  printf("\n");

int main(void)
{
  MY_BIG_MACRO
  return 0;
}

Kompilieren Sie es und speichern Sie die temporären Dateien (einschließlich des vorverarbeiteten Quellcodes):

gcc -Wall -O2 -g -std=c99 prep.c -o prep.exe -save-temps

Dadurch sollten Sie eine vorverarbeitete Version von erhalten prep.c, prep.i (der Kürze halber gekürzt):

# 1 "prep.c"
# 1 "C:\\MinGW\\msys\\1.0\\home\\Alex//"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "prep.c"

# 1 "c:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 1 3

...

int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (const wchar_t * __restrict__,
         const wchar_t * __restrict__, __gnuc_va_list);
# 3 "prep.c" 2
# 11 "prep.c"
int main(void)
{
  int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) printf("%d ", i); printf("\n");
  return 0;
}

Jetzt möchten Sie die #-Zeilen loswerden. Wenn sie drin bleiben, wirken sie sich auf die eine oder andere Weise auf die Debug-Informationen aus. Überraschenderweise bedeutet dies, dass das Makro nicht erweitert angezeigt wird gdb.

Gott sei Dank, grep kann helfen (ich bin kein Grep-Profi, also prüfen Sie, ob die Parameter korrekt sind, aber sie scheinen bei mir unter Windows mit MinGW x86 zu funktionieren):

grep ^[^\#].*$ prep.i > prepi.c

Dadurch erhalten Sie eine abgespeckte Version von prep.i In prepi.c:

typedef unsigned int size_t;
typedef short unsigned int wchar_t;
typedef short unsigned int wint_t;

...

int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (const wchar_t * __restrict__,
         const wchar_t * __restrict__, __gnuc_va_list);
int main(void)
{
  int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) printf("%d ", i); printf("\n");
  return 0;
}

Jetzt können Sie es kompilieren:

gcc -Wall -O2 -g -std=c99 prepi.c -o prepi.exe

Und lassen Sie es einlaufen gdb:

gdb prepi.exe

Geben Sie die folgenden Befehle ein:

b main
r
l

Dadurch wird die App ausgeführt, bis main() und listen Sie den Quellcode auf, der sich auf den erreichten Haltepunkt bezieht:

(gdb) b main
Breakpoint 1 at 0x40643f: file prepi.c, line 184.
(gdb) r
Starting program: C:\MinGW\msys\1.0\home\Alex\prepi.exe
[New Thread 7340.0x20c4]

Breakpoint 1, main () at prepi.c:184
184       int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) pri
ntf("%d ", i); printf("\n");
(gdb) l
179              const wchar_t * __restrict__, __gnuc_va_list);
180     int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (c
onst wchar_t * __restrict__,
181              const wchar_t * __restrict__, __gnuc_va_list);
182     int main(void)
183     {
184       int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) pri
ntf("%d ", i); printf("\n");
185       return 0;
186     }
(gdb)

Wie Sie sehen können, befindet sich der Makrokörper jetzt in der einfachen Ansicht.

Ein kleines Problem hierbei ist, dass mehrzeilige Makros (die Fortsetzung mit \) werden zu einer einzigen Zeile erweitert. Ich habe keine Option gefunden, sie in mehrere Zeilen zu erweitern, aber Sie können das manuell tun.

  • Möchten Sie dem Downvoter erklären, was genau an meiner Antwort falsch ist? Aus einer solchen Erklärung könnte ich einiges lernen.

    – Alexey Frunze

    2. Okt. 2012 um 8:17 Uhr

  • Vielen Dank für Ihre Zeit und Antwort. Gibt es eine andere Möglichkeit, das Problem zu lösen, als eine vorverarbeitete Datei zu verwenden? Ich habe einen Code, der mehrere hundert .c- und .h-Dateien umfasst.

    – mezda

    2. Okt. 2012 um 8:17 Uhr

  • Mir ist kein anderer Weg bekannt, obwohl es einen geben könnte (entweder mit gcc/gdb oder mit einem anderen Toolset). Verzeihung.

    – Alexey Frunze

    2. Okt. 2012 um 8:19

  • O_O, wow, das ist eine ausführliche, detaillierte und clevere Antwort. Ich kann mir nicht vorstellen, warum jemand ablehnen würde. Sehr hilfreich, vielen Dank, dass Sie sich die Zeit genommen haben, es zu veröffentlichen.

    – Leo Ufimtsev

    31. März 2016 um 18:45 Uhr

  • Es ist seltsam, dass das Makroverständnis von GDB so ist darauf beschränkt

    – Unsinn

    18. Mai 2017 um 13:28

“Man macht nicht einfach step in Makros.

Sie haben noch einige Möglichkeiten:

  1. Verwenden Sie den Präprozessor, wie von @WhozCraig empfohlen.
  2. Um den Code etwas weniger aufzublähen, konvertieren Sie Ihre Makros in Funktionen und kompilieren Sie sie erneut.
  3. Wenn Sie auf keinen Fall neu kompilieren möchten und mit dem Assembler-Code vertraut sind, können Sie ihn verwenden stepi um Ihr Makro jeweils einzeln auszuführen.

  • Ich würde mich für Nummer 3 entscheiden, bevor ich meinen eigenen Rat befolgen und anfangen würde, 20.000 Zeilen vorverarbeiteter Quellen zu durchforsten, aber ich fühle mich auf der geschäftlichen Seite eines Disassemblers wohl. Vielleicht ist es das OP auch.

    – WhozCraig

    2. Okt. 2012 um 8:01

Wenn all das nicht funktioniert, sollten Sie es wirklich wieder verwenden printf/fprintf innerhalb Ihres großen Makros.

Ich musste mich mit einem 300-Zeilen-MAKRO auseinandersetzen, das tief in der Bibliothek vergraben war. Dies war einfacher als das Kompilieren von Hand und der Umgang mit nachbearbeiteten Dateien.

1451830cookie-checkSo sehen Sie Schritt für Schritt die Ausführung jeder Zeile einer Makroerweiterung mit GDB

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy