Skip to main content

Cyber AI România

Vineri, 6 noiembrie 2026

Dynamic Debug și kernel tracepoints: logging izolat pentru un modul Linux

Un modul intern începe să răspundă imprevizibil, dar activarea globală a mesajelor kernel ar umple jurnalul cu date fără legătură. Laboratorul de mai jos rezolvă exact această problemă: construiește modulul inofensiv ddlab, activează temporar numai callsite-urile sale pr_debug(), înregistrează numai tracepoint-ul său și dezactivează totul chiar dacă sesiunea e întreruptă.

Rezultatul este verificabil în trei fișiere protejate din /var/log/ddlab/: evenimentele din ring buffer-ul tracefs, mesajele Dynamic Debug preluate din kernel journal și un manifest cu versiunea kernelului, numărul de callsite-uri și numărul de evenimente. Serviciul nu rămâne pornit, iar un timer systemd verifică periodic dacă debug-ul a fost lăsat activ accidental.

Proiectul este destinat unei mașini de laborator sau unui server de test pe care îl administrezi. Nu încărca un modul experimental pe un sistem de producție înainte de revizuirea codului, testarea pe aceeași versiune de kernel și respectarea politicii Secure Boot.

Separăm mesajele explicative de evenimentele structurate

Dynamic Debug și tracepoint-urile rezolvă două probleme diferite.

Dynamic Debug controlează callsite-uri construite cu pr_debug(), dev_dbg() și mecanisme compatibile. Catalogul lor se găsește în /proc/dynamic_debug/control atunci când kernelul are suportul necesar. O interogare precum module ddlab +pmlft selectează numai modulul ddlab: p activează mesajul, iar m, l, f și t adaugă modulul, linia, funcția și identificatorul threadului.

Tracepoint-ul ddlab:ddlab_request produce în schimb un eveniment cu două câmpuri stabile, bytes și status. Evenimentul intră în ring buffer-ul tracefs și poate fi filtrat sau analizat fără a interpreta text liber.

Nu activăm +p fără selector și nu pornim toate evenimentele din events/enable. Aceste comenzi ar mări zgomotul și costul de observabilitate la nivelul întregului kernel.

Dacă problema urmărită este latența dintr-un traseu de apeluri, proiectul poate fi continuat cu function_graph tracer pentru măsurarea funcțiilor kernelului. Pentru corelarea schedulerului, IRQ-urilor și context switch-urilor pe o cronologie vizuală, este mai potrivit fluxul cu Trace-cmd și KernelShark. Aici păstrăm intenționat o singură țintă: modulul ddlab.

Fixăm laboratorul la Ubuntu Server 24.04 LTS și la header-ele kernelului activ

Laboratorul folosește Ubuntu Server 24.04 LTS, systemd 255, GCC 13 și pachetul e2fsprogs 1.47.0 din seria Noble. Kernelul aparține familiei GA 6.8, dar numărul ABI de forma 6.8.0-xx-generic trebuie citit pe mașina locală, nu copiat din articol: actualizările de securitate schimbă acel sufix, iar modulul trebuie compilat împotriva header-elor pentru rezultatul exact al comenzii uname -r.

Sunt suficiente două nuclee CPU, 2 GB RAM, aproximativ 1 GB liber și 30–45 de minute. Nu este necesar GPU.

Verifică mediul înainte de a crea fișiere:

. /etc/os-release
printf 'Sistem: %s %sn' "$NAME" "$VERSION_ID"
printf 'Kernel: '; uname -r
printf 'Arhitectură: '; uname -m
printf 'systemd: '; systemd --version | head -n 1
printf 'GCC: '; gcc --version | head -n 1
printf 'Spațiu liber în /opt: '; df -h /opt | tail -n 1
printf 'RAM: '; free -h | awk '/^Mem:/ {print $2}'

Un exemplu de rezultat este:

Sistem: Ubuntu 24.04
Kernel: 6.8.0-xx-generic
Arhitectură: x86_64
systemd: systemd 255 (255.4-1ubuntu8...)
GCC: gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Spațiu liber în /opt: /dev/...  ...  ...  ...  ...  /opt
RAM: 2.0Gi

Valorile marcate prin xx și câmpurile raportate de df diferă între sisteme. Oprește-te dacă VERSION_ID nu este 24.04, dacă uname -m nu corespunde mediului tău de test sau dacă sistemul nu are header-e disponibile pentru kernelul activ.

Instalarea necesară este scurtă și folosește numai depozitele Ubuntu configurate local:

sudo apt update
sudo apt install --yes build-essential "linux-headers-$(uname -r)"
sudo install -d -m 0750 -o root -g root /opt/ddlab
sudo install -d -m 0700 -o root -g root /var/log/ddlab

build-essential aduce compilatorul și make, iar linux-headers-$(uname -r) păstrează corespondența dintre modul și kernelul care îl va încărca.

Punct de control: următoarea comandă trebuie să afișeze un director existent, nu o cale lipsă.

test -d "/lib/modules/$(uname -r)/build" && printf 'Header-ele corespund kernelului activ.n'

Șapte fișiere delimitează modulul, captura și supravegherea

La final, proiectul și unitățile instalate au această structură:

/opt/ddlab/
├── Makefile                    — regulile Kbuild pentru modulul extern
├── ddlab.c                     — interfața debugfs și mesajele pr_debug()
├── ddlab_trace.h               — definiția tracepoint-ului structurat
├── ddlab-capture               — sesiunea limitată de captură
└── ddlab-guard                 — detectează debug sau tracing rămase active
/etc/ddlab.conf                 — calea logurilor și perioada de retenție
/etc/systemd/system/
├── ddlab-capture.service       — execută manual o singură captură
├── ddlab-guard.service         — rulează verificarea fără modificări
└── ddlab-guard.timer           — pornește verificarea periodic

Fișierele sursă rămân în /opt/ddlab, iar modulul compilat este instalat sub /lib/modules/$(uname -r)/extra/. Directorul de loguri este accesibil numai utilizatorului root.

Pasul 1 — Expunem o cerere controlată prin debugfs

De ce: avem nevoie de un callsite Dynamic Debug și de un eveniment structurat care pot fi declanșate determinist. Modulul acceptă doar un număr între 1 și 4096; nu citește memorie arbitrară, nu accesează rețeaua și nu modifică dispozitive.

Fișierul /opt/ddlab/ddlab_trace.h trebuie să conțină integral:

#undef TRACE_SYSTEM
#define TRACE_SYSTEM ddlab

#if !defined(_DDLAB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _DDLAB_TRACE_H

#include <linux/tracepoint.h>

TRACE_EVENT(ddlab_request,
	TP_PROTO(unsigned int bytes, int status),
	TP_ARGS(bytes, status),
	TP_STRUCT__entry(
		__field(unsigned int, bytes)
		__field(int, status)
	),
	TP_fast_assign(
		__entry->bytes = bytes;
		__entry->status = status;
	),
	TP_printk("bytes=%u status=%d",
		  __entry->bytes, __entry->status)
);

#endif

#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE ddlab_trace

#include <trace/define_trace.h>

Fișierul /opt/ddlab/ddlab.c trebuie să conțină integral:

#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/uaccess.h>

#define CREATE_TRACE_POINTS
#include "ddlab_trace.h"

#define DDLAB_MAX_REQUEST 4096U

static struct dentry *ddlab_dir;

static ssize_t ddlab_write(struct file *file, const char __user *user_buffer,
			   size_t count, loff_t *position)
{
	char buffer[32];
	size_t copied;
	unsigned int bytes;
	int result;

	if (count == 0)
		return -EINVAL;

	copied = min(count, sizeof(buffer) - 1);
	if (copy_from_user(buffer, user_buffer, copied))
		return -EFAULT;

	buffer[copied] = '';
	result = kstrtouint(strim(buffer), 10, &bytes);
	if (result || bytes == 0 || bytes > DDLAB_MAX_REQUEST) {
		trace_ddlab_request(0, -EINVAL);
		pr_debug("rejected request count=%zun", count);
		return -EINVAL;
	}

	trace_ddlab_request(bytes, 0);
	pr_debug("accepted request bytes=%un", bytes);
	return count;
}

static const struct file_operations ddlab_fops = {
	.owner = THIS_MODULE,
	.write = ddlab_write,
	.llseek = no_llseek,
};

static int __init ddlab_init(void)
{
	struct dentry *request_file;

	ddlab_dir = debugfs_create_dir("ddlab", NULL);
	if (IS_ERR_OR_NULL(ddlab_dir))
		return ddlab_dir ? PTR_ERR(ddlab_dir) : -ENOMEM;

	request_file = debugfs_create_file("request", 0200, ddlab_dir, NULL,
					   &ddlab_fops);
	if (IS_ERR_OR_NULL(request_file)) {
		debugfs_remove_recursive(ddlab_dir);
		return request_file ? PTR_ERR(request_file) : -ENOMEM;
	}

	pr_info("ddlab: laboratory module loadedn");
	return 0;
}

static void __exit ddlab_exit(void)
{
	debugfs_remove_recursive(ddlab_dir);
	pr_info("ddlab: laboratory module unloadedn");
}

module_init(ddlab_init);
module_exit(ddlab_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cyber AI Romania");
MODULE_DESCRIPTION("Dynamic Debug and tracepoint laboratory module");

TRACE_EVENT() declară câmpurile care apar în tracefs. pr_debug() creează callsite-urile controlabile prin Dynamic Debug. Permisiunea 0200 permite numai scrierea de către root; modulul nu oferă citire.

Ce trebuie să vezi după salvare:

/opt/ddlab/ddlab.c
/opt/ddlab/ddlab_trace.h

Punct de control: grep -n 'pr_debug|TRACE_EVENT' /opt/ddlab/ddlab.c /opt/ddlab/ddlab_trace.h trebuie să găsească două apeluri pr_debug() și definiția TRACE_EVENT().

Pasul 2 — Compilăm modulul exact pentru kernelul care rulează

De ce: modulele externe trebuie construite prin Kbuild și pentru versiunea kernelului care le va încărca. Un fișier .ko compilat pentru alt ABI poate fi refuzat.

Fișierul /opt/ddlab/Makefile trebuie să fie:

obj-m += ddlab.o
ccflags-y += -I$(src)

Compilează și inspectează metadatele fără a încărca încă modulul:

cd /opt/ddlab
make -C "/lib/modules/$(uname -r)/build" M="$PWD" modules
modinfo ./ddlab.ko

-C mută execuția în arborele Kbuild al kernelului, iar M="$PWD" indică sursele modulului extern. ccflags-y permite mecanismului define_trace.h să regăsească header-ul local.

Un fragment de output așteptat, dat ca exemplu, este:

filename:       /opt/ddlab/./ddlab.ko
description:    Dynamic Debug and tracepoint laboratory module
author:         Cyber AI Romania
license:        GPL
vermagic:       6.8.0-xx-generic SMP preempt mod_unload modversions

Sufixul vermagic trebuie să corespundă rezultatului uname -r.

Punct de control: rulează modinfo -F vermagic /opt/ddlab/ddlab.ko și compară primul câmp cu uname -r. Dacă diferă, nu continua cu încărcarea.

Pasul 3 — Încărcăm modulul și confirmăm ambele suprafețe de observabilitate

De ce: înainte de automatizare verificăm separat că debugfs, Dynamic Debug și tracefs sunt disponibile.

Instalează modulul și montează pseudo-filesystem-urile numai dacă nu sunt deja montate:

sudo install -D -m 0644 /opt/ddlab/ddlab.ko 
  "/lib/modules/$(uname -r)/extra/ddlab.ko"
sudo depmod -a
mountpoint -q /sys/kernel/debug ||
  sudo mount -t debugfs nodev /sys/kernel/debug
mountpoint -q /sys/kernel/tracing ||
  sudo mount -t tracefs nodev /sys/kernel/tracing
sudo modprobe ddlab

sudo test -w /sys/kernel/debug/ddlab/request
sudo test -r /proc/dynamic_debug/control
sudo test -d /sys/kernel/tracing/events/ddlab/ddlab_request

depmod -a actualizează dependențele modulelor. modprobe ddlab încarcă fișierul instalat, iar înregistrarea tracepoint-ului creează automat directorul evenimentului în tracefs.

Ce trebuie să vezi:

grep '[ddlab]' /proc/dynamic_debug/control
cat /sys/kernel/tracing/events/ddlab/ddlab_request/format

Exemplul de output conține callsite-urile inactive și câmpurile evenimentului:

/opt/ddlab/ddlab.c:... [ddlab]ddlab_write =_ "accepted request bytes=%un"
name: ddlab_request
field:unsigned int bytes;
field:int status;

Numerele de linie și câmpurile comune tracepoint-urilor pot diferi. Caracterul _ indică faptul că mesajul Dynamic Debug nu are activat flagul p.

Punct de control: dacă grep nu găsește modulul, verifică dmesg și modinfo ddlab înainte de a scrie în fișierele de control.

Pasul 4 — Limităm sesiunea la modulul și PID-ul capturii

De ce: scriptul creează o instanță tracefs separată, refuză să suprascrie o sesiune Dynamic Debug deja activă și folosește trap pentru dezactivare la ieșire normală, eroare sau semnal.

Fișierul /etc/ddlab.conf trebuie să fie:

OUTPUT_DIR=/var/log/ddlab
RETENTION_DAYS=14

Aplică permisiuni restrictive:

sudo chown root:root /etc/ddlab.conf
sudo chmod 0600 /etc/ddlab.conf

Fișierul /opt/ddlab/ddlab-capture trebuie să conțină integral:

#!/usr/bin/env bash
set -euo pipefail

readonly CONFIG=/etc/ddlab.conf
readonly TRACE_ROOT=/sys/kernel/tracing
readonly INSTANCE_NAME=ddlab-focus
readonly INSTANCE="${TRACE_ROOT}/instances/${INSTANCE_NAME}"
readonly EVENT="${INSTANCE}/events/ddlab/ddlab_request"
readonly DYNAMIC_CONTROL=/proc/dynamic_debug/control

[[ $EUID -eq 0 ]] || {
	printf 'ddlab-capture: trebuie rulat ca rootn' >&2
	exit 1
}

[[ -r "$CONFIG" ]] || {
	printf 'ddlab-capture: lipsește %sn' "$CONFIG" >&2
	exit 1
}

source "$CONFIG"

[[ "$OUTPUT_DIR" == /var/log/ddlab ]] || {
	printf 'ddlab-capture: OUTPUT_DIR neașteptatn' >&2
	exit 1
}

[[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || {
	printf 'ddlab-capture: RETENTION_DAYS nu este număr întregn' >&2
	exit 1
}

[[ -r "$DYNAMIC_CONTROL" && -w "$DYNAMIC_CONTROL" ]] || {
	printf 'ddlab-capture: Dynamic Debug indisponibiln' >&2
	exit 1
}

[[ -w /sys/kernel/debug/ddlab/request ]] || {
	printf 'ddlab-capture: modulul ddlab nu este încărcatn' >&2
	exit 1
}

if grep -E '[ddlab].*=.*p' "$DYNAMIC_CONTROL" >/dev/null; then
	printf 'ddlab-capture: există deja callsite-uri ddlab activen' >&2
	exit 1
fi

install -d -m 0700 -o root -g root "$OUTPUT_DIR"
mkdir -p "$INSTANCE"

[[ -d "$EVENT" ]] || {
	printf 'ddlab-capture: lipsește tracepoint-ul ddlab_requestn' >&2
	exit 1
}

cleanup()
{
	printf '0n' > "${INSTANCE}/tracing_on" 2>/dev/null || true
	printf '0n' > "${EVENT}/enable" 2>/dev/null || true
	printf 'n' > "${INSTANCE}/set_event_pid" 2>/dev/null || true
	printf '0n' > "${INSTANCE}/options/event-fork" 2>/dev/null || true
	printf 'module ddlab -pmlftn' > "$DYNAMIC_CONTROL" 2>/dev/null || true
}

trap cleanup EXIT INT TERM HUP
cleanup

readonly SESSION="$(date -u +%Y%m%dT%H%M%SZ)"
readonly TRACE_LOG="${OUTPUT_DIR}/${SESSION}-trace.txt"
readonly DEBUG_LOG="${OUTPUT_DIR}/${SESSION}-dynamic-debug.txt"
readonly MANIFEST="${OUTPUT_DIR}/${SESSION}-manifest.txt"

readonly CURSOR="$(
	journalctl -k -n 0 --show-cursor |
	sed -n 's/^-- cursor: //p'
)"

: > "${INSTANCE}/trace"
printf '%sn' "$$" > "${INSTANCE}/set_event_pid"
printf '1n' > "${INSTANCE}/options/event-fork"
printf '1n' > "${EVENT}/enable"
printf 'module ddlab +pmlftn' > "$DYNAMIC_CONTROL"
printf '1n' > "${INSTANCE}/tracing_on"

for request in 512 1024 2048; do
	printf '%sn' "$request" > /sys/kernel/debug/ddlab/request
done

printf '0n' > "${INSTANCE}/tracing_on"
cat "${INSTANCE}/trace" > "$TRACE_LOG"
journalctl --sync

if [[ -n "$CURSOR" ]]; then
	journalctl -k --after-cursor="$CURSOR" --no-pager |
		grep -E 'accepted request|rejected request' > "$DEBUG_LOG" || true
else
	printf 'Nu a fost disponibil un cursor pentru kernel journal.n' 
		> "$DEBUG_LOG"
fi

readonly CALLS="$(
	grep -c '[ddlab]' "$DYNAMIC_CONTROL" || true
)"
readonly TRACE_LINES="$(
	grep -c 'ddlab_request:' "$TRACE_LOG" || true
)"
readonly DEBUG_LINES="$(
	grep -c 'accepted request' "$DEBUG_LOG" || true
)"

{
	printf 'session=%sn' "$SESSION"
	printf 'kernel=%sn' "$(uname -r)"
	printf 'dynamic_debug_callsites=%sn' "$CALLS"
	printf 'trace_events=%sn' "$TRACE_LINES"
	printf 'dynamic_debug_messages=%sn' "$DEBUG_LINES"
} > "$MANIFEST"

chmod 0600 "$TRACE_LOG" "$DEBUG_LOG" "$MANIFEST"
find "$OUTPUT_DIR" -maxdepth 1 -type f -mtime "+${RETENTION_DAYS}" 
	-delete

printf 'Captură salvată: %sn' "$SESSION"

Instalează scriptul:

sudo chown root:root /opt/ddlab/ddlab-capture
sudo chmod 0750 /opt/ddlab/ddlab-capture

Instanța ddlab-focus evită contaminarea bufferului global. set_event_pid acceptă PID-ul shellului, iar event-fork include procesele copil dacă proiectul este extins ulterior. Selectorul module ddlab lasă celelalte callsite-uri Dynamic Debug neschimbate.

Ce trebuie să vezi la o rulare directă:

sudo /opt/ddlab/ddlab-capture
sudo cat "$(ls -1t /var/log/ddlab/*-manifest.txt | head -n 1)"

Exemplu:

Captură salvată: 20260926T101500Z
session=20260926T101500Z
kernel=6.8.0-xx-generic
dynamic_debug_callsites=2
trace_events=3
dynamic_debug_messages=3

Numărul callsite-urilor poate crește dacă extinzi modulul. Pentru codul prezent, trebuie să existe trei evenimente și trei mesaje acceptate.

Punct de control: dacă evenimentele sunt trei, dar mesajele sunt zero, inspectează imediat journalctl -k -n 50 și starea callsite-urilor înainte de a modifica scriptul.

Pasul 5 — Rulăm captura ca unitate oneshot, nu ca daemon permanent

De ce: sesiunea trebuie să aibă limite explicite, log propriu în journal și o stare de succes sau eșec vizibilă prin systemctl.

Fișierul /etc/systemd/system/ddlab-capture.service trebuie să fie:

[Unit]
Description=Captură limitată Dynamic Debug și tracepoint pentru ddlab
Documentation=https://docs.kernel.org/admin-guide/dynamic-debug-howto.html
RequiresMountsFor=/sys/kernel/debug /sys/kernel/tracing /var/log/ddlab
ConditionPathExists=/sys/kernel/debug/ddlab/request
ConditionPathExists=/proc/dynamic_debug/control
After=systemd-journald.service

[Service]
Type=oneshot
ExecStart=/opt/ddlab/ddlab-capture
TimeoutStartSec=30
User=root
Group=root
UMask=0077
Nice=10
IOSchedulingClass=idle
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/var/log/ddlab
NoNewPrivileges=yes
ProtectKernelModules=yes
ProtectKernelLogs=no
ProtectKernelTunables=no

[Install]
WantedBy=multi-user.target

Serviciul rulează ca root deoarece fișierele de control Dynamic Debug, tracefs și debugfs nu sunt destinate unui utilizator neprivilegiat. Izolarea păstrează sistemul de fișiere protejat, cu excepția directorului de rezultate. ProtectKernelTunables=no este deliberat: activarea tracefs și Dynamic Debug este chiar sarcina unității.

Încarcă definiția și pornește o sesiune:

sudo systemctl daemon-reload
sudo systemctl start ddlab-capture.service
sudo systemctl status ddlab-capture.service --no-pager
sudo journalctl -u ddlab-capture.service -n 20 --no-pager

Output-ul așteptat este un serviciu terminat cu succes:

ddlab-capture.service: Deactivated successfully.
Finished ddlab-capture.service - Captură limitată Dynamic Debug și tracepoint pentru ddlab.

inactive (dead) după terminare este normal pentru Type=oneshot fără RemainAfterExit=yes.

Punct de control: verifică systemctl show ddlab-capture.service -p Result -p ExecMainStatus. Rezultatul corect este Result=success și ExecMainStatus=0.

Pasul 6 — Alertăm dacă o sesiune lasă debug-ul activ

De ce: o întrerupere brutală, de exemplu oprirea mașinii în timpul capturii, poate ocoli handlerul shellului. Un control periodic trebuie să detecteze starea, nu să o ascundă prin dezactivare automată.

Fișierul /opt/ddlab/ddlab-guard trebuie să fie:

#!/usr/bin/env bash
set -euo pipefail

readonly DYNAMIC_CONTROL=/proc/dynamic_debug/control
readonly INSTANCE=/sys/kernel/tracing/instances/ddlab-focus
failed=0

if [[ -r "$DYNAMIC_CONTROL" ]] &&
   grep -E '[ddlab].*=.*p' "$DYNAMIC_CONTROL" >/dev/null; then
	logger -p daemon.warning -t ddlab-guard 
		'callsite-urile Dynamic Debug pentru ddlab sunt încă active'
	failed=1
fi

if [[ -r "${INSTANCE}/tracing_on" ]] &&
   [[ "$(<"${INSTANCE}/tracing_on")" != "0" ]]; then
	logger -p daemon.warning -t ddlab-guard 
		'instanța tracefs ddlab-focus înregistrează încă evenimente'
	failed=1
fi

if (( failed != 0 )); then
	printf 'ddlab-guard: a fost detectată o sesiune rămasă activăn' >&2
	exit 1
fi

printf 'ddlab-guard: starea este curatăn'

Instalează-l cu sudo chmod 0750 /opt/ddlab/ddlab-guard.

Fișierul /etc/systemd/system/ddlab-guard.service trebuie să fie:

[Unit]
Description=Verifică starea Dynamic Debug și tracefs pentru ddlab

[Service]
Type=oneshot
ExecStart=/opt/ddlab/ddlab-guard
User=root
Group=root
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ProtectKernelTunables=no
ProtectKernelLogs=no

Fișierul /etc/systemd/system/ddlab-guard.timer trebuie să fie:

[Unit]
Description=Verificare periodică a sesiunii ddlab

[Timer]
OnBootSec=5min
OnUnitActiveSec=15min
AccuracySec=1min
Persistent=true
Unit=ddlab-guard.service

[Install]
WantedBy=timers.target

Activează timerul:

sudo systemctl daemon-reload
sudo systemctl enable --now ddlab-guard.timer
sudo systemctl start ddlab-guard.service
sudo systemctl status ddlab-guard.service --no-pager
sudo systemctl list-timers ddlab-guard.timer --no-pager

OnUnitActiveSec=15min programează verificarea față de ultima activare, iar Persistent=true recuperează o execuție omisă cât sistemul a fost oprit. Alerta ajunge în journal cu prioritatea daemon.warning; nu este trimisă automat prin e-mail.

Ce trebuie să vezi:

ddlab-guard: starea este curată

Punct de control: journalctl -t ddlab-guard --since today nu trebuie să conțină avertismente după o captură încheiată normal.

Pasul 7 — Legăm cele trei dovezi într-un singur test de capăt

De ce: un status verde al serviciului nu dovedește singur că selectorul, evenimentul și revenirea au funcționat. Testul final verifică rezultatele și starea kernelului.

Rulează:

sudo systemctl start ddlab-capture.service

LATEST_MANIFEST="$(sudo ls -1t /var/log/ddlab/*-manifest.txt | head -n 1)"
SESSION="${LATEST_MANIFEST%-manifest.txt}"
sudo cat "$LATEST_MANIFEST"
sudo grep 'ddlab_request:' "${SESSION}-trace.txt"
sudo grep 'accepted request' "${SESSION}-dynamic-debug.txt"

if sudo grep -E '[ddlab].*=.*p' /proc/dynamic_debug/control; then
	printf 'EȘEC: Dynamic Debug a rămas activ.n' >&2
	exit 1
fi

test "$(< /sys/kernel/tracing/instances/ddlab-focus/tracing_on)" = "0"
printf 'OK: evenimente capturate, mesaje izolate, tracing oprit.n'

Exemplu de evenimente, cu PID-uri și timestamp-uri care vor diferi:

ddlab-captur-1234  [001] ...1 ...: ddlab_request: bytes=512 status=0
ddlab-captur-1234  [001] ...1 ...: ddlab_request: bytes=1024 status=0
ddlab-captur-1234  [001] ...1 ...: ddlab_request: bytes=2048 status=0
OK: evenimente capturate, mesaje izolate, tracing oprit.

Tabelul de acceptare este:

Dovadă Valoare așteptată
trace_events 3
dynamic_debug_messages 3
status în fiecare tracepoint 0
tracing_on după sesiune 0
callsite-uri ddlab cu flagul p după sesiune 0

Fișierul tracepoint arată cererile structurate. Fișierul Dynamic Debug oferă contextul textual din funcție. Manifestul face captura auditabilă fără să amestece logurile altor module.

Pentru o investigație de memorie, nu interpreta aceste evenimente ca dovezi ale unui leak; acolo este necesar un instrument dedicat și separarea alarmelor reale de false positive, descrisă în ghidul despre kmemleak și memory leaks în kernel.

Șase erori care indică exact componenta defectă

ddlab-capture: Dynamic Debug indisponibil

Cauză: /proc/dynamic_debug/control lipsește sau nu este accesibil pentru scriere. Verifică suportul kernelului:

grep CONFIG_DYNAMIC_DEBUG /boot/config-"$(uname -r)"
sudo test -e /proc/dynamic_debug/control

Nu înlocui controlul granular cu ridicarea globală a nivelului consolei.

modprobe: FATAL: Module ddlab not found in directory /lib/modules/...

Cauză: modulul nu a fost instalat în arborele kernelului activ sau depmod nu a actualizat indexul.

sudo install -D -m 0644 /opt/ddlab/ddlab.ko 
  "/lib/modules/$(uname -r)/extra/ddlab.ko"
sudo depmod -a
sudo modprobe ddlab

modprobe: ERROR: could not insert 'ddlab': Key was rejected by service

Cauză: Secure Boot impune semnarea modulelor, iar modulul local nu este semnat. Nu dezactiva Secure Boot ca remediu automat. Mută laboratorul într-o mașină virtuală de test sau semnează modulul prin procedura aprobată de administratorul platformei.

ddlab-capture: lipsește tracepoint-ul ddlab_request

Cauză: modulul nu este încărcat, tracefs nu este montat sau rulează o versiune veche a modulului.

sudo modprobe ddlab
mountpoint -q /sys/kernel/tracing ||
  sudo mount -t tracefs nodev /sys/kernel/tracing
test -d /sys/kernel/tracing/events/ddlab/ddlab_request

ddlab-capture: există deja callsite-uri ddlab active

Cauză: o sesiune manuală a activat anterior flagul p. Inspectează cine lucrează pe sistem înainte de dezactivare. Dacă nu există altă investigație activă:

printf 'module ddlab -pmlftn' |
  sudo tee /proc/dynamic_debug/control >/dev/null
sudo /opt/ddlab/ddlab-guard

Această protecție previne ștergerea accidentală a stării altui administrator.

ddlab-guard: a fost detectată o sesiune rămasă activă

Cauză: Dynamic Debug sau instanța tracefs nu au revenit la starea neutră. Oprește întâi înregistrarea, apoi callsite-urile:

printf '0n' |
  sudo tee /sys/kernel/tracing/instances/ddlab-focus/tracing_on >/dev/null
printf '0n' |
  sudo tee /sys/kernel/tracing/instances/ddlab-focus/events/ddlab/ddlab_request/enable >/dev/null
printf 'module ddlab -pmlftn' |
  sudo tee /proc/dynamic_debug/control >/dev/null
sudo /opt/ddlab/ddlab-guard

Demontăm laboratorul fără să lăsăm callsite-uri sau timere active

Avertisment: comenzile următoare șterg toate capturile din /var/log/ddlab și sursele laboratorului. Salvează separat dovezile pe care trebuie să le păstrezi.

sudo systemctl disable --now ddlab-guard.timer
sudo systemctl stop ddlab-capture.service 2>/dev/null || true

printf '0n' |
  sudo tee /sys/kernel/tracing/instances/ddlab-focus/tracing_on >/dev/null
printf 'module ddlab -pmlftn' |
  sudo tee /proc/dynamic_debug/control >/dev/null

sudo modprobe -r ddlab
sudo rm -rf /sys/kernel/tracing/instances/ddlab-focus
sudo rm -f /etc/systemd/system/ddlab-capture.service
sudo rm -f /etc/systemd/system/ddlab-guard.service
sudo rm -f /etc/systemd/system/ddlab-guard.timer
sudo rm -f /etc/ddlab.conf
sudo rm -f "/lib/modules/$(uname -r)/extra/ddlab.ko"
sudo rm -rf /opt/ddlab /var/log/ddlab
sudo depmod -a
sudo systemctl daemon-reload
sudo systemctl reset-failed

Nu demonta global debugfs sau tracefs: alte instrumente de diagnostic le pot utiliza.

Verificarea curățeniei:

test ! -e /sys/kernel/debug/ddlab/request
test ! -d /sys/kernel/tracing/instances/ddlab-focus
test ! -e "/lib/modules/$(uname -r)/extra/ddlab.ko"
printf 'Laborator eliminat.n'

Lista de control înainte de folosirea pe un modul real

  • ☐ Header-ele instalate corespund exact rezultatului uname -r.
  • ☐ Modulul ddlab apare în catalogul /proc/dynamic_debug/control.
  • ☐ Numai selectorul module ddlab primește flagul p.
  • ☐ Instanța ddlab-focus înregistrează numai ddlab:ddlab_request.
  • ☐ Captura produce exact trei evenimente cu status=0.
  • ☐ Jurnalul conține exact cele trei cereri acceptate.
  • ☐ Fișierele din /var/log/ddlab au permisiunea 0600.
  • ☐ După captură, tracing_on este 0.
  • ☐ După captură, niciun callsite ddlab nu mai are flagul p.
  • ☐ ddlab-guard.timer apare în lista timerelor active.

Următorul pas logic este înlocuirea selectorului module ddlab cu numele modulului intern investigat și alegerea unui tracepoint deja expus de acel subsistem. Înainte de schimbare, inventariază callsite-urile și câmpurile reale din format; nu presupune că două versiuni de kernel au aceleași linii sau aceleași evenimente.

Pentru capturi mai lungi, păstrează aceeași izolare, dar exportă bufferul prin trace-cmd record și analizează fișierul rezultat în KernelShark. Dacă simptomul este un blocaj complet, treci la instrumentele dedicate pentru investigarea blocajelor kernel cu Magic SysRq, NMI watchdog și hung task detector, fără să transformi logging-ul suplimentar într-un substitut pentru watchdog.

Întrebări frecvente

De ce nu activăm toate mesajele Dynamic Debug?

O comandă fără selector poate activa callsite-uri din întregul kernel. Proiectul folosește module ddlab pentru a limita mesajele la subsistemul investigat și le dezactivează la terminarea capturii.

De ce serviciul rulează ca root?

Fișierele de control din Dynamic Debug, tracefs și debugfs necesită privilegii administrative. Unitatea limitează durata, protejează sistemul de fișiere și scrie doar în directorul de rezultate.

Este normal ca serviciul să apară inactive (dead)?

Da. ddlab-capture.service este o unitate Type=oneshot, deci rulează captura, salvează rezultatele și se încheie. Succesul se verifică prin Result=success și ExecMainStatus=0.

Pot folosi proiectul cu Secure Boot activ?

Numai dacă modulul este semnat conform politicii sistemului. Dacă încărcarea este refuzată, folosește o mașină virtuală de laborator sau procedura aprobată de semnare; nu dezactiva automat Secure Boot.

Surse

Facebook
X
WhatsApp
Dynamic Debug și kernel tracepoints: logging izolat pentru un modul Linux

Te-ar putea interesa si: