diff --git a/share/man/man9/KASSERT.9 b/share/man/man9/KASSERT.9 --- a/share/man/man9/KASSERT.9 +++ b/share/man/man9/KASSERT.9 @@ -29,7 +29,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 19, 2024 +.Dd April 1, 2026 .Dt KASSERT 9 .Os .Sh NAME @@ -153,10 +153,12 @@ MPASS(td == curthread); .Ed .Pp -located on line 87 of a file named foo.c would generate the following panic +located in a function named +.Fn bar +on line 87 of a file named foo.c would generate the following panic message: .Bd -literal -offset indent -panic: Assertion td == curthread failed at foo.c:87 +panic: Assertion td == curthread failed at foo.c:87 (bar) .Ed .Pp This is a simple condition, and the message provides enough information to diff --git a/share/man/man9/panic.9 b/share/man/man9/panic.9 --- a/share/man/man9/panic.9 +++ b/share/man/man9/panic.9 @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 17, 2023 +.Dd April 1, 2026 .Dt PANIC 9 .Os .Sh NAME @@ -143,6 +143,13 @@ .Xr pause 9 may be performed as a busy-wait. .El +.Sh IMPLEMENTATION NOTES +For kernel callers, both interfaces are provided as macros that capture the +call site's file name, line number, and function name before invoking the +underlying implementation. +This context is included in panic diagnostics unless +.Cd "options KASSERT_PANIC_NOCONTEXT" +is set. .Sh RETURN VALUES The .Fn panic diff --git a/sys/amd64/amd64/efirt_machdep.c b/sys/amd64/amd64/efirt_machdep.c --- a/sys/amd64/amd64/efirt_machdep.c +++ b/sys/amd64/amd64/efirt_machdep.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -394,6 +395,15 @@ return (error); } +void +efi_panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + panic(fmt, ap); +} + SYSCTL_PROC(_debug, OID_AUTO, efi_time, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, efi_time_sysctl_handler, "I", diff --git a/sys/amd64/amd64/efirt_support.S b/sys/amd64/amd64/efirt_support.S --- a/sys/amd64/amd64/efirt_support.S +++ b/sys/amd64/amd64/efirt_support.S @@ -79,7 +79,7 @@ decl %ebx jz 1f movq $efi_rt_panic_str, %rdi - call panic + call efi_panic 1: movq EC_FPTR(%rdi), %rax movq $efi_rt_fault, PCB_ONFAULT(%rsi) callq *%rax diff --git a/sys/conf/NOTES b/sys/conf/NOTES --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -563,6 +563,13 @@ # options KASSERT_PANIC_OPTIONAL +# +# KASSERT_PANIC_NOCONTEXT omits source-location context from panic(9) and +# KASSERT diagnostics. This reduces kernel image size at the cost of less +# precise panic messages. +# +options KASSERT_PANIC_NOCONTEXT + # # The DIAGNOSTIC option is used to enable extra debugging information # and invariants checking. The added checks are too expensive or noisy diff --git a/sys/conf/options b/sys/conf/options --- a/sys/conf/options +++ b/sys/conf/options @@ -605,6 +605,7 @@ INVARIANT_SUPPORT opt_global.h INVARIANTS opt_global.h KASSERT_PANIC_OPTIONAL opt_global.h +KASSERT_PANIC_NOCONTEXT opt_global.h MAXCPU opt_global.h MAXMEMDOM opt_global.h MAXPHYS opt_maxphys.h diff --git a/sys/contrib/openzfs/include/os/freebsd/spl/sys/cmn_err.h b/sys/contrib/openzfs/include/os/freebsd/spl/sys/cmn_err.h --- a/sys/contrib/openzfs/include/os/freebsd/spl/sys/cmn_err.h +++ b/sys/contrib/openzfs/include/os/freebsd/spl/sys/cmn_err.h @@ -72,8 +72,10 @@ extern void vuprintf(const char *, __va_list) __attribute__((format(__printf__, 1, 0))); -extern void panic(const char *, ...) - __attribute__((format(__printf__, 1, 2), __noreturn__)); +struct panic_codeptr; + +extern void do_panic(const struct panic_codeptr *, const char *, ...) + __attribute__((format(__printf__, 2, 3), __noreturn__)); #define cmn_err_once(ce, ...) \ do { \ diff --git a/sys/contrib/openzfs/module/lua/lapi.c b/sys/contrib/openzfs/module/lua/lapi.c --- a/sys/contrib/openzfs/module/lua/lapi.c +++ b/sys/contrib/openzfs/module/lua/lapi.c @@ -124,8 +124,8 @@ LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { lua_CFunction old; lua_lock(L); - old = G(L)->panic; - G(L)->panic = panicf; + old = G(L)->on_panic; + G(L)->on_panic = panicf; lua_unlock(L); return old; } diff --git a/sys/contrib/openzfs/module/lua/ldo.c b/sys/contrib/openzfs/module/lua/ldo.c --- a/sys/contrib/openzfs/module/lua/ldo.c +++ b/sys/contrib/openzfs/module/lua/ldo.c @@ -191,9 +191,9 @@ luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ - if (G(L)->panic) { /* panic function? */ + if (G(L)->on_panic) { /* panic function? */ lua_unlock(L); - G(L)->panic(L); /* call it (last chance to jump out) */ + G(L)->on_panic(L); /* call it (last chance to jump out) */ } panic("no error handler"); } diff --git a/sys/contrib/openzfs/module/lua/lstate.h b/sys/contrib/openzfs/module/lua/lstate.h --- a/sys/contrib/openzfs/module/lua/lstate.h +++ b/sys/contrib/openzfs/module/lua/lstate.h @@ -140,7 +140,7 @@ int gcpause; /* size of pause between successive GCs */ int gcmajorinc; /* pause between major collections (only in gen. mode) */ int gcstepmul; /* GC `granularity' */ - lua_CFunction panic; /* to be called in unprotected errors */ + lua_CFunction on_panic; /* to be called in unprotected errors */ struct lua_State *mainthread; const lua_Number *version; /* pointer to version number */ TString *memerrmsg; /* memory-error message */ diff --git a/sys/contrib/openzfs/module/lua/lstate.c b/sys/contrib/openzfs/module/lua/lstate.c --- a/sys/contrib/openzfs/module/lua/lstate.c +++ b/sys/contrib/openzfs/module/lua/lstate.c @@ -288,7 +288,7 @@ g->strt.hash = NULL; setnilvalue(&g->l_registry); luaZ_initbuffer(L, &g->buff); - g->panic = NULL; + g->on_panic = NULL; g->version = NULL; g->gcstate = GCSpause; g->allgc = NULL; diff --git a/sys/dev/mlx4/mlx4_core/mlx4_cmd.c b/sys/dev/mlx4/mlx4_core/mlx4_cmd.c --- a/sys/dev/mlx4/mlx4_core/mlx4_cmd.c +++ b/sys/dev/mlx4/mlx4_core/mlx4_cmd.c @@ -32,6 +32,10 @@ * SOFTWARE. */ +#include +#include +#include + #include #include #include diff --git a/sys/dev/mlx4/mlx4_core/mlx4_fw.c b/sys/dev/mlx4/mlx4_core/mlx4_fw.c --- a/sys/dev/mlx4/mlx4_core/mlx4_fw.c +++ b/sys/dev/mlx4/mlx4_core/mlx4_fw.c @@ -40,6 +40,7 @@ #include #include +#include #include "fw.h" #include "icm.h" diff --git a/sys/dev/mlx4/mlx4_ib/mlx4_ib_main.c b/sys/dev/mlx4/mlx4_ib/mlx4_ib_main.c --- a/sys/dev/mlx4/mlx4_ib/mlx4_ib_main.c +++ b/sys/dev/mlx4/mlx4_ib/mlx4_ib_main.c @@ -45,6 +45,7 @@ #include #include +#include #include #include diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c --- a/sys/kern/kern_shutdown.c +++ b/sys/kern/kern_shutdown.c @@ -804,13 +804,45 @@ return (0); } +static const char * +trim_to_sys(const char *path) +{ + const char *tp, *cp = path; + + while ((tp = strstr(cp, "/sys/")) != NULL ) { + cp = tp + 1; + } + return (cp); +} + +static void +panic_print_location(const struct panic_codeptr *where) +{ + +#if !defined(KASSERT_PANIC_NOCONTEXT) + printf(" at %s:%d (%s)", trim_to_sys(where->fname), where->linen, + where->funcn); +#else + (void)where; +#endif +} + #ifdef KASSERT_PANIC_OPTIONAL +static void +kassert_print(const struct panic_codeptr *where, const char *buf) +{ + + printf("KASSERT failed: %s", buf); + panic_print_location(where); + printf("\n"); +} + /* * Called by KASSERT, this decides if we will panic * or if we will log via printf and/or ktr. */ void -kassert_panic(const char *fmt, ...) +do_kassert_panic(const struct panic_codeptr *where, const char *fmt, ...) { static char buf[256]; va_list ap; @@ -825,7 +857,7 @@ */ if (KERNEL_PANICKED() && kassert_suppress_in_panic) { if (kassert_do_log) { - printf("KASSERT failed: %s\n", buf); + kassert_print(where, buf); #ifdef KDB if (trace_all_panics && trace_on_panic) kdb_backtrace(); @@ -842,7 +874,7 @@ (kassert_log_panic_at > 0 && kassert_warnings >= kassert_log_panic_at)) { va_start(ap, fmt); - vpanic(fmt, ap); + do_vpanic(where, fmt, ap); /* NORETURN */ } #ifdef KTR @@ -859,7 +891,7 @@ static int curerr; if (ppsratecheck(&lasterr, &curerr, kassert_log_pps_limit)) { - printf("KASSERT failed: %s\n", buf); + kassert_print(where, buf); kdb_backtrace(); } } @@ -874,21 +906,22 @@ #endif /* - * Panic is called on unresolvable fatal errors. It prints "panic: mesg", - * and then reboots. If we are called twice, then we avoid trying to sync - * the disks as this often leads to recursive panics. + * Panic is called on unresolvable fatal errors. It prints "panic: mesg" + * and any available source-location context, and then reboots. If we are + * called twice, then we avoid trying to sync the disks as this often leads + * to recursive panics. */ void -panic(const char *fmt, ...) +do_panic(const struct panic_codeptr *where, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - vpanic(fmt, ap); + do_vpanic(where, fmt, ap); } void -vpanic(const char *fmt, va_list ap) +do_vpanic(const struct panic_codeptr *where, const char *fmt, va_list ap) { #ifdef SMP cpuset_t other_cpus; @@ -940,16 +973,18 @@ /* Unmute when panic */ cn_mute = 0; + if (newpanic) + cngrab(); if (newpanic) { (void)vsnprintf(buf, sizeof(buf), fmt, ap); panicstr = buf; - cngrab(); - printf("panic: %s\n", buf); + printf("panic: %s", buf); } else { printf("panic: "); vprintf(fmt, ap); - printf("\n"); } + panic_print_location(where); + printf("\n"); #ifdef SMP printf("cpuid = %d\n", PCPU_GET(cpuid)); #endif diff --git a/sys/sys/efi.h b/sys/sys/efi.h --- a/sys/sys/efi.h +++ b/sys/sys/efi.h @@ -394,6 +394,9 @@ int efi_status_to_errno(efi_status status); +void efi_panic(const char *, ...) __attribute__((format(printf, 1, 2), + __noreturn__)); + #endif /* _KERNEL */ #endif /* _SYS_EFI_H_ */ diff --git a/sys/sys/kassert.h b/sys/sys/kassert.h --- a/sys/sys/kassert.h +++ b/sys/sys/kassert.h @@ -107,14 +107,33 @@ #define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") #endif +struct panic_codeptr { + const char * const fname; + const int linen; + const char * const funcn; +}; + +#if !defined(KASSERT_PANIC_NOCONTEXT) +# define PANIC_CTX ({static const struct panic_codeptr _panic_ctx = { \ + .fname = __FILE__, .linen = __LINE__, .funcn = __func__}; \ + &_panic_ctx;}) +#else +# define PANIC_CTX ((const struct panic_codeptr *)(0)) +#endif /* KASSERT_PANIC_NOCONTEXT */ + /* * These functions need to be declared before the KASSERT macro is invoked in * !KASSERT_PANIC_OPTIONAL builds, so their declarations are sort of out of * place compared to other function definitions in this header. On the other * hand, this header is a bit disorganized anyway. */ -void panic(const char *, ...) __dead2 __printflike(1, 2); -void vpanic(const char *, __va_list) __dead2 __printflike(1, 0); +void do_panic(const struct panic_codeptr *, const char *, ...) + __dead2 __printflike(2, 3); +void do_vpanic(const struct panic_codeptr *, const char *, __va_list) + __dead2 __printflike(2, 0); + +# define panic(args...) do_panic(PANIC_CTX, ## args) +# define vpanic(args...) do_vpanic(PANIC_CTX, ## args) #endif /* _KERNEL */ #if defined(_STANDALONE) @@ -127,10 +146,15 @@ */ int printf(const char *, ...) __printflike(1, 2); # define kassert_panic printf +void panic(const char *, ...) __dead2 __printflike(1, 2); #else /* !_STANDALONE */ # if defined(WITNESS) || defined(INVARIANT_SUPPORT) # ifdef KASSERT_PANIC_OPTIONAL -void kassert_panic(const char *fmt, ...) __printflike(1, 2); + +void do_kassert_panic(const struct panic_codeptr *, const char *fmt, ...) + __printflike(2, 3); +# define kassert_panic(args...) do_kassert_panic(PANIC_CTX, ## args) + # else # define kassert_panic panic # endif /* KASSERT_PANIC_OPTIONAL */ @@ -161,8 +185,13 @@ #define MPASS(ex) MPASS4(ex, #ex, __FILE__, __LINE__) #define MPASS2(ex, what) MPASS4(ex, what, __FILE__, __LINE__) #define MPASS3(ex, file, line) MPASS4(ex, #ex, file, line) +#if !defined(KASSERT_PANIC_NOCONTEXT) +#define MPASS4(ex, what, file, line) \ + KASSERT((ex), ("Assertion %s failed", what)) +#else #define MPASS4(ex, what, file, line) \ KASSERT((ex), ("Assertion %s failed at %s:%d", what, file, line)) +#endif /* * Assert that a pointer can be loaded from memory atomically.