Advertisements

Kernel-Tracing mit trace-cmd

In meinem vorherigen Artikel habe ich die Verwendung erklärt ftrace Kernelfunktionen zu verfolgen. Verwenden von ftrace Durch das Schreiben und Lesen von Dateien kann es mühsam werden, daher habe ich einen Wrapper verwendet, um Befehle mit Optionen zum Aktivieren und Deaktivieren der Ablaufverfolgung, zum Festlegen von Filtern, zum Anzeigen der Ausgabe, zum Löschen der Ausgabe und mehr auszuführen.

Der Befehl trace-cmd ist ein Dienstprogramm, das Ihnen dabei hilft. In diesem Artikel verwende ich trace-cmd um die gleichen Aufgaben auszuführen, die ich in meinem ftrace Artikel. Da ich mich häufig auf diesen Artikel beziehe, empfehle ich Ihnen, ihn zu lesen, bevor Sie diesen lesen.

Trace-cmd installieren

Ich führe die Befehle in diesem Artikel als Root-Benutzer aus.

Die ftrace Mechanismus ist in den Kernel integriert, und Sie können überprüfen, ob er aktiviert ist mit:

# mount | grep tracefs
none on /sys/kernel/tracing type tracefs (rw,relatime,seclabel)

Sie müssen jedoch die trace-cmd Dienstprogramm manuell.

# dnf install trace-cmd -y

Verfügbare Tracer auflisten

Beim Benutzen ftrace, müssen Sie den Inhalt einer Datei anzeigen, um zu sehen, welche Tracer verfügbar sind. Aber mit trace-cmd, erhalten Sie diese Informationen mit:

# trace-cmd list -t
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

Aktivieren Sie den Funktions-Tracer

In meinem früheren Artikel habe ich zwei Tracer verwendet, und ich werde hier dasselbe tun. Aktivieren Sie Ihren ersten Tracer, function, mit:

$ trace-cmd start -p function
  plugin 'function'

Anzeigen der Trace-Ausgabe

Sobald der Tracer aktiviert ist, können Sie die Ausgabe anzeigen, indem Sie die show Argumente. Dies zeigt nur die ersten 20 Zeilen, um das Beispiel kurz zu halten (siehe meinen früheren Artikel für eine Erklärung der Ausgabe):

# trace-cmd show | head -20
## tracer: function
#
# entries-in-buffer/entries-written: 410142/3380032   #P:8
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
           gdbus-2606    [004] ..s. 10520.538759: __msecs_to_jiffies <-rebalance_domains
           gdbus-2606    [004] ..s. 10520.538760: load_balance <-rebalance_domains
           gdbus-2606    [004] ..s. 10520.538761: idle_cpu <-load_balance
           gdbus-2606    [004] ..s. 10520.538762: group_balance_cpu <-load_balance
           gdbus-2606    [004] ..s. 10520.538762: find_busiest_group <-load_balance
           gdbus-2606    [004] ..s. 10520.538763: update_group_capacity <-update_sd_lb_stats.constprop.0
           gdbus-2606    [004] ..s. 10520.538763: __msecs_to_jiffies <-update_group_capacity
           gdbus-2606    [004] ..s. 10520.538765: idle_cpu <-update_sd_lb_stats.constprop.0
           gdbus-2606    [004] ..s. 10520.538766: __msecs_to_jiffies <-rebalance_domains

Stoppen Sie die Verfolgung und löschen Sie den Puffer

Die Ablaufverfolgung läuft weiterhin im Hintergrund und Sie können die Ausgabe weiterhin mit anzeigen show.

Um die Verfolgung zu beenden, führen Sie trace-cmd mit dem stop Streit:

# trace-cmd stop

Um den Puffer zu löschen, führen Sie ihn mit dem clear Streit:

# trace-cmd clear

Aktivieren Sie den function_graph-Tracer

Aktivieren Sie den zweiten Tracer, function_graph, indem Sie Folgendes ausführen:

# trace-cmd start -p function_graph
  plugin 'function_graph'

Betrachten Sie die Ausgabe noch einmal mit dem show Streit. Wie erwartet unterscheidet sich die Ausgabe geringfügig von der ersten Ablaufverfolgungsausgabe. Diesmal beinhaltet es a function calls Kette:

# trace-cmd show | head -20
## tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 4)   0.079 us    |        } /* rcu_all_qs */
 4)   0.327 us    |      } /* __cond_resched */
 4)   0.081 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.078 us    |        rcu_all_qs();
 4)   0.243 us    |      }
 4)   0.080 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.078 us    |        rcu_all_qs();
 4)   0.241 us    |      }
 4)   0.080 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.079 us    |        rcu_all_qs();
 4)   0.235 us    |      }
 4)   0.095 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {

Verwenden Sie die stop und clear Befehle zum Stoppen der Ablaufverfolgung und Löschen des Puffers:

# trace-cmd stop
# trace-cmd clear

Optimieren Sie die Verfolgung, um die Tiefe zu erhöhen

Wenn Sie mehr Tiefe in den Funktionsaufrufen sehen möchten, können Sie den Tracer optimieren:

# trace-cmd start -p function_graph --max-graph-depth 5
  plugin 'function_graph'

Wenn Sie nun diese Ausgabe mit dem vergleichen, was Sie zuvor gesehen haben, sollten Sie mehr verschachtelte Funktionsaufrufe sehen:

# trace-cmd show | head -20
## tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 6)               |        __fget_light() {
 6)   0.804 us    |          __fget_files();
 6)   2.708 us    |        }
 6)   3.650 us    |      } /* __fdget */
 6)   0.547 us    |      eventfd_poll();
 6)   0.535 us    |      fput();
 6)               |      __fdget() {
 6)               |        __fget_light() {
 6)   0.946 us    |          __fget_files();
 6)   1.895 us    |        }
 6)   2.849 us    |      }
 6)               |      sock_poll() {
 6)   0.651 us    |        unix_poll();
 6)   1.905 us    |      }
 6)   0.475 us    |      fput();
 6)               |      __fdget() {

Informieren Sie sich über die verfügbaren Funktionen zum Nachverfolgen

Wenn Sie nur bestimmte Funktionen verfolgen und den Rest ignorieren möchten, müssen Sie die genauen Funktionsnamen kennen. Du bekommst sie mit dem list Argument gefolgt von -f. In diesem Beispiel wird nach der allgemeinen Kernelfunktion gesucht kmalloc, die verwendet wird, um Speicher im Kernel zuzuweisen:

# trace-cmd list -f | grep kmalloc
bpf_map_kmalloc_node
mempool_kmalloc
__traceiter_kmalloc
__traceiter_kmalloc_node
kmalloc_slab
kmalloc_order
kmalloc_order_trace
kmalloc_large_node
__kmalloc
__kmalloc_track_caller
__kmalloc_node
__kmalloc_node_track_caller
[...]

Hier ist die Gesamtzahl der auf meinem Testsystem verfügbaren Funktionen:

# trace-cmd list -f | wc -l
63165

Sie können auch Funktionen verfolgen, die sich auf ein bestimmtes Kernelmodul beziehen. Stellen Sie sich vor, Sie möchten nachverfolgen kvm Kernelmodulbezogene Funktionen. Stellen Sie sicher, dass das Modul geladen ist:

# lsmod  | grep kvm_intel
kvm_intel             335872  0
kvm                   987136  1 kvm_intel

Lauf trace-cmd wieder mit dem list Argument und aus der Ausgabe grep für Zeilen, die auf enden ]. Dadurch werden die Kernel-Module herausgefiltert. Dann grep das Kernel-Modul kvm_intel, und Sie sollten alle Funktionen sehen, die sich auf dieses Kernel-Modul beziehen:

# trace-cmd list -f | grep ]$  | grep kvm_intel
vmx_can_emulate_instruction [kvm_intel]
vmx_update_emulated_instruction [kvm_intel]
vmx_setup_uret_msr [kvm_intel]
vmx_set_identity_map_addr [kvm_intel]
handle_machine_check [kvm_intel]
handle_triple_fault [kvm_intel]
vmx_patch_hypercall [kvm_intel]

[...]

vmx_dump_dtsel [kvm_intel]
vmx_dump_sel [kvm_intel]

Weitere Linux-Ressourcen

Trace-spezifische Funktionen

Nachdem Sie nun wissen, wie Sie interessante Funktionen finden, können Sie dieses Wissen anhand eines Beispiels anwenden. Versuchen Sie wie im vorherigen Artikel, dateisystembezogene Funktionen zu verfolgen. Das Dateisystem, das ich auf meinem Testsystem hatte, war ext4.

Dieses Verfahren ist etwas anders; Anstatt von start, führen Sie den Befehl mit dem aus record Argument gefolgt vom “Muster” der Funktionen, die Sie verfolgen möchten. Sie müssen auch den gewünschten Tracer angeben; in diesem Fall ist das function_graph. Der Befehl zeichnet den Trace so lange auf, bis Sie ihn mit stoppen Strg+C. Drücken Sie also nach ein paar Sekunden Strg+C um die Verfolgung zu beenden:

# trace-cmd list -f | grep ^ext4_

# trace-cmd record -l ext4_* -p function_graph
  plugin 'function_graph'
Hit Ctrl^C to stop recording
^C
CPU0 data recorded at offset=0x856000
    8192 bytes in size
[...]

Sehen Sie sich die aufgezeichnete Spur an

Um den zuvor aufgezeichneten Trace anzuzeigen, führen Sie den Befehl mit der report Streit. Aus der Ausgabe geht hervor, dass der Filter funktioniert hat, und Sie sehen nur den ext4-bezogenen Funktionstrace:

# trace-cmd report | head -20
[...]
cpus=8
       trace-cmd-12697 [000] 11303.928103: funcgraph_entry:                   |  ext4_show_options() {
       trace-cmd-12697 [000] 11303.928104: funcgraph_entry:        0.187 us   |    ext4_get_dummy_policy();
       trace-cmd-12697 [000] 11303.928105: funcgraph_exit:         1.583 us   |  }
       trace-cmd-12697 [000] 11303.928122: funcgraph_entry:                   |  ext4_create() {
       trace-cmd-12697 [000] 11303.928122: funcgraph_entry:                   |    ext4_alloc_inode() {
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.101 us   |      ext4_es_init_tree();
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.083 us   |      ext4_init_pending_tree();
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.141 us   |      ext4_fc_init_inode();
       trace-cmd-12697 [000] 11303.928123: funcgraph_exit:         0.931 us   |    }
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.081 us   |    ext4_get_dummy_policy();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.133 us   |    ext4_get_group_desc();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.115 us   |    ext4_free_inodes_count();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.114 us   |    ext4_get_group_desc();

Verfolgen Sie eine bestimmte PID

Angenommen, Sie möchten Funktionen verfolgen, die sich auf einen bestimmten persistenten Identifikator (PID) beziehen. Öffnen Sie ein anderes Terminal und notieren Sie sich die PID der laufenden Shell:

# echo $$
10885

Führen Sie die record Befehl erneut und übergeben Sie die PID mit dem -P Möglichkeit. Lassen Sie dieses Mal das Terminal laufen (dh drücken Sie nicht Strg+C noch):

# trace-cmd record -P 10885 -p function_graph
  plugin 'function_graph'
Hit Ctrl^C to stop recording

Führen Sie einige Aktivitäten auf der Shell aus

Gehen Sie zurück zu dem anderen Terminal, auf dem eine Shell mit einer bestimmten PID ausgeführt wurde, und führen Sie einen beliebigen Befehl aus, z. ls Dateien auflisten:

# ls
Temp-9b61f280-fdc1-4512-9211-5c60f764d702
tracker-extract-3-files.1000
v8-compile-cache-1000
[...]

Gehen Sie zurück zu dem Terminal, in dem Sie die Verfolgung aktiviert haben, und drücken Sie Strg+C um die Verfolgung zu beenden:

# trace-cmd record -P 10885 -p function_graph
  plugin 'function_graph'
Hit Ctrl^C to stop recording
^C
CPU1 data recorded at offset=0x856000
    618496 bytes in size
[...]

In der Ausgabe des Trace sehen Sie links die PID und die Bash-Shell und rechts die damit verbundenen Funktionsaufrufe. Dies kann sehr praktisch sein, um Ihre Ablaufverfolgung einzugrenzen:

# trace-cmd report  | head -20

cpus=8
          <idle>-0     [001] 11555.380581: funcgraph_entry:                   |  switch_mm_irqs_off() {
          <idle>-0     [001] 11555.380583: funcgraph_entry:        1.703 us   |    load_new_mm_cr3();
          <idle>-0     [001] 11555.380586: funcgraph_entry:        0.493 us   |    switch_ldt();
          <idle>-0     [001] 11555.380587: funcgraph_exit:         7.235 us   |  }
            bash-10885 [001] 11555.380589: funcgraph_entry:        1.046 us   |  finish_task_switch.isra.0();
            bash-10885 [001] 11555.380591: funcgraph_entry:                   |  __fdget() {
            bash-10885 [001] 11555.380592: funcgraph_entry:        2.036 us   |    __fget_light();
            bash-10885 [001] 11555.380594: funcgraph_exit:         3.256 us   |  }
            bash-10885 [001] 11555.380595: funcgraph_entry:                   |  tty_poll() {
            bash-10885 [001] 11555.380597: funcgraph_entry:                   |    tty_ldisc_ref_wait() {
            bash-10885 [001] 11555.380598: funcgraph_entry:                   |      ldsem_down_read() {
            bash-10885 [001] 11555.380598: funcgraph_entry:                   |        __cond_resched() {

Versuche es

Diese kurzen Beispiele zeigen, wie die Verwendung von trace-cmd statt des Basiswertes ftrace Mechanismus ist sowohl einfach zu bedienen als auch reich an Funktionen, darunter viele, die ich hier nicht behandelt habe. Um mehr zu erfahren und besser darin zu werden, konsultieren Sie die Manpage und probieren Sie die anderen nützlichen Befehle aus.

Kernel-Tracing mit trace-cmd

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top