web-dev-qa-db-fra.com

Mesurer le temps d'exécution d'une fonction dans le noyau Linux

J'utilise les hooks du module de sécurité Linux pour ajouter des fonctionnalités personnalisées à l'appel système recv (). Je veux mesurer la surcharge de cette fonctionnalité par rapport à la primitive recv (). J'ai écrit un simple serveur TCP que j'exécute avec et sans mon module. Ce serveur tcp appelle une fonction recv () "N" plusieurs fois. Il mesure le temps pris pour chaque recv avec quelque chose comme:

clock_gettime(before);
recv()
clock_gettime(after);
global_time += after - before.

Au final, j'imprime le temps moyen d'un seul recv () avec "global_time/N". Permet d'appeler cette fois l'heure "user_space_avg_recv".

Dans mon module, je souhaite placer des fonctions de mesure du temps pour calculer le temps d'exécution exact de mon hook. J'ai essayé 3 méthodes.

  1. J'ai utilisé des jiffies comme suit:

    sj = jiffies;
    my_hook();
    ej = jiffies;
    current->total_oh = ej - sj;
    

    Mais je vois qu'il n'y a pas de différence entre les valeurs sj et ej. Par conséquent, total_oh est inchangé.

  2. J'ai utilisé current_kernel_time () car je pensais qu'il renvoie l'heure en nanosecondes. Cependant, encore une fois, il n'y avait aucune différence entre le temps avant et après.

  3. J'ai utilisé get_cycles. J'imprime le nombre total de cycles lorsque le processus se termine. Cependant, lorsque je convertis ces valeurs de cycles totaux en millisecondes, elles sont bien supérieures à la valeur "user_space_avg_recv". Cela n'a pas de sens car la valeur mesurée à l'intérieur du noyau est toujours inférieure à la valeur de temps mesurée depuis l'espace utilisateur. Cela pourrait signifier que je ne mesure pas en utilisant une API correcte ou que je fais une erreur lors de la conversion de la valeur de cycles en millisecondes.

J'utilise essentiellement la formule suivante pour convertir les cycles en millisecondes:

avg overhead of my hook in milliseconds = 
             (((cycles / 2.99) / 10^6) / N)

2.99 car ma fréquence d'horloge est de 2.99Ghz

Des points:

  • Mon programme d'espace utilisateur est lié à un seul cœur en utilisant une affinité définie.

  • J'utilise le noyau 2.6.22.14

  • Pour empêcher le noyau de changer de contexte à l'intérieur de mon hook, j'utilise preempt_disable () et preempt_enable (). Ainsi, il ne comptera pas les temps d'exécution des autres threads du noyau. Même dans ce cas, étant donné que mon hook utilise des E/S, mon thread peut libérer le contrôle volontairement ou une interruption peut se produire et augmenter le nombre total de cycles.

Question: Comment puis-je mesurer avec précision les temps d'exécution des fonctions à l'intérieur du noyau?

27
Methos

Vous pouvez utiliser function tracer API pour obtenir une trace de tous les appels et retours de fonction, avec des horodatages de haute précision. Cela inclut les événements d'interruption et les changements de contexte. Vous pouvez ensuite analyser la trace résultante dans l'espace utilisateur pour avoir une idée précise de la durée d'exécution de votre fonction.

Si vous ne pouvez pas utiliser l'API de suivi de fonction, vous pouvez appeler l'appel do_gettimeofday() pour obtenir un horodatage à la microseconde, ou getnstimeofday() pour une résolution nanoseconde. Ce sont les mêmes fonctions que l'appel de l'espace utilisateur gettimeofday() utilise en interne. Bien entendu, pour des fonctions très rapides, cette précision peut ne pas être suffisante; une précision plus rapide que cela et vous devrez probablement creuser dans le code du minuteur pour voir comment il implémente les conversions de cycle. Notez également que ce n'est pas parce qu'ils ont une haute résolution qu'ils ont autant de précision - mais qu'ils devraient être utiles à des fins d'analyse comparative.

Notez que toute forme de traçage entraînera une latence supplémentaire - do_gettimeofday() nécessite un certain nombre d'opérations atomiques de comparaison et d'échange, et ftrace met le code de journalisation sur chaque fonction unique avant - et post-amble . Vous devez en tenir compte lors de l'interprétation des résultats.

21
bdonlan

Je ne suis pas sûr que vous obtiendrez le résultat souhaité, mais nous utilisons le code suivant pour avoir des microsecondes.

double Microsecs()
{
   static struct timeval _t;  
   static struct timezone tz;  
   gettimeofday(&_t, &tz);  
   return   (double)_t.tv_sec + (double)_t.tv_usec/(1000*1000);
}

Que vous l'appelez avant et après l'appel que vous voulez et voyez combien de fois.
Nous avons utilisé cette méthode pour évaluer IO time monitoring read/write/seek operation) afin d'optimiser les performances et nous avons de bons résultats.

HTH.

3
Mr.Gate

Avez-vous essayé d'utiliser OProfile?

0
Dan