Utiliser ftrace

ftrace est un outil de débogage permettant de comprendre ce qui se passe dans le kernel Linux. Les sections suivantes décrivent les fonctionnalités de base de ftrace, l'utilisation de ftrace avec atrace (qui capture les événements du noyau) et la ftrace dynamique.

Pour en savoir plus sur les fonctionnalités avancées de ftrace qui ne sont pas disponibles dans systrace, consultez la documentation ftrace sur <kernel tree>/Documentation/trace/ftrace.txt.

Capturer des événements du kernel avec atrace

atrace (frameworks/native/cmds/atrace) utilise ftrace pour capturer les événements du noyau. À son tour, systrace.py (ou run_systrace.py dans les versions ultérieures de Catapult) utilise adb pour exécuter atrace sur l'appareil. atrace effectue les opérations suivantes:

  • Configure le traçage en mode utilisateur en définissant une propriété (debug.atrace.tags.enableflags).
  • Active la fonctionnalité ftrace souhaitée en écrivant dans les nœuds sysfs ftrace appropriés. Toutefois, comme ftrace est compatible avec davantage de fonctionnalités, vous pouvez définir vous-même certains nœuds sysfs, puis utiliser atrace.

À l'exception du traçage au démarrage, utilisez atrace pour définir la propriété sur la valeur appropriée. La propriété est un masque de bits, et il n'existe aucun moyen efficace de déterminer les valeurs correctes, si ce n'est en examinant l'en-tête approprié (qui peut changer entre les versions d'Android).

Activer les événements ftrace

Les nœuds sysfs ftrace se trouvent dans /sys/kernel/tracing et les événements de suivi sont divisés en catégories dans /sys/kernel/tracing/events.

Pour activer les événements par catégorie, utilisez:

echo 1 > /sys/kernel/tracing/events/irq/enable

Pour activer les événements par événement, utilisez:

echo 1 > /sys/kernel/tracing/events/sched/sched_wakeup/enable

Si des événements supplémentaires ont été activés en écrivant dans des nœuds sysfs, ils ne seront pas réinitialisés par atrace. Un modèle courant pour la mise en service d'un appareil Qualcomm consiste à activer les tracepoints kgsl (GPU) et mdss (pipeline d'affichage), puis à utiliser atrace ou systrace:

adb shell "echo 1 > /sys/kernel/tracing/events/mdss/enable"
adb shell "echo 1 > /sys/kernel/tracing/events/kgsl/enable"
./systrace.py sched freq idle am wm gfx view binder_driver irq workq ss sync -t 10 -b 96000 -o full_trace.html

Vous pouvez également utiliser ftrace sans atrace ni systrace, ce qui est utile lorsque vous souhaitez des traces réservées au noyau (ou si vous avez pris le temps d'écrire manuellement la propriété de traçage en mode utilisateur). Pour exécuter uniquement ftrace:

  1. Définissez la taille de la mémoire tampon sur une valeur suffisamment importante pour votre trace:
    echo 96000 > /sys/kernel/tracing/buffer_size_kb
    
  2. Activez le traçage:
    echo 1 > /sys/kernel/tracing/tracing_on
    
  3. Exécutez votre test, puis désactivez le traçage:
    echo 0 > /sys/kernel/tracing/tracing_on
    
  4. Videz la trace:
    cat /sys/kernel/tracing/trace > /data/local/tmp/trace_output
    

trace_output fournit la trace sous forme de texte. Pour le visualiser à l'aide de Catapult, récupérez le dépôt Catapult sur GitHub et exécutez trace2html:

catapult/tracing/bin/trace2html ~/path/to/trace_file

Par défaut, trace_file.html est écrit dans le même répertoire.

Mettre en corrélation des événements

Il est souvent utile d'examiner simultanément la visualisation Catapult et le journal ftrace. Par exemple, certains événements ftrace (en particulier ceux spécifiques au fournisseur) ne sont pas visualisés par Catapult. Toutefois, les codes temporels de Catapult sont relatifs au premier événement de la trace ou à un code temporel spécifique extrait par atrace, tandis que les codes temporels bruts de ftrace sont basés sur une source d'horloge absolue particulière dans le noyau Linux.

Pour trouver un événement ftrace donné à partir d'un événement Catapult:

  1. Ouvrez le journal ftrace brut. Les traces des versions récentes de systrace sont compressées par défaut :
    • Si vous avez capturé votre trace système avec --no-compress, elle se trouve dans le fichier HTML, dans la section commençant par BEGIN TRACE.
    • Dans le cas contraire, exécutez html2trace à partir de l'arborescence Catapult (tracing/bin/html2trace) pour décompresser la trace.
  2. Recherchez l'horodatage relatif dans la visualisation Catapult.
  3. Recherchez une ligne au début de la trace contenant tracing_mark_sync. Exemple :
    <5134>-5134  (-----) [003] ...1    68.104349: tracing_mark_write: trace_event_clock_sync: parent_ts=68.104286
    

    Si cette ligne n'existe pas (ou si vous avez utilisé ftrace sans atrace), les temps seront relatifs au premier événement du journal ftrace.
    1. Ajoutez le code temporel relatif (en millisecondes) à la valeur dans parent_ts (en secondes).
    2. Recherchez le nouveau code temporel.

Ces étapes devraient vous placer à proximité de l'événement (ou du moins très près de celui-ci).

Utiliser ftrace dynamique

Lorsque systrace et ftrace standard sont insuffisants, il existe un dernier recours: le ftrace dynamique. La ftrace dynamique implique la réécriture du code du noyau après le démarrage. Par conséquent, elle n'est pas disponible dans les noyaux de production pour des raisons de sécurité. Toutefois, chaque bug de performances difficile en 2015 et 2016 a finalement été identifié à l'aide de ftrace dynamique. Il est particulièrement efficace pour déboguer les veilles ininterruptibles, car vous pouvez obtenir une trace de pile dans le noyau chaque fois que vous appuyez sur la fonction déclenchant la veille ininterruptible. Vous pouvez également déboguer des sections avec les interruptions et les préemptions désactivées, ce qui peut être très utile pour prouver les problèmes.

Pour activer ftrace dynamique, modifiez le fichier defconfig de votre kernel:

  1. Supprimez CONFIG_STRICT_MEMORY_RWX (s'il est présent). Si vous utilisez la version 3.18 ou ultérieure et arm64, elle n'est pas disponible.
  2. Ajoutez les éléments suivants: CONFIG_DYNAMIC_FTRACE=y, CONFIG_FUNCTION_TRACER=y, CONFIG_IRQSOFF_TRACER=y, CONFIG_FUNCTION_PROFILER=y et CONFIG_PREEMPT_TRACER=y.
  3. Recompilez et démarrez le nouveau noyau.
  4. Exécutez la commande suivante pour rechercher les traceurs disponibles:
    cat /sys/kernel/tracing/available_tracers
    
  5. Vérifiez que la commande renvoie function, irqsoff, preemptoff et preemptirqsoff.
  6. Exécutez la commande suivante pour vous assurer que ftrace dynamique fonctionne:
    cat /sys/kernel/tracing/available_filter_functions | grep <a function you care about>
    

Une fois ces étapes effectuées, vous disposez de ftrace dynamique, du profileur de fonction, du profileur irqsoff et du profileur preemptoff. Nous recommandons vivement de lire la documentation ftrace sur ces sujets avant de les utiliser, car ils sont puissants, mais complexes. irqsoff et preemptoff sont principalement utiles pour confirmer que les pilotes peuvent laisser les interruptions ou la préemption désactivées trop longtemps.

Le profileur de fonction est la meilleure option pour les problèmes de performances et est souvent utilisé pour déterminer où une fonction est appelée.


Si les données du profileur de fonction ne sont pas assez spécifiques, vous pouvez combiner les points de trace ftrace avec le profileur de fonction. Les événements ftrace peuvent être activés exactement de la même manière que d'habitude, et ils seront entrelacés avec votre trace. Cela est très utile si un sommeil long et ininterruptible se produit occasionnellement dans une fonction spécifique que vous souhaitez déboguer: définissez le filtre ftrace sur la fonction souhaitée, activez les points de trace et effectuez une trace. Vous pouvez analyser la trace obtenue avec trace2html, rechercher l'événement souhaité, puis obtenir les traces de pile à proximité dans la trace brute.

Utiliser lockstat

Parfois, ftrace ne suffit pas et vous devez vraiment déboguer ce qui semble être un conflit de verrouillage du kernel. Il existe une autre option de kernel à essayer : CONFIG_LOCK_STAT. Il s'agit d'une solution de dernier recours, car il est extrêmement difficile de la faire fonctionner sur des appareils Android, car elle gonfle la taille du noyau au-delà de ce que la plupart des appareils peuvent gérer.

Toutefois, lockstat utilise l'infrastructure de verrouillage de débogage, qui est utile pour de nombreuses autres applications. Toute personne travaillant sur la mise en service de l'appareil doit trouver un moyen de faire fonctionner cette option sur chaque appareil, car il y aura un moment où vous penserez : "Si seulement je pouvais activer LOCK_STAT, je pourrais confirmer ou infirmer que c'est le problème en cinq minutes au lieu de cinq jours."


Si vous pouvez démarrer un noyau avec l'option de configuration, le traçage des verrouillages est semblable à ftrace:

  1. Activez le traçage:
    echo 1 > /proc/sys/kernel/lock_stat
    
  2. Exécutez votre test.
  3. Pour désactiver le traçage:
    echo 0 > /proc/sys/kernel/lock_stat
    
  4. Videz votre trace:
    cat /proc/lock_stat > /data/local/tmp/lock_stat
    

Pour obtenir de l'aide pour interpréter le résultat, consultez la documentation de lockstat sur <kernel>/Documentation/locking/lockstat.txt.

Utiliser des tracepoints de fournisseur

Utilisez d'abord les tracepoints en amont, mais vous devrez parfois utiliser des tracepoints du fournisseur:

  { "gfx",        "Graphics",         ATRACE_TAG_GRAPHICS, {
        { OPT,      "events/mdss/enable" },
        { OPT,      "events/sde/enable" },
        { OPT,      "events/mali_systrace/enable" },
    } },

Les points de trace sont extensibles par le service HAL, ce qui vous permet d'ajouter des points/catégories de trace spécifiques à l'appareil. Les points de trace sont intégrés à Perfetto, atrace/systrace et à l'application de traçage système sur l'appareil.

Les API permettant d'implémenter des tracepoints/catégories sont les suivantes:

  • listCategories()génère (vec<TracingCategory> categories);
  • enableCategories(vec<string> categories) génère (Status status);
  • disableAllCategories() génère (Status status);
Pour en savoir plus, consultez la définition de HAL et l'implémentation par défaut dans AOSP: