La experiencia de usar Tracepoint del kernel de Linux por primera vez

No me avergüenzo, en absoluto.

Quiero decir que he trabajado durante tantos años para hacer cosas relacionadas con el kernel de Linux, y solo usé tracepoint por primera vez la semana pasada.

Esto no es sorprendente, hay muchas cosas que no sé, por ejemplo, siempre he enfatizado que no puedo programar y que no puedo usar git.

Más cerca de casa, si no he usado tracepoint durante tantos años, ¿qué debo usar en su lugar?

Si depuro el kernel o depuro el módulo del kernel, el método más común es agregar un printk al código (si es un programa en modo de usuario, agrego un printf.), Déjame decirte el motivo:

  • printk / printf no necesita instalar ningún otro paquete de dependencia.
  • Printk / printf está disponible y no hay necesidad de escribir demasiado código que está sobre-diseñado para su reutilización.
  • printk / printf es muy amigable para las personas que no pueden programar.
  • ...

Por lo tanto, me encanta printk. No me gusta gdp / crash tool, kprobe / systemtap y bpftrace. Por supuesto, conozco estas cosas muy bien, pero creo que son demasiado complicadas y necesitan recordar demasiadas gramáticas, como una persona sin hogar. A las personas sin hogar no les gusta llevar ninguna carga.

Después de tanta charla, tiene algo que ver con tracepoint.

Está relacionado, porque descubrí que agregar un punto de seguimiento al lugar donde se debe agregar un printk es aproximadamente la misma cantidad de trabajo, y el efecto es mejor. Tracepoint es más como agregar un GANCHO, puede personalizar lo que quiere hacer afuera, no solo quedarse con printk para imprimir un mensaje.

Usando tracepoint, si solo desea lograr el efecto de printk, puede ver la información que desea imprimir en / sys / kernel / debug / tracing / trace. Si desea hacer algo más, también puede usar eBPF.

La parte de la metafísica de Taishigong similar a "Registros históricos" se encuentra al final de este artículo. Ahora vamos a dar algunas cosas prácticas.

Agregar un tracepoint al código del kernel es tan simple como no hace falta decirlo, solo necesita volver a compilar el kernel. Permítanme dar un Howto para agregar tracepoint al módulo.

Primero dame el código del módulo del kernel que quiero agregar tracepoit:

#include <net/protocol.h>
#include <linux/ip.h>
#include <linux/udp.h>

#define IPPROTO_MYPROTO  123
int myproto_rcv(struct sk_buff *skb)
{
    
    
    struct udphdr *uh;
    struct iphdr *iph;

    iph = ip_hdr(skb);
    uh = udp_hdr(skb);

    printk("proto 123\n");

    kfree_skb(skb);
    return 0;
}

static const struct net_protocol myproto_protocol = {
    
    
    .handler = myproto_rcv,
    .no_policy = 1,
    .netns_ok = 1,
};

int init_module(void)
{
    
    
    int ret = 0;
    ret = inet_add_protocol(&myproto_protocol, IPPROTO_MYPROTO);
    if (ret) {
    
    
        printk("failed\n");
        return ret;
    }
    return 0;
}
void cleanup_module(void)
{
    
    
    inet_del_protocol(&myproto_protocol, IPPROTO_MYPROTO);
}

int init_module(void);
void cleanup_module(void);
MODULE_LICENSE("GPL");

Un ejemplo muy realista, este código registra un protocolo de cuatro capas que es igual a TCP y UDP, pero en realidad es UDP, pero el número de protocolo se cambia a 123. Nuestra expectativa es que cuando se reciba tal paquete de protocolo, printk lo imprima. Puerto.

Quiero usar tracepoint en lugar de printk, ¿qué debo hacer? Este es el método. Aquí está el nuevo código:

/*
 * 注意:
 * 1. CREATE_TRACE_POINTS 必须define
 * 2. CREATE_TRACE_POINTS 必须在头文件之前define
 */

#define CREATE_TRACE_POINTS
#include "test_tp.h"

#include <net/protocol.h>
#include <linux/ip.h>
#include <linux/udp.h>

#define IPPROTO_MYPROTO  123
int myproto_rcv(struct sk_buff *skb)
{
    
    
    struct udphdr *uh;
    struct iphdr *iph;

    iph = ip_hdr(skb);
    uh = udp_hdr(skb);

    // 这里增加一个tracepoint点
    trace_myprot_port(uh->dest, uh->source);

    kfree_skb(skb);
    return 0;
}

static const struct net_protocol myproto_protocol = {
    
    
    .handler = myproto_rcv,
    .no_policy = 1,
    .netns_ok = 1,
};

int init_module(void)
{
    
    
    int ret = 0;
    ret = inet_add_protocol(&myproto_protocol, IPPROTO_MYPROTO);
    if (ret) {
    
    
        printk("failed\n");
        return ret;
    }
    return 0;
}
void cleanup_module(void)
{
    
    
    inet_del_protocol(&myproto_protocol, IPPROTO_MYPROTO);
}

int init_module(void);
void cleanup_module(void);
MODULE_LICENSE("GPL");

Se agregó una referencia al archivo de encabezado y se agregó una definición de macro. Nada más ha cambiado. ¿No es simple?

Todas las definiciones de metadatos relacionadas con tracepoint están en el archivo de encabezado:

// test_tp.h
#undef TRACE_SYSTEM
#define TRACE_SYSTEM myprot

#if !defined(_TEST_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TEST_TRACE_H
#include <linux/tracepoint.h>

// tracepoint的定义就在这里了。可用的参数都在这里,日后debugfs输出的就是按照下面这个TP_printk的格式来的。
// 如果eBPF来输出,依然要遵循这个格式取参数,下面有例子。
TRACE_EVENT(myprot_port,
    TP_PROTO(unsigned short dest, unsigned short source),
    TP_ARGS(dest, source),
    TP_STRUCT__entry(
        __field(unsigned short, dest)
        __field(unsigned short, source)
    ),
TP_fast_assign(
    __entry->dest = dest;
    __entry->source = source;
),

TP_printk("dest:%d, source:%d", __entry->dest, __entry->source)
);

#endif
/*
 * 需要添加以下这一坨
 * 由于模块不能修改内核头文件,因此我们需要在自己的目录放头文件,
 * TRACE_INCLUDE_PATH 必须重新定义,而不是仅仅在内核头文件目录寻找定义。
 *
 * 为了支持这个重定义,Makefile中必须包含:
 * CFLAGS_myprot.o = -I$(src)
 *
 */

#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE test_tp // 这就是该头文件的名字
#include <trace/define_trace.h>

El siguiente es el Makefile, solo debemos prestar atención a la segunda línea:

obj-m += test.o
# CFLAGS_xx.o 其中 xx 为内核模块的名字
CFLAGS_test.o = -DDEBUG -I$(src)

Bien, compila el módulo y cárgalo.

Primero abrimos el tracepoint:

echo 1 >/sys/kernel/debug/tracing/events/myprot/enable

En este momento, envíe un mensaje con el protocolo 123 a través del socket sin formato, veremos la salida en debugfs:

cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1   #P:4
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
     ksoftirqd/1-16      [001] ..s. 174074.362291: myprot_port: dest:36895, source:53764

Por supuesto, esto es solo una simulación de printk, esto es solo para unificar printk en un solo lugar, no es gran cosa. Lo que hace que Tracepoint sea más significativo es que puede montar programas eBPF. Ahora podemos intentar:

bpftrace -l|grep tracepoint.*myprot
tracepoint:myprot:myprot_port

Simplemente podemos depurar con bpftrace:

#!/usr/bin/bpftrace

tracepoint:myprot:myprot_port
{
    
    
	$dest = args->dest;
	$source = args->source;
	printf("bpftrace:dest:%d  source:%d\n",
		($source & 0xff) << 8 | ($source & 0xff00) >> 8,
		($dest & 0xff) << 8 | ($dest & 0xff00) >> 8);
}

Ejecútelo, envíe el paquete:

./test_bpf_tp.tp
Attaching 1 probe...
bpftrace:dest:1234  source:8080

¡Lo único de lo que quejarse es por qué no existe una función incorporada para las interfaces de uso común como ntohs!

En comparación con kprobe / kretprobe, ¿cuáles son las ventajas y desventajas de tracepoint? ¿Por qué no me gusta kprobe? Ni siquiera me gusta el parche en vivo, pero por eso.

La única desventaja de tracepoint es que es estático. Debe insertarse estáticamente en una ubicación específica del código fuente escribiendo código y luego ejecutarse después de la recompilación para que surta efecto. Por el contrario, kprobe es dinámico y puede GANCHAR en tiempo de ejecución. Adjuntar dinámicamente a una ubicación específica, pero desafortunadamente, esta es la única ventaja de kprobe.

Delante de todos, he revelado mi insatisfacción con kprobe. La razón es la misma que mi insatisfacción con el parche en vivo. Es decir, para insertar dinámicamente incluso un solo printk, el costo de ejecución del código para mantener el marco de trabajo de kprobe / live patch es demasiado alto.

Puede rastrear la longitud de la ruta del código que llama a la nueva función del parche en vivo bajo el marco ftrace. Puede rastrear cuántas cosas adicionales hace krpobe / kretprobe para ejecutar el controlador. Todo lo que necesito es el controlador, y el controlador son solo variables printk El valor de es solo, no quiero luchar por algo tan pequeño. Si se encuentra otro spinlock en el camino de llamar al controlador, será aún más vergonzoso. El nuevo cuello de botella introducido para depurar el cuello de botella de rendimiento excede el cuello de botella de rendimiento original en sí. . En la optimización del rendimiento, movimientos tan finos, ¡tal efecto de incertidumbre es muy limitante!

Soy un artesano y me cuesta mucho confiar en kprobe y live patch, que en mi opinión son completamente incontrolables. Por supuesto que no me importa el ridículo de aquellos que pueden operar estas herramientas con destreza, yo tengo mi propio camino.

Si quiero parchear una función, o si quiero insertar un printk en algún lugar, lo haré manualmente. Es muy simple. Simplemente cambie el código para llamar a myhandler en una ubicación específica. Cuando myhandler regrese, vuelva a ejecutar la instrucción reemplazada. Este método es muy simple y directo. Confío en este método. Cuando encuentro este método y puedo usarlo de manera competente, en mi opinión, no hay una diferencia esencial entre tracepoint y kprobe.

Olvídese de kprobe, live patch y olvídese de tracepoint puede ser mejor que recordarlos.

Hacer señas para detener un taxi puede ser más liviano que Didi en su teléfono móvil. Del mismo modo, no necesita preocuparse por la duración de la batería y los problemas de privacidad cuando paga con papel moneda ...


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.

Supongo que te gusta

Origin blog.csdn.net/dog250/article/details/112058189
Recomendado
Clasificación