diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c --- a/sys/kern/kern_ktrace.c +++ b/sys/kern/kern_ktrace.c @@ -31,15 +31,16 @@ * SUCH DAMAGE. */ -#include #include "opt_ktrace.h" -#include -#include +#define EXTERR_CATEGORY EXTERR_KTRACE #include +#include +#include #include #include #include +#include #include #include #include @@ -48,16 +49,15 @@ #include #include #include -#include -#include #include #include -#include #include #include #include #include #include +#include +#include #include @@ -104,6 +104,7 @@ struct ktr_fault ktr_fault; struct ktr_faultend ktr_faultend; struct ktr_struct_array ktr_struct_array; + struct ktr_exterr ktr_exterr; } ktr_data; STAILQ_ENTRY(ktr_request) ktr_list; }; @@ -126,6 +127,7 @@ [KTR_STRUCT_ARRAY] = sizeof(struct ktr_struct_array), [KTR_ARGS] = 0, [KTR_ENVS] = 0, + [KTR_EXTERR] = sizeof(struct ktr_exterr), }; static STAILQ_HEAD(, ktr_request) ktr_free; @@ -1033,8 +1035,35 @@ ktr_enqueuerequest(td, req); ktrace_exit(td); } + +void +ktrexterr(struct thread *td) +{ + struct ktr_request *req; + struct ktr_exterr *ktre; + + if (!KTRPOINT(td, KTR_EXTERR)) + return; + + req = ktr_getrequest(KTR_EXTERR); + if (req == NULL) + return; + ktre = &req->ktr_data.ktr_exterr; + if (exterr_to_ue(td, &ktre->ue) == 0) + ktr_enqueuerequest(td, req); + else + ktr_freerequest(req); + ktrace_exit(td); +} #endif /* KTRACE */ +#ifndef KTRACE +void +ktrexterr(struct thread *td __unused) +{ +} +#endif + /* Interface and common routines */ #ifndef _SYS_SYSPROTO_H_ diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -2202,6 +2202,23 @@ return (kcmp_cmp((uintptr_t)fp1->f_data, (uintptr_t)fp2->f_data)); } +int +exterr_to_ue(struct thread *td, struct uexterror *ue) +{ + if ((td->td_pflags2 & TDP2_EXTERR) == 0) + return (ENOENT); + + memset(ue, 0, sizeof(*ue)); + ue->error = td->td_kexterr.error; + ue->cat = td->td_kexterr.cat; + ue->src_line = td->td_kexterr.src_line; + ue->p1 = td->td_kexterr.p1; + ue->p2 = td->td_kexterr.p2; + if (td->td_kexterr.msg != NULL) + strlcpy(ue->msg, td->td_kexterr.msg, sizeof(ue->msg)); + return (0); +} + void exterr_copyout(struct thread *td) { @@ -2215,18 +2232,11 @@ uloc = (char *)td->td_exterr_ptr + __offsetof(struct uexterror, error); - if ((td->td_pflags2 & TDP2_EXTERR) == 0) { + error = exterr_to_ue(td, &ue); + if (error != 0) { ue.error = 0; sz = sizeof(ue.error); } else { - memset(&ue, 0, sizeof(ue)); - ue.error = td->td_kexterr.error; - ue.cat = td->td_kexterr.cat; - ue.src_line = td->td_kexterr.src_line; - ue.p1 = td->td_kexterr.p1; - ue.p2 = td->td_kexterr.p2; - if (td->td_kexterr.msg != NULL) - strlcpy(ue.msg, td->td_kexterr.msg, sizeof(ue.msg)); sz = sizeof(ue) - __offsetof(struct uexterror, error); } error = copyout(&ue.error, uloc, sz); diff --git a/sys/sys/_uexterror.h b/sys/sys/_uexterror.h new file mode 100644 --- /dev/null +++ b/sys/sys/_uexterror.h @@ -0,0 +1,27 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 The FreeBSD Foundation + * All rights reserved. + * + * This software were developed by Konstantin Belousov + * under sponsorship from the FreeBSD Foundation. + */ + +#ifndef _SYS__UEXTERROR_H_ +#define _SYS__UEXTERROR_H_ + +#include + +struct uexterror { + __uint32_t ver; + __uint32_t error; + __uint32_t cat; + __uint32_t src_line; + __uint64_t p1; + __uint64_t p2; + __uint64_t rsrv1[4]; + char msg[128]; +}; + +#endif diff --git a/sys/sys/exterr_cat.h b/sys/sys/exterr_cat.h --- a/sys/sys/exterr_cat.h +++ b/sys/sys/exterr_cat.h @@ -13,6 +13,8 @@ #define EXTERR_CAT_MMAP 1 #define EXTERR_CAT_FILEDESC 2 +#define EXTERR_KTRACE 3 /* To allow inclusion of this + file into kern_ktrace.c */ #endif diff --git a/sys/sys/exterrvar.h b/sys/sys/exterrvar.h --- a/sys/sys/exterrvar.h +++ b/sys/sys/exterrvar.h @@ -12,19 +12,8 @@ #define _SYS_EXTERRVAR_H_ #include +#include #include -#include - -struct uexterror { - uint32_t ver; - uint32_t error; - uint32_t cat; - uint32_t src_line; - uint64_t p1; - uint64_t p2; - uint64_t rsrv1[4]; - char msg[128]; -}; #define UEXTERROR_MAXLEN 256 @@ -57,13 +46,19 @@ _Td->td_kexterr.p1 = (uintptr_t)pp1; \ _Td->td_kexterr.p2 = (uintptr_t)pp2; \ _Td->td_kexterr.src_line = __LINE__; \ + ktrexterr(_Td); \ } \ } while (0) #define SET_ERROR0(eerror, mmsg) SET_ERROR2(eerror, mmsg, 0, 0) #define SET_ERROR1(eerror, mmsg, pp1) SET_ERROR2(eerror, mmsg, pp1, 0) +int exterr_to_ue(struct thread *td, struct uexterror *ue); +void ktrexterr(struct thread *td); + #else /* _KERNEL */ +#include + __BEGIN_DECLS int exterrctl(u_int op, u_int flags, void *ptr); __END_DECLS diff --git a/sys/sys/ktrace.h b/sys/sys/ktrace.h --- a/sys/sys/ktrace.h +++ b/sys/sys/ktrace.h @@ -36,6 +36,7 @@ #include #include #include +#include #include /* @@ -272,6 +273,14 @@ */ #define KTR_ENVS 17 +/* + * KTR_EXTERR - extended error reported + */ +#define KTR_EXTERR 18 +struct ktr_exterr { + struct uexterror ue; +}; + /* * KTR_DROP - If this bit is set in ktr_type, then at least one event * between the previous record and this record was dropped. @@ -306,6 +315,7 @@ #define KTRFAC_STRUCT_ARRAY (1< #endif -int fetchprocinfo(struct ktr_header *, u_int *); -u_int findabi(struct ktr_header *); -int fread_tail(void *, int, int); -void dumpheader(struct ktr_header *, u_int); -void dumptimeval(struct ktr_header_v0 *kth); -void dumptimespec(struct ktr_header *kth); -void ktrsyscall(struct ktr_syscall *, u_int); -void ktrsysret(struct ktr_sysret *, u_int); -void ktrnamei(char *, int); -void hexdump(char *, int, int); -void visdump(char *, int, int); -void ktrgenio(struct ktr_genio *, int); -void ktrpsig(struct ktr_psig *); -void ktrcsw(struct ktr_csw *); -void ktrcsw_old(struct ktr_csw_old *); -void ktruser(int, void *); -void ktrcaprights(cap_rights_t *); -void ktritimerval(struct itimerval *it); -void ktrsockaddr(struct sockaddr *); -void ktrsplice(struct splice *); -void ktrstat(struct stat *); -void ktrstruct(char *, size_t); -void ktrcapfail(struct ktr_cap_fail *); -void ktrfault(struct ktr_fault *); -void ktrfaultend(struct ktr_faultend *); -void ktrkevent(struct kevent *); -void ktrpollfd(struct pollfd *); -void ktrstructarray(struct ktr_struct_array *, size_t); -void ktrbitset(char *, struct bitset *, size_t); -void ktrsyscall_freebsd(struct ktr_syscall *ktr, register_t **resip, +static int fetchprocinfo(struct ktr_header *, u_int *); +static u_int findabi(struct ktr_header *); +static int fread_tail(void *, int, int); +static void dumpheader(struct ktr_header *, u_int); +static void dumptimeval(struct ktr_header_v0 *kth); +static void dumptimespec(struct ktr_header *kth); +static void ktrsyscall(struct ktr_syscall *, u_int); +static void ktrsysret(struct ktr_sysret *, u_int); +static void ktrnamei(char *, int); +static void hexdump(char *, int, int); +static void visdump(char *, int, int); +static void ktrgenio(struct ktr_genio *, int); +static void ktrpsig(struct ktr_psig *); +static void ktrcsw(struct ktr_csw *); +static void ktrcsw_old(struct ktr_csw_old *); +static void ktruser(int, void *); +static void ktrcaprights(cap_rights_t *); +static void ktritimerval(struct itimerval *it); +static void ktrsockaddr(struct sockaddr *); +static void ktrsplice(struct splice *); +static void ktrstat(struct stat *); +static void ktrstruct(char *, size_t); +static void ktrcapfail(struct ktr_cap_fail *); +static void ktrfault(struct ktr_fault *); +static void ktrfaultend(struct ktr_faultend *); +static void ktrkevent(struct kevent *); +static void ktrpollfd(struct pollfd *); +static void ktrstructarray(struct ktr_struct_array *, size_t); +static void ktrbitset(char *, struct bitset *, size_t); +static void ktrsyscall_freebsd(struct ktr_syscall *ktr, register_t **resip, int *resnarg, char *resc, u_int sv_flags); -void ktrexecve(char *, int); -void usage(void); +static void ktrexecve(char *, int); +static void ktrexterr(struct ktr_exterr *); +static void usage(void); #define TIMESTAMP_NONE 0x0 #define TIMESTAMP_ABSOLUTE 0x1 @@ -521,6 +522,8 @@ case KTR_ENVS: ktrexecve(m, ktrlen); break; + case KTR_EXTERR: + ktrexterr((struct ktr_exterr *)m); default: printf("\n"); break; @@ -531,7 +534,7 @@ return 0; } -int +static int fread_tail(void *buf, int size, int num) { int i; @@ -543,7 +546,7 @@ return (i); } -int +static int fetchprocinfo(struct ktr_header *kth, u_int *flags) { struct proc_info *pi; @@ -578,7 +581,7 @@ return (0); } -u_int +static u_int findabi(struct ktr_header *kth) { struct proc_info *pi; @@ -591,7 +594,7 @@ return (0); } -void +static void dumptimeval(struct ktr_header_v0 *kth) { static struct timeval prevtime, prevtime_e; @@ -625,7 +628,7 @@ } } -void +static void dumptimespec(struct ktr_header *kth) { static struct timespec prevtime, prevtime_e; @@ -659,7 +662,26 @@ } } -void +static const char * const hdr_names[] = { + [KTR_SYSCALL] = "CALL", + [KTR_SYSRET] = "RET ", + [KTR_NAMEI] = "NAMI", + [KTR_GENIO] = "GIO ", + [KTR_PSIG] = "PSIG", + [KTR_CSW] = "CSW ", + [KTR_USER] = "USER", + [KTR_STRUCT] = "STRU", + [KTR_STRUCT_ARRAY] = "STRU", + [KTR_SYSCTL] = "SCTL", + [KTR_CAPFAIL] = "CAP ", + [KTR_FAULT] = "PFLT", + [KTR_FAULTEND] = "PRET", + [KTR_ARGS] = "ARGS", + [KTR_ENVS] = "ENVS", + [KTR_EXTERR] = "EERR", +}; + +static void dumpheader(struct ktr_header *kth, u_int sv_flags) { static char unknown[64]; @@ -667,53 +689,12 @@ const char *arch; const char *type; - switch (kth->ktr_type) { - case KTR_SYSCALL: - type = "CALL"; - break; - case KTR_SYSRET: - type = "RET "; - break; - case KTR_NAMEI: - type = "NAMI"; - break; - case KTR_GENIO: - type = "GIO "; - break; - case KTR_PSIG: - type = "PSIG"; - break; - case KTR_CSW: - type = "CSW "; - break; - case KTR_USER: - type = "USER"; - break; - case KTR_STRUCT: - case KTR_STRUCT_ARRAY: - type = "STRU"; - break; - case KTR_SYSCTL: - type = "SCTL"; - break; - case KTR_CAPFAIL: - type = "CAP "; - break; - case KTR_FAULT: - type = "PFLT"; - break; - case KTR_FAULTEND: - type = "PRET"; - break; - case KTR_ARGS: - type = "ARGS"; - break; - case KTR_ENVS: - type = "ENVS"; - break; - default: - sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type); + if (kth->ktr_type < 0 || (size_t)kth->ktr_type >= nitems(hdr_names)) { + snprintf(unknown, sizeof(unknown), "UNKNOWN(%d)", + kth->ktr_type); type = unknown; + } else { + type = hdr_names[kth->ktr_type]; } /* @@ -826,7 +807,7 @@ printf("SIG %d", signo); } -void +static void ktrsyscall(struct ktr_syscall *ktr, u_int sv_flags) { int narg = ktr->ktr_narg; @@ -862,7 +843,7 @@ putchar('\n'); } -void +static void ktrsyscall_freebsd(struct ktr_syscall *ktr, register_t **resip, int *resnarg, char *resc, u_int sv_flags) { @@ -1619,7 +1600,7 @@ *resnarg = narg; } -void +static void ktrsysret(struct ktr_sysret *ktr, u_int sv_flags) { register_t ret = ktr->ktr_retval; @@ -1652,13 +1633,13 @@ putchar('\n'); } -void +static void ktrnamei(char *cp, int len) { printf("\"%.*s\"\n", len, cp); } -void +static void ktrexecve(char *m, int len) { int i = 0; @@ -1673,7 +1654,7 @@ printf("\n"); } -void +static void hexdump(char *p, int len, int screenwidth) { int n, i; @@ -1719,7 +1700,7 @@ printf("\n"); } -void +static void visdump(char *dp, int datalen, int screenwidth) { int col = 0; @@ -1765,7 +1746,7 @@ printf("\"\n"); } -void +static void ktrgenio(struct ktr_genio *ktr, int len) { int datalen = len - sizeof (struct ktr_genio); @@ -1803,7 +1784,7 @@ visdump(dp, datalen, screenwidth); } -void +static void ktrpsig(struct ktr_psig *psig) { const char *str; @@ -1824,21 +1805,21 @@ putchar('\n'); } -void +static void ktrcsw_old(struct ktr_csw_old *cs) { printf("%s %s\n", cs->out ? "stop" : "resume", cs->user ? "user" : "kernel"); } -void +static void ktrcsw(struct ktr_csw *cs) { printf("%s %s \"%s\"\n", cs->out ? "stop" : "resume", cs->user ? "user" : "kernel", cs->wmesg); } -void +static void ktruser(int len, void *p) { unsigned char *cp; @@ -1858,7 +1839,7 @@ printf("\n"); } -void +static void ktrcaprights(cap_rights_t *rightsp) { @@ -1874,7 +1855,7 @@ printf("{%ld, %ld}", (long)tv->tv_sec, tv->tv_usec); } -void +static void ktritimerval(struct itimerval *it) { @@ -1885,7 +1866,7 @@ printf(" }\n"); } -void +static void ktrsockaddr(struct sockaddr *sa) { /* @@ -1960,7 +1941,7 @@ printf(" }\n"); } -void +static void ktrsplice(struct splice *sp) { printf("struct splice { fd=%d, max=%#jx, idle=%jd.%06jd }\n", @@ -1968,7 +1949,7 @@ (intmax_t)sp->sp_idle.tv_usec); } -void +static void ktrstat(struct stat *statp) { char mode[12], timestr[PATH_MAX + 4]; @@ -2073,7 +2054,7 @@ printf(" }\n"); } -void +static void ktrbitset(char *name, struct bitset *set, size_t setlen) { int i, maxi, c = 0; @@ -2097,7 +2078,7 @@ printf(" ]\n"); } -void +static void ktrstruct(char *buf, size_t buflen) { char *name, *data; @@ -2174,7 +2155,7 @@ printf("invalid record\n"); } -void +static void ktrcapfail(struct ktr_cap_fail *ktr) { union ktr_cap_data *kcd = &ktr->cap_data; @@ -2249,7 +2230,7 @@ printf("\n"); } -void +static void ktrfault(struct ktr_fault *ktr) { @@ -2258,7 +2239,7 @@ printf("\n"); } -void +static void ktrfaultend(struct ktr_faultend *ktr) { const char *str; @@ -2271,7 +2252,7 @@ printf("\n"); } -void +static void ktrkevent(struct kevent *kev) { @@ -2302,7 +2283,7 @@ printf(", data=%#jx, udata=%p }", (uintmax_t)kev->data, kev->udata); } -void +static void ktrpollfd(struct pollfd *pfd) { @@ -2314,7 +2295,7 @@ printf("}"); } -void +static void ktrstructarray(struct ktr_struct_array *ksa, size_t buflen) { struct kevent kev; @@ -2420,7 +2401,18 @@ return; } -void +static void +ktrexterr(struct ktr_exterr *ke) +{ + struct uexterror *ue; + + ue = &ke->ue; + printf("{ errno %d category %u (src line %u) p1 %#jx p2 %#jx %s }", + ue->error, ue->cat, ue->src_line, + (uintmax_t)ue->p1, (uintmax_t)ue->p2, ue->msg); +} + +static void usage(void) { fprintf(stderr, "usage: kdump [-dEnlHRrSsTA] [-f trfile] " diff --git a/usr.bin/ktrace/ktrace.h b/usr.bin/ktrace/ktrace.h --- a/usr.bin/ktrace/ktrace.h +++ b/usr.bin/ktrace/ktrace.h @@ -32,7 +32,7 @@ #define DEF_POINTS (KTRFAC_SYSCALL | KTRFAC_SYSRET | KTRFAC_NAMEI | \ KTRFAC_GENIO | KTRFAC_PSIG | KTRFAC_USER | \ KTRFAC_STRUCT | KTRFAC_SYSCTL | KTRFAC_STRUCT_ARRAY | \ - KTRFAC_ARGS | KTRFAC_ENVS) + KTRFAC_ARGS | KTRFAC_ENVS | KTRFAC_EXTERR) #define PROC_ABI_POINTS (KTRFAC_PROCCTOR | KTRFAC_PROCDTOR) diff --git a/usr.bin/ktrace/ktrace.1 b/usr.bin/ktrace/ktrace.1 --- a/usr.bin/ktrace/ktrace.1 +++ b/usr.bin/ktrace/ktrace.1 @@ -150,9 +150,13 @@ trace .Xr execve 2 environment variables +.It Cm x +trace +.Xr exterr 2 +extended errors reports from kernel .It Cm + trace the default set of trace points - -.Cm a, c , e, i , n , s , t , u , y +.Cm a, c , e, i , n , s , t , u , x, y .El .It Ar command Execute diff --git a/usr.bin/ktrace/subr.c b/usr.bin/ktrace/subr.c --- a/usr.bin/ktrace/subr.c +++ b/usr.bin/ktrace/subr.c @@ -87,6 +87,8 @@ case 'e': facs |= KTRFAC_ENVS; break; + case 'x': + facs |= KTRFAC_EXTERR; case '+': facs |= DEF_POINTS; break;