2013-08-03 3 views
3

J'écris actuellement un algorithme multithread, hautement efficace et évolutif. Parce que je dois deviner un paramètre pour le code et je ne suis pas sûr de la façon dont le calcul fonctionne sur un ensemble de données spécifique, je voudrais regarder une variable. Le test ne fonctionne qu'avec un monde réel, un énorme ensemble de données. Il est possible d'analyser les données collectées après le profilage. Imaginez l'exemple, le code simple suivant (le code réel peut contenir des points de surveillance multiples:Surveillance efficace des variables en C/C++

// function get's called by loops of multiple threads 
void payload(data_t* data, double threshold) { 
    double value = calc(data); 
    // here I want to watch the value 
    if (value < threshold) { 
     doSomething(data); 
    } else { 
     doSomethingElse(data); 
    } 
} 

Je pensais que sur les approches suivantes:

  1. En utilisant cout ou d'autres sorties système
  2. Utilisez une sortie binaire (fichier, réseau)
  3. Définir un point d'arrêt via gdb/LLDB
  4. Utiliser observation variables + journalisation via gdb/LLDB

Je ne suis pas satisfait des résultats parce que: Pour utiliser 1. et 2. Je dois changer le code, mais c'est une tâche de débogage/évaluation. En outre 1. nécessite un verrouillage et 1. + 2. nécessite des opérations d'E/S, ce qui ralentit fortement l'ensemble du code et rend presque impossible le test avec des données réelles. 3. est également trop lent. Pour utiliser 4., je dois connaître l'adresse de la variable parce que ce n'est pas une variable globale, mais parce que les threads sont créés par un planificateur dynamique, cela nécessiterait de casser + stepping pour chaque thread. Donc, ma conclusion est, que j'ai besoin d'un profileur/débogueur qui fonctionne au niveau du code machine et dumps/logs/watches la variable sans conversion double-chaîne et est très efficace, ou pour résumer avec d'autres mots: voudrais profiler l'état interne de mon algorithme sans lourd ralentissement et sans faire de modification en profondeur. Est-ce que quelqu'un connaît un outil qui est capable de cela?

+0

Sur Linux, le débogueur 'de gdb' a * watchpoints * (passe sa commande' watch') , implémenté en utilisant 'ptrace' –

+0

Si la fonction est appelée par plusieurs threads, il y a peut-être plusieurs emplacements où' value' est stocké/mis à jour, n'est-ce pas? Pourriez-vous créer un tableau statique (emplacement connu) avec N éléments, où N = nombre de threads; puis copiez 'value' dans l'emplacement du tableau approprié pour le thread qui l'a appelé? Ensuite, vous n'avez qu'à regarder ce tableau pour toutes les valeurs. Si vous ne vous souciez pas si vous avez seulement une valeur, il pourrait s'agir d'un emplacement simple plutôt que d'un tableau - et encore une fois, vous pourriez alors regarder avec 'gdb'. – Floris

+0

Il cesse certainement d'être très efficace et évolutif une fois que vous commencez à faire l'une de ces choses. Si vous ne pouvez pas utiliser une fonctionnalité de système d'exploitation intégrée, similaire à un compteur de performance ou à ETW sous Windows, vous devez commencer à penser à un tampon circulaire lu par un autre thread. –

Répondre

1

vous pouvez définir watchpoint de matériel à l'aide débogueur gdb, par exemple si vous avez

bool b; 
variables

et que vous souhaitez être averti chaque fois que la valeur de celui-ci a chenged (par un fil) vous déclarer comme ce point d'observation:

(gdb) watch *(bool*)0x7fffffffe344 

exemple:

[email protected]:~# gdb prog 
GNU gdb (GDB) 7.5-ubuntu 
Copyright ... 
For bug reporting instructions, please see: 
<http://www.gnu.org/software/gdb/bugs/>... 
Reading symbols from /dist/Debug/GNU-Linux-x86/cppapp_socket5_ipaddresses...done. 
(gdb) watch *(bool*)0x7fffffffe344 
Hardware watchpoint 1: *(bool*)0x7fffffffe344 
(gdb) start 
Temporary breakpoint 2 at 0x40079f: file main.cpp, line 26. 
Starting program: /dist/Debug/GNU-Linux-x86/cppapp_socket5_ipaddresses 

Hardware watchpoint 1: *(bool*)0x7fffffffe344 

Old value = true 
New value = false 
main() at main.cpp:50 
50     if (strcmp(mask, "255.0.0.0") != 0) { 
(gdb) c 
Continuing. 
Hardware watchpoint 1: *(bool*)0x7fffffffe344 

Old value = false 
New value = true 
main() at main.cpp:41 
41    if (ifa ->ifa_addr->sa_family == AF_INET) { // check it is IP4 
(gdb) c 
Continuing. 
mask:255.255.255.0 
eth0 IP Address 192.168.1.5 
[Inferior 1 (process 18146) exited normally] 
(gdb) q 
3

OK, cela a pris du temps mais maintenant je suis capable de présenter une solution à mon problème. Ça s'appelle des traces. Au lieu de casser le programme à chaque fois, il est plus léger et (idéalement) ne change pas trop les performances/timing. Il ne nécessite pas de modifications de code. Voici comment les utiliser avec gdb:

Assurez-vous d'avoir compilé votre programme avec les symboles de débogage (en utilisant le drapeau -g). Maintenant, démarrez le serveur gdb et de fournir un port réseau (par exemple 10000) et les arguments du programme:

gdbserver :10000 ./program --parameters you --want --to use 

Maintenant, passez à une deuxième console et commencer à gdb (paramètres du programme ne sont pas nécessaires ici):

gdb ./program 

Toutes les commandes suivantes sont entrées dans l'interface de ligne de commande gdb.Donc, nous allons connecter au serveur:

target remote :10000 

Après avoir obtenu la confirmation de connexion, utilisez trace ou ftrace pour définir un point de trace à un emplacement de source spécifique (essayez ftrace d'abord, il devrait être plus rapide, mais ne fonctionne pas sur tous les plates-formes):

trace source.c:127 

Ceci devrait créer le point de trace # 1. Vous pouvez maintenant configurer une action pour ce point de trace. Ici, je veux recueillir les données de myVariable

action 1 
collect myVariable 
end 

Si attendez beaucoup de données ou si vous voulez utiliser les données plus tard (après le redémarrage), vous pouvez définir un fichier de trace binaire:

tsave trace.bin 

Maintenant, début traçage et exécuter le programme:

tstart 
continue 

Vous pouvez attendre la sortie du programme ou d'interrompre votre programme en utilisant CTRL-C (toujours sur la console de gDB, et non sur le côté serveur). Continuez en disant gdb que vous voulez arrêter le suivi:

tstop 

Nous arrivons maintenant à la partie la plus délicate et je ne suis pas vraiment heureux avec le code suivant, car il est vraiment lent:

set pagination off 
set logging file trace.txt 
tfind start 
while ($trace_frame != -1) 
set logging on 
printf "%f\n", myVariable 
set logging off 
tfind 
end 

Cette décharge toutes données variables dans un fichier texte. Vous pouvez ajouter un filtre ou une préparation ici. Maintenant vous avez terminé et vous pouvez quitter gdb. Cela va arrêter également le serveur:

quit 

Pour une documentation détaillée en particulier pour les explications de filtrage et plus avancés positions de trace, vous pouvez visiter le document suivant: http://sourceware.org/gdb/onlinedocs/gdb/Tracepoints.html

Pour isoler le fichier de trace écrite de l'exécution de votre programme, Vous pouvez utiliser des groupes de contrôle ou un autre ordinateur connecté au réseau. Lorsque vous utilisez un autre ordinateur, vous devez ajouter l'hôte aux informations de port (par exemple 192.168.1.37:10000). Pour charger un fichier de trace binaire plus tard, il suffit de commencer gdb comme indiqué ci-dessus (oubliez le serveur) et changer la cible:

gdb ./program 
target tfile trace.bin 
Questions connexes