2009-02-24 7 views
0

Mon code de journalisation utilise la valeur de retour backtrace() pour déterminer la profondeur de la pile actuelle (à des fins d'impression jolies), mais je peux voir à partir du profilage que cet appel est très coûteux.Existe-t-il un moyen moins coûteux de trouver la profondeur de la pile d'appels que d'utiliser backtrace()?

Je ne pense pas qu'il existe une façon moins chère de faire cela? Notez que je ne me soucie pas des adresses d'images, mais du nombre d'entre elles. Ces fonctions de journalisation sont utilisées sur une grande base de code, donc le suivi manuel de la profondeur de la pile n'est pas vraiment une option.

Répondre

5

Marcher soi-même sur la pile est assez rapide - la plus grande partie de la lenteur dans backtrace() est de chercher des noms de symboles. Sur x86, vous pouvez effectuer les opérations suivantes:

inline uint32_t get_ebp(void) 
{ 
    __asm__ __volatile__("mov %%ebp, %%eax"); 
} 

int get_stack_depth(void) 
{ 
    uint32_t ebp = get_ebp(); 
    int stack_depth = 0; 
    while(ebp != 0) 
    { 
     ebp = *(uint32_t *)ebp; 
     stack_depth++; 
    } 
    return stack_depth; 
} 

Cela marche la chaîne de ebp pointeurs. Gardez à l'esprit que c'est extrêmement non portable. Notez également que cela ne comptera pas les fonctions qui ont été intégrées ou optimisées (bien sûr, backtrace() a le même problème).

Un autre problème important est la condition de terminaison - une fois que vous faites marche arrière jusqu'à main(), il n'y a souvent aucune garantie sur ce que vous trouverez dans la pile. Donc, si libc ne met pas un pointeur de frame nul, vous allez très probablement segfault. Vous pouvez obtenir la valeur de terminaison en la regardant au tout début de main().

2

Si vos jolies fonctions d'impression sont raisonnablement contenues, passez le retrait (ou la taille d'indentation) en tant que paramètre et augmentez-le simplement lorsque vous appelez d'autres fonctions d'affichage.

+0

Si l'idée d'ajouter un autre paramètre à votre fonction vous fait peur, vous pouvez également utiliser une variable statique. Ce n'est généralement pas une bonne idée, à mon avis. Cela fonctionnerait de la même manière que la solution de Douglas. – Brian

2

Vous ne pouvez pas simplement porter une variable TLS autour de vous appelée "profondeur" et l'incrémenter/décrémenter chaque fonction? Bien que vous puissiez écrire votre propre code pour parcourir la pile plus rapidement, il sera toujours plus lent que de simplement transporter la variable avec vous.

+0

Non, je pense que l'incrémentation/décrémentation d'une variable TLS à chaque appel de fonction sera beaucoup plus onéreuse, en fonction de la fréquence à laquelle vous devez faire un backtrace. –

+0

Il effectuait un backtrace * à chaque appel * - il n'y a aucun moyen que integer inc/dec soit plus lent –

2

Pour les architectures de bras:

register unsigned long *rfp asm("fp"); 
unsigned long *fp = rfp; 
unsigned long depth = 0; 

while(fp) 
{ 
    fp = (unsigned long *)(*(fp -3)); 
    depth++; 
} 

return depth; 
Questions connexes