Nel mio precedente articolo, ho spiegato come usare ftrace
per tracciare le funzioni del kernel. Usando ftrace
scrivere e leggere dai file può diventare noioso, quindi ho usato un wrapper attorno ad esso per eseguire comandi con opzioni per abilitare e disabilitare la traccia, impostare filtri, visualizzare l'output, cancellare l'output e altro.
Il comando trace-cmd è un'utilità che ti aiuta a fare proprio questo. In questo articolo, utilizzo trace-cmd
per eseguire le stesse attività che ho svolto nel mio ftrace
articolo. Dato che rimando spesso a quell'articolo, ti consiglio di leggerlo prima di leggere questo.
Installa trace-cmd
Eseguo i comandi in questo articolo come utente root.
Il ftrace
il meccanismo è integrato nel kernel e puoi verificare che sia abilitato con:
# mount | grep tracefs
none on /sys/kernel/tracing type tracefs (rw,relatime,seclabel)
Tuttavia, devi installare trace-cmd
utilità manualmente.
# dnf install trace-cmd -y
Elenca i traccianti disponibili
Quando si utilizza ftrace
, è necessario visualizzare il contenuto di un file per vedere quali traccianti sono disponibili. Ma con trace-cmd
, puoi ottenere queste informazioni con:
# trace-cmd list -t
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
Abilita la funzione di traccia
Nel mio precedente articolo, ho usato due traccianti e farò lo stesso qui. Abilita il tuo primo tracciante, function
, con:
$ trace-cmd start -p function
plugin 'function'
Visualizza l'output della traccia
Una volta abilitato il tracciante, puoi visualizzare l'output utilizzando il show
argomenti. Questo mostra solo le prime 20 righe per mantenere l'esempio breve (vedi il mio articolo precedente per una spiegazione dell'output):
# 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
Interrompi la traccia e cancella il buffer
La traccia continua a essere eseguita in background e puoi continuare a visualizzare l'output utilizzando show
.
Per interrompere la traccia, esegui trace-cmd
con la stop
argomento:
# trace-cmd stop
Per cancellare il buffer, eseguilo con clear
argomento:
# trace-cmd clear
Abilita il function_graph tracciante
Abilita il secondo tracciante, function_graph
, eseguendo:
# trace-cmd start -p function_graph
plugin 'function_graph'
Ancora una volta, visualizza l'output usando show
discussione. Come previsto, l'output è leggermente diverso dal primo output di traccia. Questa volta include una function calls
catena:
# 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() {
Usa la stop
e clear
comandi per interrompere la traccia e cancellare il buffer:
# trace-cmd stop
# trace-cmd clear
Modifica il tracciato per aumentare la profondità
Se vuoi vedere più profondità nelle chiamate di funzione, puoi modificare il tracciante:
# trace-cmd start -p function_graph --max-graph-depth 5
plugin 'function_graph'
Ora, quando confronti questo output con quello che hai visto prima, dovresti vedere più chiamate di funzione nidificate:
# 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() {
Impara le funzioni disponibili per tracciare
Se si desidera tracciare solo alcune funzioni e ignorare il resto, è necessario conoscere i nomi esatti delle funzioni. Puoi ottenerli con l'list
argomento seguito da -f
. Questo esempio cerca la funzione comune del kernel kmalloc
, che viene utilizzato per allocare memoria nel kernel:
# 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
[...]
Ecco il conteggio totale delle funzioni disponibili sul mio sistema di test:
# trace-cmd list -f | wc -l
63165
Traccia le funzioni relative ai moduli del kernel
Puoi anche tracciare le funzioni relative a uno specifico modulo del kernel. Immagina di voler tracciare kvm
funzioni relative ai moduli del kernel. Assicurati che il modulo sia caricato:
# lsmod | grep kvm_intel
kvm_intel 335872 0
kvm 987136 1 kvm_intel
Esegui trace-cmd
di nuovo con la list
argomento e dall'output, grep
per le righe che terminano con ]
. Questo filtrerà i moduli del kernel. Quindi grep
il modulo del kernel kvm_intel
e dovresti vedere tutte le funzioni relative a quel modulo del kernel:
# 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]
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
Traccia funzioni specifiche
Ora che sai come trovare le funzioni di interesse, metti questa conoscenza per lavorare con un esempio. Come nell'articolo precedente, prova a tracciare le funzioni relative al filesystem. Il filesystem che avevo sul mio sistema di test era ext4
.
Questa procedura è leggermente diversa; invece di start
, esegui il comando con il record
argomento seguito dal "pattern" delle funzioni che si desidera tracciare. Devi anche specificare il tracciante che desideri; in questo caso, è function_graph
. Il comando continua a registrare la traccia finché non la interrompi con Ctrl+C . Quindi, dopo alcuni secondi, premi Ctrl+C per interrompere la traccia:
# 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
[...]
Visualizza la traccia registrata
Per visualizzare la traccia che hai registrato in precedenza, esegui il comando con il report
discussione. Dall'output, è chiaro che il filtro ha funzionato e vedi solo la traccia della funzione relativa a ext4:
# 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();
Traccia un PID specifico
Supponiamo di voler tracciare le funzioni relative a uno specifico identificatore persistente (PID). Apri un altro terminale e annota il PID della shell in esecuzione:
# echo $$
10885
Esegui il record
comando di nuovo e passare il PID utilizzando il -P
opzione. Questa volta, lascia funzionare il terminale (cioè non premere Ctrl+C ancora):
# trace-cmd record -P 10885 -p function_graph
plugin 'function_graph'
Hit Ctrl^C to stop recording
Esegui alcune attività sulla shell
Torna all'altro terminale in cui avevi una shell in esecuzione con un PID specifico ed esegui qualsiasi comando, ad esempio ls
per elencare i file:
# ls
Temp-9b61f280-fdc1-4512-9211-5c60f764d702
tracker-extract-3-files.1000
v8-compile-cache-1000
[...]
Torna al terminale in cui hai abilitato il tracciamento e premi Ctrl+C per interrompere la traccia:
# 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
[...]
Nell'output della traccia, puoi vedere il PID e la shell Bash a sinistra e le chiamate di funzione ad esso correlate a destra. Questo può essere molto utile per restringere il tracciamento:
# 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() {
Fai una prova
Questi brevi esempi mostrano come utilizzare trace-cmd
invece del sottostante ftrace
meccanismo è sia facile da usare che ricco di funzionalità, tra cui molte che non ho trattato qui. Per saperne di più e migliorarlo, consulta la sua pagina man e prova i suoi altri utili comandi.