ftrace verwenden

Ftrace ist ein Debugging-Tool, mit dem Sie nachvollziehen können, was im Linux-Kernel passiert. In den folgenden Abschnitten werden die grundlegenden Funktionen von ftrace, die Verwendung von ftrace mit atrace (das Kernelereignisse erfasst) und dynamische ftrace beschrieben.

Weitere Informationen zu erweiterten Ftrace-Funktionen, die nicht über systrace verfügbar sind, finden Sie in der Ftrace-Dokumentation unter <kernel tree>/Documentation/trace/ftrace.txt.

Kernelereignisse mit atrace erfassen

atrace (frameworks/native/cmds/atrace) verwendet ftrace, um Kernelereignisse zu erfassen. Systrace.py (oder run_systrace.py in neueren Versionen von Catapult) verwendet wiederum adb, um atrace auf dem Gerät auszuführen. atrace führt folgende Aktionen aus:

  • Richtet die Nutzermodus-Tracing-Funktion ein, indem eine Eigenschaft (debug.atrace.tags.enableflags) festgelegt wird.
  • Aktiviert die gewünschte ftrace-Funktion, indem in die entsprechenden ftrace-Sysfs-Knoten geschrieben wird. Da ftrace jedoch mehr Funktionen unterstützt, können Sie einige sysfs-Knoten selbst festlegen und dann atrace verwenden.

Verwenden Sie mit Ausnahme der Ablaufverfolgung zur Bootzeit atrace, um die Eigenschaft auf den entsprechenden Wert festzulegen. Die Property ist eine Bitmaske und es gibt keine gute Möglichkeit, die richtigen Werte zu ermitteln, außer in der entsprechenden Kopfzeile nachzusehen, die sich zwischen Android-Releases ändern kann.

Ftrace-Ereignisse aktivieren

Die ftrace-Sysfs-Knoten befinden sich in /sys/kernel/tracing und die Trace-Ereignisse sind in /sys/kernel/tracing/events in Kategorien unterteilt.

So aktivieren Sie Ereignisse pro Kategorie:

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

So aktivieren Sie Ereignisse für einzelne Ereignisse:

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

Wenn zusätzliche Ereignisse durch Schreiben in sysfs-Knoten aktiviert wurden, werden sie von atrace nicht zurückgesetzt. Ein gängiges Muster für die Inbetriebnahme von Qualcomm-Geräten besteht darin, Tracepoints für kgsl (GPU) und mdss (Displaypipeline) zu aktivieren und dann atrace oder systrace zu verwenden:

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

Sie können ftrace auch ohne atrace oder systrace verwenden. Das ist nützlich, wenn Sie nur Kernel-Traces benötigen (oder wenn Sie sich die Zeit genommen haben, die User-Mode-Tracing-Eigenschaft manuell zu schreiben). So führen Sie nur ftrace aus:

  1. Legen Sie die Puffergröße auf einen Wert fest, der für Ihren Trace groß genug ist:
    echo 96000 > /sys/kernel/tracing/buffer_size_kb
    
  2. Tracing aktivieren:
    echo 1 > /sys/kernel/tracing/tracing_on
    
  3. Führen Sie den Test aus und deaktivieren Sie dann das Tracing:
    echo 0 > /sys/kernel/tracing/tracing_on
    
  4. Dumpen Sie den Trace:
    cat /sys/kernel/tracing/trace > /data/local/tmp/trace_output
    

„trace_output“ gibt den Trace in Textform an. Wenn Sie es mit Catapult visualisieren möchten, laden Sie das Catapult-Repository von GitHub herunter und führen Sie trace2html aus:

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

Standardmäßig wird trace_file.html in dasselbe Verzeichnis geschrieben.

Ereignisse korrelieren

Es ist oft hilfreich, sich die Catapult-Visualisierung und das Ftrace-Protokoll gleichzeitig anzusehen. Einige Ftrace-Ereignisse (insbesondere anbieterspezifische) werden beispielsweise von Catapult nicht visualisiert. Die Zeitstempel von Catapult sind jedoch entweder relativ zum ersten Ereignis im Trace oder zu einem bestimmten Zeitstempel, der von atrace gedumpt wurde. Die Zeitstempel der Roh-ftrace basieren dagegen auf einer bestimmten absoluten Uhrenquelle im Linux-Kernel.

So finden Sie ein bestimmtes ftrace-Ereignis aus einem Catapult-Ereignis:

  1. Öffnen Sie das Roh-Ftrace-Protokoll. In den neuesten Versionen von systrace werden Traces standardmäßig komprimiert:
    • Wenn Sie den Systrace mit --no-compress erfasst haben, finden Sie diese Informationen in der HTML-Datei im Abschnitt, der mit BEGIN TRACE beginnt.
    • Falls nicht, führen Sie html2trace aus dem Catapult-Baum (tracing/bin/html2trace) aus, um den Trace zu dekomprimieren.
  2. Suchen Sie in der Catapult-Visualisierung nach dem relativen Zeitstempel.
  3. Suchen Sie am Anfang der Spur nach einer Zeile, die tracing_mark_sync enthält. Das sollte ungefähr so aussehen:
    <5134>-5134  (-----) [003] ...1    68.104349: tracing_mark_write: trace_event_clock_sync: parent_ts=68.104286
    

    Wenn diese Zeile nicht vorhanden ist (oder Sie ftrace ohne atrace verwendet haben), sind die Zeitangaben relativ zum ersten Ereignis im ftrace-Log.
    1. Addieren Sie den relativen Zeitstempel (in Millisekunden) zum Wert in parent_ts (in Sekunden).
    2. Suchen Sie nach dem neuen Zeitstempel.

Mit diesen Schritten sollten Sie sich am Ereignis befinden oder zumindest sehr nah dran sein.

Dynamische ftrace verwenden

Wenn Systrace und die Standard-Ftrace nicht ausreichen, gibt es noch eine letzte Möglichkeit: die dynamische Ftrace. Bei der dynamischen Ftrace wird der Kernelcode nach dem Start neu geschrieben. Daher ist sie aus Sicherheitsgründen in Produktionskerneln nicht verfügbar. Allerdings wurde die Ursache für jeden einzelnen schwierigen Leistungsfehler in den Jahren 2015 und 2016 letztendlich mithilfe der dynamischen ftrace ermittelt. Sie eignet sich besonders gut zum Debuggen von ununterbrechbaren Ruhezuständen, da Sie jedes Mal, wenn Sie die Funktion aufrufen, die den ununterbrechbaren Ruhezustand auslöst, einen Stack-Trace im Kernel erhalten. Sie können auch Abschnitte mit deaktivierten Unterbrechungen und Vorwegnahmen debuggen, was sehr hilfreich sein kann, um Probleme nachzuweisen.

Wenn Sie die dynamische ftrace aktivieren möchten, bearbeiten Sie die Defconfig Ihres Kernels:

  1. Entfernen Sie CONFIG_STRICT_MEMORY_RWX, falls vorhanden. Wenn Sie 3.18 oder höher und arm64 verwenden, ist sie nicht vorhanden.
  2. Fügen Sie Folgendes hinzu: CONFIG_DYNAMIC_FTRACE=y, CONFIG_FUNCTION_TRACER=y, CONFIG_IRQSOFF_TRACER=y, CONFIG_FUNCTION_PROFILER=y und CONFIG_PREEMPT_TRACER=y
  3. Erstellen Sie den neuen Kernel neu und starten Sie ihn.
  4. Führen Sie den folgenden Befehl aus, um nach verfügbaren Tracern zu suchen:
    cat /sys/kernel/tracing/available_tracers
    
  5. Prüfen Sie, ob der Befehl function, irqsoff, preemptoff und preemptirqsoff zurückgibt.
  6. Führen Sie den folgenden Befehl aus, um zu prüfen, ob die dynamische ftrace funktioniert:
    cat /sys/kernel/tracing/available_filter_functions | grep <a function you care about>
    

Nachdem Sie diese Schritte ausgeführt haben, sind die dynamische ftrace, der Funktionsprofiler, der irqsoff-Profiler und der preemptoff-Profiler verfügbar. Wir empfehlen dringend, die Ftrace-Dokumentation zu diesen Themen zu lesen, bevor Sie sie verwenden, da sie leistungsstark, aber komplex sind. „irqsoff“ und „preemptoff“ sind in erster Linie nützlich, um zu bestätigen, dass Treiber Unterbrechungen oder die Voraktivierung möglicherweise zu lange deaktiviert lassen.

Der Funktionsprofiler ist die beste Option für Leistungsprobleme und wird häufig verwendet, um herauszufinden, wo eine Funktion aufgerufen wird.


Wenn die Daten aus dem Funktionsprofiler nicht spezifisch genug sind, können Sie Ftrace-Tracepoints mit dem Funktionsprofiler kombinieren. Ftrace-Ereignisse können genau wie gewohnt aktiviert werden und werden in Ihren Trace eingefügt. Das ist sehr nützlich, wenn in einer bestimmten Funktion, die Sie debuggen möchten, gelegentlich eine lange, nicht unterbrechbare Ruhezeit auftritt: Legen Sie den Ftrace-Filter auf die gewünschte Funktion fest, aktivieren Sie Tracepoints und erstellen Sie eine Trace. Sie können den resultierenden Trace mit trace2html analysieren, das gewünschte Ereignis finden und dann Stack-Traces in der Nähe im Roh-Trace abrufen.

lockstat verwenden

Manchmal reicht ftrace nicht aus und Sie müssen wirklich die Fehlerbehebung für das Problem mit der Kernelsperre durchführen. Es gibt noch eine weitere Kerneloption, die Sie ausprobieren können: CONFIG_LOCK_STAT. Dies ist nur als letzter Ausweg zu empfehlen, da es extrem schwierig ist, die Funktion auf Android-Geräten zu aktivieren, da die Größe des Kernels dadurch über das hinausgeht, was die meisten Geräte verarbeiten können.

Lockstat verwendet jedoch die Infrastruktur zum Sperren von Debug-Objekten, die für viele andere Apps nützlich ist. Alle, die an der Geräteeinrichtung arbeiten, sollten eine Möglichkeit finden, diese Option auf jedem Gerät zum Laufen zu bringen. Denn es wird der Moment kommen, in dem Sie denken: „Wenn ich nur LOCK_STAT aktivieren könnte, könnte ich in fünf Minuten statt in fünf Tagen bestätigen oder widerlegen, ob das das Problem ist.“


Wenn Sie einen Kernel mit der Konfigurationsoption starten können, ähnelt die Sperren-Tracing-Funktion der ftrace:

  1. Tracing aktivieren:
    echo 1 > /proc/sys/kernel/lock_stat
    
  2. Führen Sie den Test aus.
  3. Tracing deaktivieren:
    echo 0 > /proc/sys/kernel/lock_stat
    
  4. Dumpen Sie den Trace:
    cat /proc/lock_stat > /data/local/tmp/lock_stat
    

Informationen zur Interpretation der Ausgabe finden Sie in der lockstat-Dokumentation unter <kernel>/Documentation/locking/lockstat.txt.

Anbieter-Tracepoints verwenden

Verwenden Sie zuerst Upstream-Tracepoints. Manchmal müssen Sie jedoch Anbieter-Tracepoints verwenden:

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

Tracepoints können über den HAL-Dienst erweitert werden, sodass Sie gerätespezifische Tracepoints/Kategorien hinzufügen können. Tracepoints sind in perfetto, atrace/systrace und die On-Device-System-Tracing-App eingebunden.

Die APIs zur Implementierung von Tracepoints/Kategorien sind:

  • listCategories() generiert (vec<TracingCategory> categories);
  • enableCategories(vec<string> categories) generiert (Status status);
  • disableAllCategories() generiert (Status status);
Weitere Informationen finden Sie in der HAL-Definition und der Standardimplementierung in AOSP: