Page MenuHomeFreeBSD

D50825.id162815.diff
No OneTemporary

D50825.id162815.diff

diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -51,6 +51,10 @@
# xargs -n1 | sort | uniq -d;
# done
+# 20250925: kgdb python scripts moved:
+OLD_FILES+=usr/libexec/kgdb/acttrace.py
+OLD_DIRS+=usr/libexec/kgdb
+
# 20250917: Remove a miscapitalized manual
OLD_FILES+=usr/share/man/man9/vnet.9.gz
OLD_FILES+=usr/share/man/man9/vimage.9.gz
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -179,8 +179,6 @@
..
hyperv
..
- kgdb
- ..
lpr
ru
..
diff --git a/libexec/Makefile b/libexec/Makefile
--- a/libexec/Makefile
+++ b/libexec/Makefile
@@ -10,7 +10,6 @@
flua \
getty \
${_hyperv} \
- kgdb \
${_mail.local} \
${_makewhatis.local} \
${_mknetid} \
diff --git a/libexec/kgdb/Makefile b/libexec/kgdb/Makefile
deleted file mode 100644
--- a/libexec/kgdb/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-FILESDIR?= /usr/libexec/kgdb
-
-FILES= acttrace.py
-
-.include <bsd.prog.mk>
diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk
--- a/sys/conf/kern.post.mk
+++ b/sys/conf/kern.post.mk
@@ -398,6 +398,8 @@
.endif
.endfor
+GDB_PYTHON_MODULES!= find $S/tools/gdb -name \*.py
+
${_ILINKS}:
@case ${.TARGET} in \
machine) \
@@ -447,6 +449,13 @@
.if defined(DEBUG) && !defined(INSTALL_NODEBUG) && ${MK_KERNEL_SYMBOLS} != "no"
mkdir -p ${DESTDIR}${KERN_DEBUGDIR}${KODIR}
${INSTALL} -p -m ${KMODMODE} -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO}.debug ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/
+ ${INSTALL} -p -m ${KMODMODE} -o ${KMODOWN} -g ${KMODGRP} \
+ $S/tools/kernel-gdb.py ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/${KERNEL_KO}-gdb.py
+ mkdir -p ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/gdb
+.for file in ${GDB_PYTHON_MODULES}
+ ${INSTALL} -p -m ${KMODMODE} -o ${KMODOWN} -g ${KMODGRP} \
+ $S/tools/gdb/${file:T} ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/gdb/${file:T}
+.endfor
.endif
.if defined(KERNEL_EXTRA_INSTALL)
${INSTALL} -p -m ${KMODMODE} -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_EXTRA_INSTALL} ${DESTDIR}${KODIR}/
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -521,6 +521,9 @@
#ifdef VIMAGE
TAILQ_INIT(&set_vnet_list);
vnet_save_init((void *)VNET_START, VNET_STOP - VNET_START);
+ ef->vnet_start = VNET_START;
+ ef->vnet_stop = VNET_STOP;
+ ef->vnet_base = VNET_START;
#endif
}
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -70,6 +70,7 @@
typedef struct {
void *addr;
+ void *origaddr; /* Used by debuggers. */
Elf_Off size;
int flags; /* Section flags. */
int sec; /* Original section number. */
@@ -492,7 +493,8 @@
case SHT_FINI_ARRAY:
if (shdr[i].sh_addr == 0)
break;
- ef->progtab[pb].addr = (void *)shdr[i].sh_addr;
+ ef->progtab[pb].addr = ef->progtab[pb].origaddr =
+ (void *)shdr[i].sh_addr;
if (shdr[i].sh_type == SHT_PROGBITS)
ef->progtab[pb].name = "<<PROGBITS>>";
#ifdef __amd64__
@@ -1088,6 +1090,8 @@
ef->progtab[pb].name = "<<NOBITS>>";
if (ef->progtab[pb].name != NULL &&
!strcmp(ef->progtab[pb].name, DPCPU_SETNAME)) {
+ ef->progtab[pb].origaddr =
+ (void *)(uintptr_t)mapbase;
ef->progtab[pb].addr =
dpcpu_alloc(shdr[i].sh_size);
if (ef->progtab[pb].addr == NULL) {
@@ -1101,6 +1105,8 @@
#ifdef VIMAGE
else if (ef->progtab[pb].name != NULL &&
!strcmp(ef->progtab[pb].name, VNET_SETNAME)) {
+ ef->progtab[pb].origaddr =
+ (void *)(uintptr_t)mapbase;
ef->progtab[pb].addr =
vnet_data_alloc(shdr[i].sh_size);
if (ef->progtab[pb].addr == NULL) {
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -10785,6 +10785,10 @@
if (!V_pf_status.running)
return (PF_PASS);
+ printf("%s:%d status %p base %p sym %p\n", __func__, __LINE__,
+ &V_pf_status, (void *)curvnet->vnet_data_base, &VNET_NAME(pf_status));
+ panic("I don't want to be around anymore");
+
kif = (struct pfi_kkif *)ifp->if_pf_kif;
if (__predict_false(kif == NULL)) {
diff --git a/sys/tools/gdb/README.txt b/sys/tools/gdb/README.txt
new file mode 100644
--- /dev/null
+++ b/sys/tools/gdb/README.txt
@@ -0,0 +1,8 @@
+This directory contains Python scripts that can be loaded by GDB to help debug
+FreeBSD kernel crashes.
+
+Add new commands and functions in their own files. Functions with general
+utility should be added to freebsd.py. sys/tools/kernel-gdb.py is installed
+into the kernel debug directory (typically /usr/lib/debug/boot/kernel). It will
+be automatically loaded by kgdb when opening a vmcore, so if you add new GDB
+commands or functions, that script should be updated to import them.
diff --git a/libexec/kgdb/acttrace.py b/sys/tools/gdb/acttrace.py
rename from libexec/kgdb/acttrace.py
rename to sys/tools/gdb/acttrace.py
--- a/libexec/kgdb/acttrace.py
+++ b/sys/tools/gdb/acttrace.py
@@ -6,21 +6,10 @@
#
import gdb
+from freebsd import *
-def symval(name):
- return gdb.lookup_global_symbol(name).value()
-
-
-def tid_to_gdb_thread(tid):
- for thread in gdb.inferiors()[0].threads():
- if thread.ptid[2] == tid:
- return thread
- else:
- return None
-
-
-def all_pcpus():
+def pcpu_foreach():
mp_maxid = symval("mp_maxid")
cpuid_to_pcpu = symval("cpuid_to_pcpu")
@@ -33,6 +22,12 @@
class acttrace(gdb.Command):
+ """
+ Register an acttrace command with gdb.
+
+ When run, acttrace prints the stack trace of all threads that were on-CPU
+ at the time of the panic.
+ """
def __init__(self):
super(acttrace, self).__init__("acttrace", gdb.COMMAND_USER)
@@ -40,7 +35,7 @@
# Save the current thread so that we can switch back after.
curthread = gdb.selected_thread()
- for pcpu in all_pcpus():
+ for pcpu in pcpu_foreach():
td = pcpu['pc_curthread']
tid = td['td_tid']
diff --git a/sys/tools/gdb/freebsd.py b/sys/tools/gdb/freebsd.py
new file mode 100644
--- /dev/null
+++ b/sys/tools/gdb/freebsd.py
@@ -0,0 +1,63 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
+#
+
+import gdb
+
+def symval(name):
+ sym = gdb.lookup_global_symbol(name)
+ if sym is None:
+ sym = gdb.lookup_static_symbol(name)
+ if sym is None:
+ raise gdb.GdbError(f"Symbol '{name}' not found")
+ return sym.value()
+
+
+def _queue_foreach(head, field, headf, nextf):
+ elm = head[headf]
+ while elm != 0:
+ yield elm
+ elm = elm[field][nextf]
+
+
+def list_foreach(head, field):
+ """sys/queue.h-style iterator."""
+ return _queue_foreach(head, field, "lh_first", "le_next")
+
+
+def tailq_foreach(head, field):
+ """sys/queue.h-style iterator."""
+ return _queue_foreach(head, field, "tqh_first", "tqe_next")
+
+
+def linker_file_foreach():
+ """Iterate over loaded linker files."""
+ return tailq_foreach(symval("linker_files"), "link")
+
+
+def tid_to_gdb_thread(tid):
+ """Convert a FreeBSD kernel thread ID to a gdb inferior thread."""
+ for thread in gdb.inferiors()[0].threads():
+ if thread.ptid[2] == tid:
+ return thread
+ else:
+ return None
+
+
+def tdfind(tid, pid=-1):
+ """Convert a FreeBSD kernel thread ID to a struct thread pointer."""
+ td = tdfind.cached_threads.get(int(tid))
+ if td:
+ return td
+
+ for p in list_foreach(symval("allproc"), "p_list"):
+ if pid != -1 and pid != p['p_pid']:
+ continue
+ for td in tailq_foreach(p['p_threads'], "td_plist"):
+ ntid = td['td_tid']
+ tdfind.cached_threads[int(ntid)] = td
+ if ntid == tid:
+ return td
+tdfind.cached_threads = dict()
diff --git a/sys/tools/gdb/vnet.py b/sys/tools/gdb/vnet.py
new file mode 100644
--- /dev/null
+++ b/sys/tools/gdb/vnet.py
@@ -0,0 +1,102 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
+#
+
+import gdb
+import traceback
+from freebsd import *
+
+class vnet(gdb.Function):
+ """
+ Register a function to look up VNET variables by name.
+
+ To look at the value of a VNET variable V_foo, print $V("foo"). The
+ currently selected thread's VNET is used by default, but can be optionally
+ specified as a second parameter, e.g., $V("foo", <vnet>), where <vnet> is a
+ pointer to a struct vnet (e.g., vnet0 or allprison.tqh_first->pr_vnet) or a
+ string naming a jail.
+ """
+ def __init__(self):
+ super(vnet, self).__init__("V")
+
+ def invoke(self, sym, vnet=None):
+ sym = sym.string()
+ if sym.startswith("V_"):
+ sym = sym[len("V_"):]
+ if gdb.lookup_symbol("sysctl___kern_features_vimage")[0] is None:
+ return symval(sym)
+
+ # Look up the VNET's base address.
+ if vnet is None:
+ vnet = tdfind(gdb.selected_thread().ptid[2])['td_vnet']
+ if not vnet:
+ # If curthread->td_vnet == NULL, vnet0 is the current vnet.
+ vnet = symval("vnet0")
+ elif type(vnet) == str:
+ if vnet == "vnet0":
+ vnet = symval("vnet0")
+ else:
+ for prison in tailq_foreach(symval("allprison")):
+ if prison['pr_name'].string() == vnet:
+ vnet = prison['pr_vnet']
+ break
+ else:
+ raise gdb.error(f"No prison named {vnet}")
+
+ def uintptr_t(val):
+ return val.cast(gdb.lookup_type("uintptr_t"))
+
+ # Now the tricky part: compute the address of the symbol relative
+ # to the selected VNET. In the compiled kernel this is done at
+ # load time by applying a magic transformation to relocations
+ # against symbols in the vnet linker set. Here we have to apply
+ # the transformation manually.
+ vnet_data_base = vnet['vnet_data_base']
+ vnet_entry = symval("vnet_entry_" + sym)
+ vnet_entry_addr = uintptr_t(vnet_entry.address)
+
+ # First, which kernel module does the symbol belong to?
+ for lf in linker_file_foreach():
+ # Find the bounds of this linker file's VNET linker set. The
+ # struct containing the bounds depends on the type of the linker
+ # file, and unfortunately both are called elf_file_t. So we use a
+ # PC value from the compilation unit (either link_elf.c or
+ # link_elf_obj.c) to disambiguate.
+ block = gdb.block_for_pc(lf['ops']['cls']['methods'][0]['func'])
+ elf_file_t = gdb.lookup_type("elf_file_t", block).target()
+ ef = lf.cast(elf_file_t)
+
+ file_type = lf['ops']['cls']['name'].string()
+ if file_type == "elf64":
+ start = uintptr_t(ef['vnet_start'])
+ if start == 0:
+ # This linker file doesn't have a VNET linker set.
+ continue
+ end = uintptr_t(ef['vnet_stop'])
+ base = uintptr_t(ef['vnet_base'])
+ elif file_type == "elf64_obj":
+ for i in range(ef['nprogtab']):
+ pe = ef['progtab'][i]
+ if pe['name'].string() == "set_vnet":
+ start = uintptr_t(pe['origaddr'])
+ end = start + uintptr_t(pe['size'])
+ base = uintptr_t(pe['addr'])
+ break
+ else:
+ # This linker file doesn't have a VNET linker set.
+ continue
+ else:
+ path = lf['pathname'].string()
+ raise gdb.error(f"{path} has unexpected linker file type {file_type}")
+
+ if vnet_entry_addr >= start and vnet_entry_addr < end:
+ # The symbol belongs to this linker file, so compute the final
+ # address.
+ obj = gdb.Value(vnet_data_base + vnet_entry_addr - start + base)
+ return obj.cast(vnet_entry.type.pointer()).dereference()
+
+
+# Register with gdb.
+vnet()
diff --git a/sys/tools/kernel-gdb.py b/sys/tools/kernel-gdb.py
new file mode 100644
--- /dev/null
+++ b/sys/tools/kernel-gdb.py
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), "gdb"))
+
+# Import FreeBSD kernel debugging commands and modules below.
+import acttrace
+import vnet
diff --git a/usr.sbin/crashinfo/crashinfo.sh b/usr.sbin/crashinfo/crashinfo.sh
--- a/usr.sbin/crashinfo/crashinfo.sh
+++ b/usr.sbin/crashinfo/crashinfo.sh
@@ -217,10 +217,7 @@
file=`mktemp /tmp/crashinfo.XXXXXX`
if [ $? -eq 0 ]; then
- scriptdir=/usr/libexec/kgdb
-
echo "bt -full" >> $file
- echo "source ${scriptdir}/acttrace.py" >> $file
echo "acttrace" >> $file
echo "quit" >> $file
${GDB%gdb}kgdb -q $KERNEL $VMCORE < $file

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 3:48 AM (17 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30866998
Default Alt Text
D50825.id162815.diff (12 KB)

Event Timeline