Est-il possible de définir un point d'arrêt dans gdb, qui est subordonné à la pile d'appel?
je suis en train de déboguer C++ dans la gdb 7.1 sur Linux.
j'ai une fonction a()
qui est appelée à de nombreux endroits dans le code. Je veux mettre un point d'arrêt, mais seulement si elle a été appelée à partir de b()
. Est-il possible de le faire?
Est-il possible de le faire uniquement si b()
a été appelé de c()
, et ainsi de suite ad infinitum?
6 réponses
le besoin que vous décrivez apparaît assez souvent, habituellement dans le contexte de some_utility_fn
être appelé beaucoup, mais vous êtes seulement intéressé par l'appel qui vient de some_other_fn
.
vous pourriez probablement script toute cette interaction en utilisant le nouveau support Python intégré dans GDB à partir du tronc CVS.
Sans Python, vous êtes limité dans ce que vous pouvez faire, mais la technique habituelle est d'avoir un désactivé point d'arrêt sur a()
, et de l'activer à partir d'une commande, attaché à 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 t.c
gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) break a
Breakpoint 1 at 0x4004cb: file t.c, line 3.
(gdb) disable 1
(gdb) break b
Breakpoint 2 at 0x4004d7: file t.c, line 8.
(gdb) command 2
>silent
>enable 1
>continue
>end
(gdb) run
Breakpoint 1, a (x=1) at t.c:3
3 return x + 1;
(gdb) bt
#0 a (x=1) at t.c:3
#1 0x00000000004004e1 in b () at t.c:8
#2 0x000000000040052c in main () at t.c:21
(gdb) q
Voila: nous avons arrêté sur a()
appelé b()
, ignorant 100 précédents appels à a()
.
j'ai testé cela sur gdb 7.6 qui est déjà disponible, mais il ne fonctionne pas sur gdb 7.2 et probablement sur gdb 7.1:
Donc c'est le principal.rpc:
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;
}
g++ -g principal.rpc
C'est my_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 voilà 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 Python scripting est d'utiliser un point de rupture temporaire.
Il ressemble à ceci:
b ParentFunction
command 1
tb FunctionImInterestedIn
c
end
Chaque fois que vous cassez dans ParentFunction
, vous allez définir un point de rupture unique sur la fonction qui vous intéresse réellement, puis continuer à courir (probablement jusqu'à ce que vous atteigniez ce point de rupture).
puisque vous casserez exactement une fois sur FunctionImInterestedIn
, cela ne fonctionnera pas si FunctionImInterestedIn
est appelé plusieurs fois dans le contexte de ParentFunction
et vous voulez pour rompre sur chaque invocation.
Je ne sais pas comment le faire par gdb.
Mais vous pouvez déclarer une variable globale comme:
bool call_a = false;
et lorsque b de l'appel d'une
call_a = true;
a();
et définissez call_a à false quand une autre fonction appelle a () ou après votre point de rupture
alors utilisez le point de rupture de condition
break [line-number] if call_a == true
facile pour les bras:
Définir le point d'arrêt dans la fonction qui vous intéresse.
break a
attachez une commande gdb à ce point de rupture.
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
lorsque vous arrivez dans la fonction a (), la commande affiche temporairement un cadre de pile, mettant ainsi à jour le registre de liens. Les appelants lien enregistrer la valeur peut ensuite être utilisé continuer lorsque l'appelant n'est pas l'exécution chemin d'accès dont vous avez besoin.
Profiter.
gdb peut gérer cela directement maintenant, sans besoin de Python. Faites simplement ceci:
b a if $_caller_is("b")