2009-09-02 5 views
2

J'ai une application C++ qui utilise une bibliothèque tierce. Ici et là, dans mon code, il y a des appels vers cette bibliothèque. Je voudrais retracer tous ces appels. Il serait facile si ce sont des fonctions dans mon code - j'insérerais une macro qui obtiendrait le nom de la fonction en cours et l'heure de début de l'appel et les passerait à un constructeur d'objet local, puis à la sortie de l'objet serait détruit et trace les données nécessaires. La macro s'étendrait à une chaîne vide pour les configurations où je n'ai pas besoin de traçage pour éliminer le surcoût associé.Comment tracer tous les appels vers un ensemble prédéfini de fonctions en C++?

Existe-t-il un moyen facile de faire quelque chose de manière fiable pour les appels vers une bibliothèque externe? Toute l'interface à la bibliothèque que j'ai est le fichier .h avec des prototypes de fonctions inclus dans mon code.

Répondre

0

Eh bien, vous pourriez simplement ajouter une autre couche au-dessus des appels lib de 3ème partie. De cette façon, vous pouvez ajouter l'habillage de traçage sophistiqué que vous voulez.

par exemple.

struct trace 
{ 
    static void myfoo() { cout << "calling foo" << endl; foo(); } 
    // or 
    // static void myfoo() { if (_trace) {..} foo(); } 
}; 
4

Vous pouvez essayer d'écrire une bibliothèque de wrapper qui expose la même interface et redirige en interne les appels vers la bibliothèque d'origine.

Ensuite, vous pouvez facilement ajouter votre code de trace aux fonctions de l'encapsuleur. Tout ce qui change pour votre projet est la lib que vous allez lier.

Pour éviter la définition de plusieurs symboles, vous pouvez inclure l'en-tête des bibliothèques externes dans un espace de noms distinct.

EDIT:

Y compris l'en-tête de libs externe dans un espace de noms ne résout pas le problème de symbole. Vous devez utiliser une macro dans votre en-tête qui renomme la fonction d'origine et chaque occurrence dans votre code. Utilisez quelque chose comme ça pour nouvel en-tête de la bibliothèque wrapper:

#define originalExportedFunction WRAPPED_originalExportedFunction 

extern "C" int originalExportedFunction(int); 

Votre mise en œuvre dans l'emballage lib alors peut ressembler à:

extern "C" int WRAPPED_originalExportedFunction(int i) 
{ 
    //trace code here... 
    return originalExportedFunction(i); 
} 
+1

Si vous incluez les en-têtes à l'intérieur d'un espace de noms, les symboles définis dans l'en-tête seront dans un espace de noms différent des symboles définis dans la bibliothèque. –

+0

@Jon: Vous avez raison, utiliser namespace ne résout pas le problème de symbole. Mais vous pouvez toujours utiliser une approche macro qui renomme la fonction appelée et laisse votre bibliothèque exporter le nom de la fonction renommée. La fonction renommée appelle alors la fonction d'origine et il n'y a pas de conflit de symbole. –

3

Si vous arrive de travailler en utilisation unix/linux

ltrace

pour le suivi des appels bibliothèque,

strace

pour les appels système. Ce sont des commandes non dans la solution de code cependant. Vous pouvez également regarder valgrind avec l'option -callgrind pour profiler.

0

Étant donné que vous semblez connaître les fonctions que vous souhaitez appeler (et les signatures pour ces appels), vous pouvez toujours utiliser votre idée de macro/classe wrapper. Quelque chose comme:

typedef void (*pfun)(int); 

class Foo { 
    pfun call; 
    public: 
     Foo(pfun p) : call(p) {} 
     void operator()(int x) { 
      std::cout << "Start trace..." << std::endl; 
      (*call)(x); 
      std::cout << "End trace" << std::endl; 
     } 
}; 

void bar (int x) { 
    std::cout << "In bar: " << x << std::endl; 
} 

int main() { 

    Foo foo(&bar); 
    foo (42); 
    return 0; 

} 
0

Essayez de créer une macro pour toutes les API d'interface, par exemple. Supposons que le api est appelé comme:

obj->run_first(var1); 

Ensuite, créez ci-dessous macro:

#define obj->run_first(args) \ 
    dumptimestamp(__FUNCTION__, __LINE__); \ 
    obj->run_first(args);     \ 
    dumptimestamp(__FUNCTION__, __LINE__); 

Vous pouvez générer la liste des macros similaires à partir du fichier d'en-tête d'un lib car il a la liste de toutes les méthodes d'interface .

dumptimestamp vider l'horodatage avec les numéros de fonction et de ligne.

0

Si vous ne voulez pas changer votre code, alors il y a un moyen de faire une telle chose par instrumentation. Si vous êtes intéressé par cette façon, jetez un oeil à une belle boîte à outils d'instrumentation binaire dynamique appelée PIN (maintenu par Intel):

http://www.pintool.org/downloads.html

avec code PIN, vous pouvez insérer votre propre code sur la fonction entrée/sortie. Un exemple serait la capture malloc/gratuit:

http://www.pintool.org/docs/29972/Pin/html/index.html#FindSymbol

Ceci est tout à fait différente façon de tracer les appels de fonction. Mais, ça vaut le coup de jeter un oeil.

Questions connexes