Existe-t-il un moyen de définir un point d’arrêt dans gdb qui soit conditionnel à la stack d’appels?

Je suis en train de déboguer C ++ dans gdb 7.1 sous Linux.

J’ai une fonction a() qui est appelée à plusieurs endroits dans le code. Je veux définir un point d’arrêt dans celui-ci, mais uniquement s’il a été appelé depuis b() . Y a-t-il un moyen de le faire?

Est-il possible de le faire uniquement si b() été appelé à partir de c() , et ainsi de suite à l’infini?

Le besoin que vous décrivez apparaît assez souvent, généralement dans le contexte où some_utility_fn souvent appelé, mais vous n’êtes intéressé que par l’appel qui vient de some_other_fn .

Vous pourriez probablement créer un script pour toute cette interaction en utilisant le nouveau support Python intégré dans GDB à partir du tronc CVS.

Sans Python, ce que vous pouvez faire est limité, mais la technique habituelle consiste à avoir un point d’arrêt désactivé sur a() et à l’activer à partir d’une commande attachée à un point d’arrêt sur b() .

Voici un exemple:

 int a(int x) { return x + 1; } int b() { return a(1); } int call_a_lots() { int i, sum = 0; for (i = 0; i < 100; i++) sum += a(i); } int main() { call_a_lots(); return b(); } gcc -g tc gdb -q ./a.out Reading symbols from /tmp/a.out...done. (gdb) break a Breakpoint 1 at 0x4004cb: file tc, line 3. (gdb) disable 1 (gdb) break b Breakpoint 2 at 0x4004d7: file tc, line 8. (gdb) command 2 >silent >enable 1 >continue >end (gdb) run Breakpoint 1, a (x=1) at tc:3 3 return x + 1; (gdb) bt #0 a (x=1) at tc:3 #1 0x00000000004004e1 in b () at tc:8 #2 0x000000000040052c in main () at tc:21 (gdb) q 

Voilà: nous nous sums arrêtés sur a() appelé de b() , en ignorant les 100 derniers appels à a() .

J’ai testé cela sur gdb 7.6 qui est déjà disponible mais cela ne fonctionne pas sur gdb 7.2 et probablement sur gdb 7.1:

Donc, c’est main.cpp:

 int a() { int p = 0; p = p +1; return p; } int b() { return a(); } int c() { return a(); } int main() { c(); b(); a(); return 0; } 

Puis g ++ -g main.cpp

C’est mon_check.py:

 class MyBreakpoint (gdb.Breakpoint): def stop (self): if gdb.selected_frame().older().name()=="b": gdb.execute("bt") return True else: return False MyBreakpoint("a") 

Et voici comment cela fonctionne:

 4>gdb -q -x my_check.py ./a.out Reading symbols from /home/a.out...done. Breakpoint 1 at 0x400540: file main.cpp, line 3. (gdb) r Starting program: /home/a.out #0 a () at main.cpp:3 #1 0x0000000000400559 in b () at main.cpp:10 #2 0x0000000000400574 in main () at main.cpp:21 Breakpoint 1, a () at main.cpp:3 3 int p = 0; (gdb) c Continuing. [Inferior 1 (process 16739) exited normally] (gdb) quit 

Une solution plus simple que les scripts Python consiste à utiliser un point d’arrêt temporaire .

Cela ressemble à ceci:

 b ParentFunction command 1 tb FunctionImInterestdIn c end 

Chaque fois que vous définissez une fonction ParentFunction , vous définissez un point d’arrêt ponctuel sur la fonction qui vous intéresse réellement, puis vous continuez à exécuter (probablement jusqu’à ce que vous atteignez ce point d’arrêt).

Étant donné que vous interrompez exactement une fois sur FunctionImInterestdIn , cela ne fonctionnera pas si FunctionImInterestdIn est appelé plusieurs fois dans le contexte de ParentFunction et que vous souhaitez interrompre chaque appel.

Je ne sais pas comment le faire par GDB.
Mais vous pouvez déclarer une variable globale comme:

 bool call_a = false; 

et quand b appelle un

 call_a = true; a(); 

et mettez call_a à false quand une autre fonction appelle a () ou après votre point d’arrêt

puis utilisez le point d’arrêt de la condition

 break [line-number] if call_a == true 

gdb peut gérer cela directement maintenant sans avoir besoin de Python. Faites juste ceci:

 ba if $_caller_is("b") 

Un facile pour le arm est:

Définissez le point d’arrêt dans la fonction qui vous intéresse.

 break a 

Attachez une commande gdb à ce point d’arrêt.

 command 1 up 1 if $lr == 0x12345678 echo match \n down 1 else echo no match \n echo $lr \n down 1 cont end end 

Chaque fois que vous arrivez dans la fonction a (), la commande affiche temporairement un cadre de stack, mettant ainsi à jour le registre de liaison. La valeur du registre de liaison des appelants peut ensuite être utilisée continue lorsque l’appelant n’est pas le chemin d’exécution dont vous avez besoin.

Prendre plaisir.