mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
Add some minimal handling of category sources other than static kernel sources. We don't actually look up dynamic sources yet (that would require extended trace records to add the file names to the trace file since we can't assume the trace file is running on a kernel with the same numbers.) Make the decision to append a "src/" prefix to each file name dependent on the category source. Reviewed by: kib Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D58412
2500 lines
55 KiB
C
2500 lines
55 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (c) 1988, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#define _WANT_KERNEL_ERRNO
|
|
#ifdef __LP64__
|
|
#define _WANT_KEVENT32
|
|
#endif
|
|
#define _WANT_FREEBSD11_KEVENT
|
|
#define _WANT_FREEBSD_BITSET
|
|
#include <sys/param.h>
|
|
#include <sys/capsicum.h>
|
|
#include <sys/_bitset.h>
|
|
#include <sys/bitset.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/exterr_cat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/event.h>
|
|
#include <sys/ktrace.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/inotify.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysent.h>
|
|
#include <sys/thr.h>
|
|
#include <sys/umtx.h>
|
|
#include <sys/un.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/wait.h>
|
|
#ifdef WITH_CASPER
|
|
#include <sys/nv.h>
|
|
#endif
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <netlink/netlink.h>
|
|
#include <ctype.h>
|
|
#include <capsicum_helpers.h>
|
|
#include <err.h>
|
|
#include <grp.h>
|
|
#include <inttypes.h>
|
|
#include <locale.h>
|
|
#include <netdb.h>
|
|
#include <nl_types.h>
|
|
#include <pwd.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysdecode.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <vis.h>
|
|
#include "ktrace.h"
|
|
#include "kdump.h"
|
|
|
|
#ifdef WITH_CASPER
|
|
#include <libcasper.h>
|
|
|
|
#include <casper/cap_grp.h>
|
|
#include <casper/cap_pwd.h>
|
|
#endif
|
|
|
|
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 ktrinotify(struct inotify_event *);
|
|
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 ktrthrparam(struct thr_param *);
|
|
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);
|
|
static void ktrexecve(char *, int);
|
|
static void ktrexterr(struct ktr_exterr *);
|
|
static void usage(void);
|
|
|
|
#define TIMESTAMP_NONE 0x0
|
|
#define TIMESTAMP_ABSOLUTE 0x1
|
|
#define TIMESTAMP_ELAPSED 0x2
|
|
#define TIMESTAMP_RELATIVE 0x4
|
|
|
|
bool decimal, fancy = true, resolv;
|
|
static bool abiflag, suppressdata, syscallno, tail, threads, cpuflag;
|
|
static int timestamp, maxdata;
|
|
static const char *tracefile = DEF_TRACEFILE;
|
|
static struct ktr_header ktr_header;
|
|
static short version;
|
|
|
|
#define TIME_FORMAT "%b %e %T %Y"
|
|
#define eqs(s1, s2) (strcmp((s1), (s2)) == 0)
|
|
|
|
struct proc_info
|
|
{
|
|
TAILQ_ENTRY(proc_info) info;
|
|
u_int sv_flags;
|
|
pid_t pid;
|
|
};
|
|
|
|
static TAILQ_HEAD(trace_procs, proc_info) trace_procs;
|
|
|
|
#ifdef WITH_CASPER
|
|
static cap_channel_t *cappwd, *capgrp;
|
|
|
|
static int
|
|
cappwdgrp_setup(cap_channel_t **cappwdp, cap_channel_t **capgrpp)
|
|
{
|
|
cap_channel_t *capcas, *cappwdloc, *capgrploc;
|
|
const char *cmds[1], *fields[1];
|
|
|
|
capcas = cap_init();
|
|
if (capcas == NULL) {
|
|
err(1, "unable to create casper process");
|
|
exit(1);
|
|
}
|
|
cappwdloc = cap_service_open(capcas, "system.pwd");
|
|
capgrploc = cap_service_open(capcas, "system.grp");
|
|
/* Casper capability no longer needed. */
|
|
cap_close(capcas);
|
|
if (cappwdloc == NULL || capgrploc == NULL) {
|
|
if (cappwdloc == NULL)
|
|
warn("unable to open system.pwd service");
|
|
if (capgrploc == NULL)
|
|
warn("unable to open system.grp service");
|
|
exit(1);
|
|
}
|
|
/* Limit system.pwd to only getpwuid() function and pw_name field. */
|
|
cmds[0] = "getpwuid";
|
|
if (cap_pwd_limit_cmds(cappwdloc, cmds, 1) < 0)
|
|
err(1, "unable to limit system.pwd service");
|
|
fields[0] = "pw_name";
|
|
if (cap_pwd_limit_fields(cappwdloc, fields, 1) < 0)
|
|
err(1, "unable to limit system.pwd service");
|
|
/* Limit system.grp to only getgrgid() function and gr_name field. */
|
|
cmds[0] = "getgrgid";
|
|
if (cap_grp_limit_cmds(capgrploc, cmds, 1) < 0)
|
|
err(1, "unable to limit system.grp service");
|
|
fields[0] = "gr_name";
|
|
if (cap_grp_limit_fields(capgrploc, fields, 1) < 0)
|
|
err(1, "unable to limit system.grp service");
|
|
|
|
*cappwdp = cappwdloc;
|
|
*capgrpp = capgrploc;
|
|
return (0);
|
|
}
|
|
#endif /* WITH_CASPER */
|
|
|
|
void
|
|
print_integer_arg(const char *(*decoder)(int), int value)
|
|
{
|
|
const char *str;
|
|
|
|
str = decoder(value);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else {
|
|
if (decimal)
|
|
printf("<invalid=%d>", value);
|
|
else
|
|
printf("<invalid=%#x>", value);
|
|
}
|
|
}
|
|
|
|
/* Like print_integer_arg but unknown values are treated as valid. */
|
|
void
|
|
print_integer_arg_valid(const char *(*decoder)(int), int value)
|
|
{
|
|
const char *str;
|
|
|
|
str = decoder(value);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else {
|
|
if (decimal)
|
|
printf("%d", value);
|
|
else
|
|
printf("%#x", value);
|
|
}
|
|
}
|
|
|
|
bool
|
|
print_mask_arg_part(bool (*decoder)(FILE *, int, int *), int value, int *rem)
|
|
{
|
|
|
|
printf("%#x<", value);
|
|
return (decoder(stdout, value, rem));
|
|
}
|
|
|
|
void
|
|
print_mask_arg(bool (*decoder)(FILE *, int, int *), int value)
|
|
{
|
|
bool invalid;
|
|
int rem;
|
|
|
|
invalid = !print_mask_arg_part(decoder, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%u", rem);
|
|
}
|
|
|
|
void
|
|
print_mask_arg0(bool (*decoder)(FILE *, int, int *), int value)
|
|
{
|
|
bool invalid;
|
|
int rem;
|
|
|
|
if (value == 0) {
|
|
printf("0");
|
|
return;
|
|
}
|
|
printf("%#x<", value);
|
|
invalid = !decoder(stdout, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%u", rem);
|
|
}
|
|
|
|
static void
|
|
decode_fileflags(fflags_t value)
|
|
{
|
|
bool invalid;
|
|
fflags_t rem;
|
|
|
|
if (value == 0) {
|
|
printf("0");
|
|
return;
|
|
}
|
|
printf("%#x<", value);
|
|
invalid = !sysdecode_fileflags(stdout, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%u", rem);
|
|
}
|
|
|
|
void
|
|
decode_filemode(int value)
|
|
{
|
|
bool invalid;
|
|
int rem;
|
|
|
|
if (value == 0) {
|
|
printf("0");
|
|
return;
|
|
}
|
|
printf("%#o<", value);
|
|
invalid = !sysdecode_filemode(stdout, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%u", rem);
|
|
}
|
|
|
|
void
|
|
print_mask_arg32(bool (*decoder)(FILE *, uint32_t, uint32_t *), uint32_t value)
|
|
{
|
|
bool invalid;
|
|
uint32_t rem;
|
|
|
|
printf("%#x<", value);
|
|
invalid = !decoder(stdout, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%u", rem);
|
|
}
|
|
|
|
void
|
|
print_mask_argul(bool (*decoder)(FILE *, u_long, u_long *), u_long value)
|
|
{
|
|
bool invalid;
|
|
u_long rem;
|
|
|
|
if (value == 0) {
|
|
printf("0");
|
|
return;
|
|
}
|
|
printf("%#lx<", value);
|
|
invalid = !decoder(stdout, value, &rem);
|
|
printf(">");
|
|
if (invalid)
|
|
printf("<invalid>%lu", rem);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int ch, ktrlen, size;
|
|
void *m;
|
|
int trpoints = ALL_POINTS;
|
|
int drop_logged;
|
|
pid_t pid = 0;
|
|
u_int sv_flags;
|
|
|
|
setlocale(LC_CTYPE, "");
|
|
|
|
timestamp = TIMESTAMP_NONE;
|
|
|
|
while ((ch = getopt(argc,argv,"f:cdElm:np:AHRrSsTt:")) != -1)
|
|
switch (ch) {
|
|
case 'A':
|
|
abiflag = true;
|
|
break;
|
|
case 'f':
|
|
tracefile = optarg;
|
|
break;
|
|
case 'c':
|
|
cpuflag = true;
|
|
break;
|
|
case 'd':
|
|
decimal = true;
|
|
break;
|
|
case 'l':
|
|
tail = true;
|
|
break;
|
|
case 'm':
|
|
maxdata = atoi(optarg);
|
|
break;
|
|
case 'n':
|
|
fancy = false;
|
|
break;
|
|
case 'p':
|
|
pid = atoi(optarg);
|
|
break;
|
|
case 'r':
|
|
resolv = true;
|
|
break;
|
|
case 'S':
|
|
syscallno = true;
|
|
break;
|
|
case 's':
|
|
suppressdata = true;
|
|
break;
|
|
case 'E':
|
|
timestamp |= TIMESTAMP_ELAPSED;
|
|
break;
|
|
case 'H':
|
|
threads = true;
|
|
break;
|
|
case 'R':
|
|
timestamp |= TIMESTAMP_RELATIVE;
|
|
break;
|
|
case 'T':
|
|
timestamp |= TIMESTAMP_ABSOLUTE;
|
|
break;
|
|
case 't':
|
|
trpoints = getpoints(optarg);
|
|
if (trpoints < 0)
|
|
errx(1, "unknown trace point in %s", optarg);
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
|
|
if (argc > optind)
|
|
usage();
|
|
|
|
m = malloc(size = 1025);
|
|
if (m == NULL)
|
|
errx(1, "%s", strerror(ENOMEM));
|
|
if (strcmp(tracefile, "-") != 0)
|
|
if (!freopen(tracefile, "r", stdin))
|
|
err(1, "%s", tracefile);
|
|
|
|
caph_cache_catpages();
|
|
caph_cache_tzdata();
|
|
|
|
#ifdef WITH_CASPER
|
|
if (resolv) {
|
|
if (cappwdgrp_setup(&cappwd, &capgrp) < 0) {
|
|
cappwd = NULL;
|
|
capgrp = NULL;
|
|
}
|
|
}
|
|
if (!resolv || (cappwd != NULL && capgrp != NULL)) {
|
|
if (caph_enter() < 0)
|
|
err(1, "unable to enter capability mode");
|
|
}
|
|
#else
|
|
if (!resolv) {
|
|
if (caph_enter() < 0)
|
|
err(1, "unable to enter capability mode");
|
|
}
|
|
#endif
|
|
if (caph_limit_stdio() == -1)
|
|
err(1, "unable to limit stdio");
|
|
|
|
TAILQ_INIT(&trace_procs);
|
|
drop_logged = 0;
|
|
while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
|
|
if (ktr_header.ktr_type & KTR_VERSIONED) {
|
|
ktr_header.ktr_type &= ~KTR_VERSIONED;
|
|
version = ktr_header.ktr_version;
|
|
} else
|
|
version = KTR_VERSION0;
|
|
if (ktr_header.ktr_type & KTR_DROP) {
|
|
ktr_header.ktr_type &= ~KTR_DROP;
|
|
if (!drop_logged && threads) {
|
|
printf(
|
|
"%6d %6d %-8.*s Events dropped.\n",
|
|
ktr_header.ktr_pid,
|
|
ktr_header.ktr_tid > 0 ?
|
|
(lwpid_t)ktr_header.ktr_tid : 0,
|
|
MAXCOMLEN, ktr_header.ktr_comm);
|
|
drop_logged = 1;
|
|
} else if (!drop_logged) {
|
|
printf("%6d %-8.*s Events dropped.\n",
|
|
ktr_header.ktr_pid, MAXCOMLEN,
|
|
ktr_header.ktr_comm);
|
|
drop_logged = 1;
|
|
}
|
|
}
|
|
if ((ktrlen = ktr_header.ktr_len) < 0)
|
|
errx(1, "bogus length 0x%x", ktrlen);
|
|
if (ktrlen > size) {
|
|
m = realloc(m, ktrlen+1);
|
|
if (m == NULL)
|
|
errx(1, "%s", strerror(ENOMEM));
|
|
size = ktrlen;
|
|
}
|
|
if (version == KTR_VERSION0 &&
|
|
fseek(stdin, KTR_OFFSET_V0, SEEK_CUR) < 0)
|
|
errx(1, "%s", strerror(errno));
|
|
if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
|
|
errx(1, "data too short");
|
|
if (fetchprocinfo(&ktr_header, (u_int *)m) != 0)
|
|
continue;
|
|
if (pid && ktr_header.ktr_pid != pid &&
|
|
ktr_header.ktr_tid != pid)
|
|
continue;
|
|
if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
|
|
continue;
|
|
sv_flags = findabi(&ktr_header);
|
|
dumpheader(&ktr_header, sv_flags);
|
|
drop_logged = 0;
|
|
switch (ktr_header.ktr_type) {
|
|
case KTR_SYSCALL:
|
|
ktrsyscall((struct ktr_syscall *)m, sv_flags);
|
|
break;
|
|
case KTR_SYSRET:
|
|
ktrsysret((struct ktr_sysret *)m, sv_flags);
|
|
break;
|
|
case KTR_NAMEI:
|
|
case KTR_SYSCTL:
|
|
ktrnamei(m, ktrlen);
|
|
break;
|
|
case KTR_GENIO:
|
|
ktrgenio((struct ktr_genio *)m, ktrlen);
|
|
break;
|
|
case KTR_PSIG:
|
|
ktrpsig((struct ktr_psig *)m);
|
|
break;
|
|
case KTR_CSW:
|
|
if (ktrlen == sizeof(struct ktr_csw_old))
|
|
ktrcsw_old((struct ktr_csw_old *)m);
|
|
else
|
|
ktrcsw((struct ktr_csw *)m);
|
|
break;
|
|
case KTR_USER:
|
|
ktruser(ktrlen, m);
|
|
break;
|
|
case KTR_STRUCT:
|
|
ktrstruct(m, ktrlen);
|
|
break;
|
|
case KTR_CAPFAIL:
|
|
ktrcapfail((struct ktr_cap_fail *)m);
|
|
break;
|
|
case KTR_FAULT:
|
|
ktrfault((struct ktr_fault *)m);
|
|
break;
|
|
case KTR_FAULTEND:
|
|
ktrfaultend((struct ktr_faultend *)m);
|
|
break;
|
|
case KTR_STRUCT_ARRAY:
|
|
ktrstructarray((struct ktr_struct_array *)m, ktrlen);
|
|
break;
|
|
case KTR_ARGS:
|
|
case KTR_ENVS:
|
|
ktrexecve(m, ktrlen);
|
|
break;
|
|
case KTR_EXTERR:
|
|
ktrexterr((struct ktr_exterr *)m);
|
|
break;
|
|
default:
|
|
printf("\n");
|
|
break;
|
|
}
|
|
if (tail)
|
|
fflush(stdout);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fread_tail(void *buf, int size, int num)
|
|
{
|
|
int i;
|
|
|
|
while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
|
|
sleep(1);
|
|
clearerr(stdin);
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
static int
|
|
fetchprocinfo(struct ktr_header *kth, u_int *flags)
|
|
{
|
|
struct proc_info *pi;
|
|
|
|
switch (kth->ktr_type) {
|
|
case KTR_PROCCTOR:
|
|
TAILQ_FOREACH(pi, &trace_procs, info) {
|
|
if (pi->pid == kth->ktr_pid) {
|
|
TAILQ_REMOVE(&trace_procs, pi, info);
|
|
break;
|
|
}
|
|
}
|
|
pi = malloc(sizeof(struct proc_info));
|
|
if (pi == NULL)
|
|
errx(1, "%s", strerror(ENOMEM));
|
|
pi->sv_flags = *flags;
|
|
pi->pid = kth->ktr_pid;
|
|
TAILQ_INSERT_TAIL(&trace_procs, pi, info);
|
|
return (1);
|
|
|
|
case KTR_PROCDTOR:
|
|
TAILQ_FOREACH(pi, &trace_procs, info) {
|
|
if (pi->pid == kth->ktr_pid) {
|
|
TAILQ_REMOVE(&trace_procs, pi, info);
|
|
free(pi);
|
|
break;
|
|
}
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static u_int
|
|
findabi(struct ktr_header *kth)
|
|
{
|
|
struct proc_info *pi;
|
|
|
|
TAILQ_FOREACH(pi, &trace_procs, info) {
|
|
if (pi->pid == kth->ktr_pid) {
|
|
return (pi->sv_flags);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
dumptimeval(struct ktr_header_v0 *kth)
|
|
{
|
|
static struct timeval prevtime, prevtime_e;
|
|
struct timeval temp;
|
|
const char *sign;
|
|
|
|
if (timestamp & TIMESTAMP_ABSOLUTE) {
|
|
printf("%jd.%06ld ", (intmax_t)kth->ktr_time.tv_sec,
|
|
kth->ktr_time.tv_usec);
|
|
}
|
|
if (timestamp & TIMESTAMP_ELAPSED) {
|
|
if (prevtime_e.tv_sec == 0)
|
|
prevtime_e = kth->ktr_time;
|
|
timersub(&kth->ktr_time, &prevtime_e, &temp);
|
|
printf("%jd.%06ld ", (intmax_t)temp.tv_sec,
|
|
temp.tv_usec);
|
|
}
|
|
if (timestamp & TIMESTAMP_RELATIVE) {
|
|
if (prevtime.tv_sec == 0)
|
|
prevtime = kth->ktr_time;
|
|
if (timercmp(&kth->ktr_time, &prevtime, <)) {
|
|
timersub(&prevtime, &kth->ktr_time, &temp);
|
|
sign = "-";
|
|
} else {
|
|
timersub(&kth->ktr_time, &prevtime, &temp);
|
|
sign = "";
|
|
}
|
|
prevtime = kth->ktr_time;
|
|
printf("%s%jd.%06ld ", sign, (intmax_t)temp.tv_sec,
|
|
temp.tv_usec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dumptimespec(struct ktr_header *kth)
|
|
{
|
|
static struct timespec prevtime, prevtime_e;
|
|
struct timespec temp;
|
|
const char *sign;
|
|
|
|
if (timestamp & TIMESTAMP_ABSOLUTE) {
|
|
printf("%jd.%09ld ", (intmax_t)kth->ktr_time.tv_sec,
|
|
kth->ktr_time.tv_nsec);
|
|
}
|
|
if (timestamp & TIMESTAMP_ELAPSED) {
|
|
if (prevtime_e.tv_sec == 0)
|
|
prevtime_e = kth->ktr_time;
|
|
timespecsub(&kth->ktr_time, &prevtime_e, &temp);
|
|
printf("%jd.%09ld ", (intmax_t)temp.tv_sec,
|
|
temp.tv_nsec);
|
|
}
|
|
if (timestamp & TIMESTAMP_RELATIVE) {
|
|
if (prevtime.tv_sec == 0)
|
|
prevtime = kth->ktr_time;
|
|
if (timespeccmp(&kth->ktr_time, &prevtime, <)) {
|
|
timespecsub(&prevtime, &kth->ktr_time, &temp);
|
|
sign = "-";
|
|
} else {
|
|
timespecsub(&kth->ktr_time, &prevtime, &temp);
|
|
sign = "";
|
|
}
|
|
prevtime = kth->ktr_time;
|
|
printf("%s%jd.%09ld ", sign, (intmax_t)temp.tv_sec,
|
|
temp.tv_nsec);
|
|
}
|
|
}
|
|
|
|
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];
|
|
const char *abi;
|
|
const char *arch;
|
|
const char *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];
|
|
}
|
|
|
|
/*
|
|
* The ktr_tid field was previously the ktr_buffer field, which held
|
|
* the kernel pointer value for the buffer associated with data
|
|
* following the record header. It now holds a threadid, but only
|
|
* for trace files after the change. Older trace files still contain
|
|
* kernel pointers. Detect this and suppress the results by printing
|
|
* negative tid's as 0.
|
|
*/
|
|
if (threads)
|
|
printf("%6d %6d %-8.*s ", kth->ktr_pid,
|
|
kth->ktr_tid > 0 ? (lwpid_t)kth->ktr_tid : 0,
|
|
MAXCOMLEN, kth->ktr_comm);
|
|
else
|
|
printf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN, kth->ktr_comm);
|
|
if (timestamp) {
|
|
if (version == KTR_VERSION0)
|
|
dumptimeval((struct ktr_header_v0 *)kth);
|
|
else
|
|
dumptimespec(kth);
|
|
}
|
|
if (cpuflag && version > KTR_VERSION0)
|
|
printf("%3d ", kth->ktr_cpu);
|
|
printf("%s ", type);
|
|
if (abiflag != 0) {
|
|
switch (sv_flags & SV_ABI_MASK) {
|
|
case SV_ABI_LINUX:
|
|
abi = "L";
|
|
break;
|
|
case SV_ABI_FREEBSD:
|
|
abi = "F";
|
|
break;
|
|
default:
|
|
abi = "U";
|
|
break;
|
|
}
|
|
|
|
if ((sv_flags & SV_LP64) != 0)
|
|
arch = "64";
|
|
else if ((sv_flags & SV_ILP32) != 0)
|
|
arch = "32";
|
|
else
|
|
arch = "00";
|
|
|
|
printf("%s%s ", abi, arch);
|
|
}
|
|
}
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
static void
|
|
ioctlname(unsigned long val)
|
|
{
|
|
const char *str;
|
|
|
|
str = sysdecode_ioctlname(val);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else if (decimal)
|
|
printf("%lu", val);
|
|
else
|
|
printf("%#lx", val);
|
|
}
|
|
|
|
static enum sysdecode_abi
|
|
syscallabi(u_int sv_flags)
|
|
{
|
|
|
|
if (sv_flags == 0)
|
|
return (SYSDECODE_ABI_FREEBSD);
|
|
switch (sv_flags & SV_ABI_MASK) {
|
|
case SV_ABI_FREEBSD:
|
|
return (SYSDECODE_ABI_FREEBSD);
|
|
case SV_ABI_LINUX:
|
|
#ifdef __LP64__
|
|
if (sv_flags & SV_ILP32)
|
|
return (SYSDECODE_ABI_LINUX32);
|
|
#endif
|
|
return (SYSDECODE_ABI_LINUX);
|
|
default:
|
|
return (SYSDECODE_ABI_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
static void
|
|
syscallname(u_int code, u_int sv_flags)
|
|
{
|
|
const char *name;
|
|
|
|
name = sysdecode_syscallname(syscallabi(sv_flags), code);
|
|
if (name == NULL)
|
|
printf("[%d]", code);
|
|
else {
|
|
printf("%s", name);
|
|
if (syscallno)
|
|
printf("[%d]", code);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_signal(int signo)
|
|
{
|
|
const char *signame;
|
|
|
|
signame = sysdecode_signal(signo);
|
|
if (signame != NULL)
|
|
printf("%s", signame);
|
|
else
|
|
printf("SIG %d", signo);
|
|
}
|
|
|
|
static void
|
|
ktrsyscall(struct ktr_syscall *ktr, u_int sv_flags)
|
|
{
|
|
int narg = ktr->ktr_narg;
|
|
register_t *ip;
|
|
|
|
syscallname(ktr->ktr_code, sv_flags);
|
|
ip = &ktr->ktr_args[0];
|
|
if (narg) {
|
|
char c = '(';
|
|
if (fancy) {
|
|
switch (sv_flags & SV_ABI_MASK) {
|
|
case SV_ABI_FREEBSD:
|
|
ktrsyscall_freebsd(ktr, &ip, &narg, &c,
|
|
sv_flags);
|
|
break;
|
|
#ifdef SYSDECODE_HAVE_LINUX
|
|
case SV_ABI_LINUX:
|
|
#ifdef __amd64__
|
|
if (sv_flags & SV_ILP32)
|
|
ktrsyscall_linux32(ktr, &ip,
|
|
&narg, &c);
|
|
else
|
|
#endif
|
|
ktrsyscall_linux(ktr, &ip, &narg, &c);
|
|
break;
|
|
#endif /* SYSDECODE_HAVE_LINUX */
|
|
}
|
|
}
|
|
while (narg > 0)
|
|
print_number(ip, narg, c);
|
|
putchar(')');
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
static void
|
|
ktrsyscall_freebsd(struct ktr_syscall *ktr, register_t **resip,
|
|
int *resnarg, char *resc, u_int sv_flags)
|
|
{
|
|
int narg = ktr->ktr_narg;
|
|
register_t *ip, *first;
|
|
intmax_t arg;
|
|
int quad_align, quad_slots;
|
|
|
|
ip = first = &ktr->ktr_args[0];
|
|
char c = *resc;
|
|
|
|
quad_align = 0;
|
|
if (sv_flags & SV_ILP32) {
|
|
#ifdef __powerpc__
|
|
quad_align = 1;
|
|
#endif
|
|
quad_slots = 2;
|
|
} else
|
|
quad_slots = 1;
|
|
switch (ktr->ktr_code) {
|
|
case SYS_bindat:
|
|
case SYS_chflagsat:
|
|
case SYS_connectat:
|
|
case SYS_faccessat:
|
|
case SYS_fchmodat:
|
|
case SYS_fchownat:
|
|
case SYS_fstatat:
|
|
case SYS_futimesat:
|
|
case SYS_linkat:
|
|
case SYS_mkdirat:
|
|
case SYS_mkfifoat:
|
|
case SYS_mknodat:
|
|
case SYS_openat:
|
|
case SYS_readlinkat:
|
|
case SYS_renameat:
|
|
case SYS_unlinkat:
|
|
case SYS_utimensat:
|
|
putchar('(');
|
|
print_integer_arg_valid(sysdecode_atfd, *ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
}
|
|
switch (ktr->ktr_code) {
|
|
case SYS_ioctl: {
|
|
print_number(ip, narg, c);
|
|
putchar(c);
|
|
ioctlname(*ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
}
|
|
case SYS_ptrace:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_ptrace_request, *ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_access:
|
|
case SYS_eaccess:
|
|
case SYS_faccessat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_access_mode, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_close_range:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_close_range_flags,
|
|
*ip);
|
|
ip += 3;
|
|
narg -= 3;
|
|
break;
|
|
case SYS_open:
|
|
case SYS_openat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_open_flags, ip[0]);
|
|
if ((ip[0] & O_CREAT) == O_CREAT) {
|
|
putchar(',');
|
|
decode_filemode(ip[1]);
|
|
}
|
|
ip += 2;
|
|
narg -= 2;
|
|
break;
|
|
case SYS_wait4:
|
|
*ip = (pid_t)*ip;
|
|
print_decimal_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_wait4_options, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_wait6:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_idtype, *ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
print_decimal_number64(first, ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_wait6_options, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_chmod:
|
|
case SYS_fchmod:
|
|
case SYS_lchmod:
|
|
case SYS_fchmodat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
decode_filemode(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_mknodat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
decode_filemode(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_getfsstat:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_getfsstat_mode, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_mount:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_mount_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_unmount:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_mount_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_recvmsg:
|
|
case SYS_sendmsg:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_msg_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_recvfrom:
|
|
case SYS_sendto:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_msg_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_chflags:
|
|
case SYS_chflagsat:
|
|
case SYS_fchflags:
|
|
case SYS_lchflags:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
decode_fileflags(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_kill:
|
|
*ip = (pid_t)*ip;
|
|
print_decimal_number(ip, narg, c);
|
|
putchar(',');
|
|
print_signal(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_reboot:
|
|
putchar('(');
|
|
print_mask_arg(sysdecode_reboot_howto, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_umask:
|
|
putchar('(');
|
|
decode_filemode(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_msync:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_msync_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
#ifdef SYS_freebsd6_mmap
|
|
case SYS_freebsd6_mmap:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_mmap_prot, *ip);
|
|
putchar(',');
|
|
ip++;
|
|
narg--;
|
|
print_mask_arg(sysdecode_mmap_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
#endif
|
|
case SYS_mmap:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_mmap_prot, *ip);
|
|
putchar(',');
|
|
ip++;
|
|
narg--;
|
|
print_mask_arg(sysdecode_mmap_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_mprotect:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_mmap_prot, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_madvise:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_madvice, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_pathconf:
|
|
case SYS_lpathconf:
|
|
case SYS_fpathconf:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_pathconf_name, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_getpriority:
|
|
case SYS_setpriority:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_prio_which, *ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_fcntl:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_fcntl_cmd, ip[0]);
|
|
if (sysdecode_fcntl_arg_p(ip[0])) {
|
|
putchar(',');
|
|
if (ip[0] == F_SETFL)
|
|
print_mask_arg(
|
|
sysdecode_fcntl_fileflags,
|
|
ip[1]);
|
|
else
|
|
sysdecode_fcntl_arg(stdout,
|
|
ip[0], ip[1],
|
|
decimal ? 10 : 16);
|
|
}
|
|
ip += 2;
|
|
narg -= 2;
|
|
break;
|
|
case SYS_socket: {
|
|
int sockdomain;
|
|
putchar('(');
|
|
sockdomain = *ip;
|
|
print_integer_arg(sysdecode_socketdomain,
|
|
sockdomain);
|
|
ip++;
|
|
narg--;
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_socket_type, *ip);
|
|
ip++;
|
|
narg--;
|
|
if (sockdomain == PF_INET ||
|
|
sockdomain == PF_INET6) {
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_ipproto,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
}
|
|
c = ',';
|
|
break;
|
|
}
|
|
case SYS_setsockopt:
|
|
case SYS_getsockopt: {
|
|
const char *str;
|
|
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg_valid(sysdecode_sockopt_level,
|
|
*ip);
|
|
str = sysdecode_sockopt_name(ip[0], ip[1]);
|
|
if (str != NULL) {
|
|
printf(",%s", str);
|
|
ip++;
|
|
narg--;
|
|
}
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
}
|
|
#ifdef SYS_freebsd6_lseek
|
|
case SYS_freebsd6_lseek:
|
|
print_number(ip, narg, c);
|
|
/* Hidden 'pad' argument, not in lseek(2) */
|
|
print_number(ip, narg, c);
|
|
print_number64(first, ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_whence, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
#endif
|
|
case SYS_lseek:
|
|
print_number(ip, narg, c);
|
|
print_number64(first, ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_whence, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_flock:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_flock_operation, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_mkfifo:
|
|
case SYS_mkfifoat:
|
|
case SYS_mkdir:
|
|
case SYS_mkdirat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
decode_filemode(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_shutdown:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_shutdown_how, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_socketpair:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_socketdomain, *ip);
|
|
ip++;
|
|
narg--;
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_socket_type, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_getrlimit:
|
|
case SYS_setrlimit:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_rlimit, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_getrusage:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_getrusage_who, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_quotactl:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
if (!sysdecode_quotactl_cmd(stdout, *ip)) {
|
|
if (decimal)
|
|
printf("<invalid=%d>", (int)*ip);
|
|
else
|
|
printf("<invalid=%#x>",
|
|
(int)*ip);
|
|
}
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_nfssvc:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_nfssvc_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_rtprio:
|
|
case SYS_rtprio_thread:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_rtprio_function,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS___semctl:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_semctl_cmd, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_semget:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_semget_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_msgctl:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_msgctl_cmd, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_shmat:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_shmat_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_shmctl:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_shmctl_cmd, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
#ifdef SYS_freebsd12_shm_open
|
|
case SYS_freebsd12_shm_open:
|
|
if (ip[0] == (uintptr_t)SHM_ANON) {
|
|
printf("(SHM_ANON");
|
|
ip++;
|
|
} else {
|
|
print_number(ip, narg, c);
|
|
}
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_open_flags, ip[0]);
|
|
putchar(',');
|
|
decode_filemode(ip[1]);
|
|
ip += 2;
|
|
narg -= 2;
|
|
break;
|
|
#endif
|
|
case SYS_shm_open2:
|
|
if (ip[0] == (uintptr_t)SHM_ANON) {
|
|
printf("(SHM_ANON");
|
|
ip++;
|
|
} else {
|
|
print_number(ip, narg, c);
|
|
}
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_open_flags, ip[0]);
|
|
putchar(',');
|
|
decode_filemode(ip[1]);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_shmflags, ip[2]);
|
|
ip += 3;
|
|
narg -= 3;
|
|
break;
|
|
case SYS_minherit:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_minherit_inherit,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_rfork:
|
|
putchar('(');
|
|
print_mask_arg(sysdecode_rfork_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_lio_listio:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_lio_listio_mode,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_mlockall:
|
|
putchar('(');
|
|
print_mask_arg(sysdecode_mlockall_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_sched_setscheduler:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_scheduler_policy,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_sched_get_priority_max:
|
|
case SYS_sched_get_priority_min:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_scheduler_policy,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_sendfile:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_sendfile_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_kldsym:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_kldsym_cmd, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_sigprocmask:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_sigprocmask_how,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS___acl_get_file:
|
|
case SYS___acl_set_file:
|
|
case SYS___acl_get_fd:
|
|
case SYS___acl_set_fd:
|
|
case SYS___acl_delete_file:
|
|
case SYS___acl_delete_fd:
|
|
case SYS___acl_aclcheck_file:
|
|
case SYS___acl_aclcheck_fd:
|
|
case SYS___acl_get_link:
|
|
case SYS___acl_set_link:
|
|
case SYS___acl_delete_link:
|
|
case SYS___acl_aclcheck_link:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_acltype, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_sigaction:
|
|
putchar('(');
|
|
print_signal(*ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_extattrctl:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_extattrnamespace,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_nmount:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_mount_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_thr_create:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_mask_arg(sysdecode_thr_create_flags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_thr_kill:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_signal(*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_kldunloadf:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_kldunload_flags,
|
|
*ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_linkat:
|
|
case SYS_renameat:
|
|
case SYS_symlinkat:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg_valid(sysdecode_atfd, *ip);
|
|
ip++;
|
|
narg--;
|
|
print_number(ip, narg, c);
|
|
break;
|
|
case SYS_cap_fcntls_limit:
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
arg = *ip;
|
|
ip++;
|
|
narg--;
|
|
print_mask_arg32(sysdecode_cap_fcntlrights, arg);
|
|
break;
|
|
case SYS_posix_fadvise:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
(void)putchar(',');
|
|
print_integer_arg(sysdecode_fadvice, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS_procctl:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_idtype, *ip);
|
|
c = ',';
|
|
ip++;
|
|
narg--;
|
|
print_number64(first, ip, narg, c);
|
|
putchar(',');
|
|
print_integer_arg(sysdecode_procctl_cmd, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
case SYS__umtx_op: {
|
|
int op;
|
|
|
|
print_number(ip, narg, c);
|
|
putchar(',');
|
|
if (print_mask_arg_part(sysdecode_umtx_op_flags,
|
|
*ip, &op))
|
|
putchar('|');
|
|
print_integer_arg(sysdecode_umtx_op, op);
|
|
putchar('>');
|
|
switch (*ip) {
|
|
case UMTX_OP_CV_WAIT:
|
|
ip++;
|
|
narg--;
|
|
putchar(',');
|
|
print_mask_argul(
|
|
sysdecode_umtx_cvwait_flags, *ip);
|
|
break;
|
|
case UMTX_OP_RW_RDLOCK:
|
|
ip++;
|
|
narg--;
|
|
putchar(',');
|
|
print_mask_argul(
|
|
sysdecode_umtx_rwlock_flags, *ip);
|
|
break;
|
|
}
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
}
|
|
case SYS_ftruncate:
|
|
case SYS_truncate:
|
|
print_number(ip, narg, c);
|
|
print_number64(first, ip, narg, c);
|
|
break;
|
|
case SYS_fchownat:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
break;
|
|
case SYS_fstatat:
|
|
case SYS_utimensat:
|
|
print_number(ip, narg, c);
|
|
print_number(ip, narg, c);
|
|
break;
|
|
case SYS_unlinkat:
|
|
print_number(ip, narg, c);
|
|
break;
|
|
case SYS_sysarch:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_sysarch_number, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
case SYS_getitimer:
|
|
case SYS_setitimer:
|
|
putchar('(');
|
|
print_integer_arg(sysdecode_itimer, *ip);
|
|
ip++;
|
|
narg--;
|
|
c = ',';
|
|
break;
|
|
}
|
|
switch (ktr->ktr_code) {
|
|
case SYS_chflagsat:
|
|
case SYS_fchownat:
|
|
case SYS_faccessat:
|
|
case SYS_fchmodat:
|
|
case SYS_fstatat:
|
|
case SYS_linkat:
|
|
case SYS_unlinkat:
|
|
case SYS_utimensat:
|
|
putchar(',');
|
|
print_mask_arg0(sysdecode_atflags, *ip);
|
|
ip++;
|
|
narg--;
|
|
break;
|
|
}
|
|
*resc = c;
|
|
*resip = ip;
|
|
*resnarg = narg;
|
|
}
|
|
|
|
static void
|
|
ktrsysret(struct ktr_sysret *ktr, u_int sv_flags)
|
|
{
|
|
register_t ret = ktr->ktr_retval;
|
|
int error = ktr->ktr_error;
|
|
|
|
syscallname(ktr->ktr_code, sv_flags);
|
|
printf(" ");
|
|
|
|
if (error == 0) {
|
|
if (fancy) {
|
|
printf("%ld", (long)ret);
|
|
if (ret < 0 || ret > 9)
|
|
printf("/%#lx", (unsigned long)ret);
|
|
} else {
|
|
if (decimal)
|
|
printf("%ld", (long)ret);
|
|
else
|
|
printf("%#lx", (unsigned long)ret);
|
|
}
|
|
} else if (error == ERESTART)
|
|
printf("RESTART");
|
|
else if (error == EJUSTRETURN)
|
|
printf("JUSTRETURN");
|
|
else {
|
|
printf("-1 errno %d", sysdecode_freebsd_to_abi_errno(
|
|
syscallabi(sv_flags), error));
|
|
if (fancy)
|
|
printf(" %s", strerror(ktr->ktr_error));
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
static void
|
|
ktrnamei(char *cp, int len)
|
|
{
|
|
printf("\"%.*s\"\n", len, cp);
|
|
}
|
|
|
|
static void
|
|
ktrexecve(char *m, int len)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < len) {
|
|
printf("\"%s\"", m + i);
|
|
i += strlen(m + i) + 1;
|
|
if (i != len) {
|
|
printf(", ");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
hexdump(char *p, int len, int screenwidth)
|
|
{
|
|
int n, i;
|
|
int width;
|
|
|
|
width = 0;
|
|
do {
|
|
width += 2;
|
|
i = 13; /* base offset */
|
|
i += (width / 2) + 1; /* spaces every second byte */
|
|
i += (width * 2); /* width of bytes */
|
|
i += 3; /* " |" */
|
|
i += width; /* each byte */
|
|
i += 1; /* "|" */
|
|
} while (i < screenwidth);
|
|
width -= 2;
|
|
|
|
for (n = 0; n < len; n += width) {
|
|
for (i = n; i < n + width; i++) {
|
|
if ((i % width) == 0) { /* beginning of line */
|
|
printf(" 0x%04x", i);
|
|
}
|
|
if ((i % 2) == 0) {
|
|
printf(" ");
|
|
}
|
|
if (i < len)
|
|
printf("%02x", p[i] & 0xff);
|
|
else
|
|
printf(" ");
|
|
}
|
|
printf(" |");
|
|
for (i = n; i < n + width; i++) {
|
|
if (i >= len)
|
|
break;
|
|
if (p[i] >= ' ' && p[i] <= '~')
|
|
printf("%c", p[i]);
|
|
else
|
|
printf(".");
|
|
}
|
|
printf("|\n");
|
|
}
|
|
if ((i % width) != 0)
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
visdump(char *dp, int datalen, int screenwidth)
|
|
{
|
|
int col = 0;
|
|
char *cp;
|
|
int width;
|
|
char visbuf[5];
|
|
|
|
printf(" \"");
|
|
col = 8;
|
|
for (;datalen > 0; datalen--, dp++) {
|
|
vis(visbuf, *dp, VIS_CSTYLE | VIS_NOLOCALE, *(dp+1));
|
|
cp = visbuf;
|
|
/*
|
|
* Keep track of printables and
|
|
* space chars (like fold(1)).
|
|
*/
|
|
if (col == 0) {
|
|
putchar('\t');
|
|
col = 8;
|
|
}
|
|
switch(*cp) {
|
|
case '\n':
|
|
col = 0;
|
|
putchar('\n');
|
|
continue;
|
|
case '\t':
|
|
width = 8 - (col&07);
|
|
break;
|
|
default:
|
|
width = strlen(cp);
|
|
}
|
|
if (col + width > (screenwidth-2)) {
|
|
printf("\\\n\t");
|
|
col = 8;
|
|
}
|
|
col += width;
|
|
do {
|
|
putchar(*cp++);
|
|
} while (*cp);
|
|
}
|
|
if (col == 0)
|
|
printf(" ");
|
|
printf("\"\n");
|
|
}
|
|
|
|
static void
|
|
ktrgenio(struct ktr_genio *ktr, int len)
|
|
{
|
|
int datalen = len - sizeof (struct ktr_genio);
|
|
char *dp = (char *)ktr + sizeof (struct ktr_genio);
|
|
static int screenwidth = 0;
|
|
int i, binary;
|
|
|
|
printf("fd %d %s %d byte%s\n", ktr->ktr_fd,
|
|
ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen,
|
|
datalen == 1 ? "" : "s");
|
|
if (suppressdata)
|
|
return;
|
|
if (screenwidth == 0) {
|
|
struct winsize ws;
|
|
|
|
if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
|
|
ws.ws_col > 8)
|
|
screenwidth = ws.ws_col;
|
|
else
|
|
screenwidth = 80;
|
|
}
|
|
if (maxdata && datalen > maxdata)
|
|
datalen = maxdata;
|
|
|
|
for (i = 0, binary = 0; i < datalen && binary == 0; i++) {
|
|
if (dp[i] >= 32 && dp[i] < 127)
|
|
continue;
|
|
if (dp[i] == 10 || dp[i] == 13 || dp[i] == 0 || dp[i] == 9)
|
|
continue;
|
|
binary = 1;
|
|
}
|
|
if (binary)
|
|
hexdump(dp, datalen, screenwidth);
|
|
else
|
|
visdump(dp, datalen, screenwidth);
|
|
}
|
|
|
|
static void
|
|
ktrpsig(struct ktr_psig *psig)
|
|
{
|
|
const char *str;
|
|
|
|
print_signal(psig->signo);
|
|
if (psig->action == SIG_DFL) {
|
|
printf(" SIG_DFL");
|
|
} else {
|
|
printf(" caught handler=0x%lx mask=0x%x",
|
|
(u_long)psig->action, psig->mask.__bits[0]);
|
|
}
|
|
printf(" code=");
|
|
str = sysdecode_sigcode(psig->signo, psig->code);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else
|
|
printf("<invalid=%#x>", psig->code);
|
|
putchar('\n');
|
|
}
|
|
|
|
static void
|
|
ktrcsw_old(struct ktr_csw_old *cs)
|
|
{
|
|
printf("%s %s\n", cs->out ? "stop" : "resume",
|
|
cs->user ? "user" : "kernel");
|
|
}
|
|
|
|
static void
|
|
ktrcsw(struct ktr_csw *cs)
|
|
{
|
|
printf("%s %s \"%s\"\n", cs->out ? "stop" : "resume",
|
|
cs->user ? "user" : "kernel", cs->wmesg);
|
|
}
|
|
|
|
static void
|
|
ktruser(int len, void *p)
|
|
{
|
|
unsigned char *cp;
|
|
|
|
if (sysdecode_utrace(stdout, p, len)) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
printf("%d ", len);
|
|
cp = p;
|
|
while (len--)
|
|
if (decimal)
|
|
printf(" %d", *cp++);
|
|
else
|
|
printf(" %02x", *cp++);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
ktrcaprights(cap_rights_t *rightsp)
|
|
{
|
|
|
|
printf("cap_rights_t ");
|
|
sysdecode_cap_rights(stdout, rightsp);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
ktrtimeval(struct timeval *tv)
|
|
{
|
|
|
|
printf("{%ld, %ld}", (long)tv->tv_sec, tv->tv_usec);
|
|
}
|
|
|
|
static void
|
|
ktrinotify(struct inotify_event *ev)
|
|
{
|
|
printf(
|
|
"inotify { .wd = %d, .mask = %#x, .cookie = %u, .len = %u, .name = %s }\n",
|
|
ev->wd, ev->mask, ev->cookie, ev->len, ev->name);
|
|
}
|
|
|
|
static void
|
|
ktritimerval(struct itimerval *it)
|
|
{
|
|
|
|
printf("itimerval { .interval = ");
|
|
ktrtimeval(&it->it_interval);
|
|
printf(", .value = ");
|
|
ktrtimeval(&it->it_value);
|
|
printf(" }\n");
|
|
}
|
|
|
|
static void
|
|
ktrsockaddr(struct sockaddr *sa)
|
|
{
|
|
/*
|
|
TODO: Support additional address families
|
|
#include <netsmb/netbios.h>
|
|
struct sockaddr_nb *nb;
|
|
*/
|
|
const char *str;
|
|
char addr[64];
|
|
|
|
/*
|
|
* note: ktrstruct() has already verified that sa points to a
|
|
* buffer at least sizeof(struct sockaddr) bytes long and exactly
|
|
* sa->sa_len bytes long.
|
|
*/
|
|
printf("struct sockaddr { ");
|
|
str = sysdecode_sockaddr_family(sa->sa_family);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else
|
|
printf("<invalid=%d>", sa->sa_family);
|
|
printf(", ");
|
|
|
|
#define check_sockaddr_len(n) \
|
|
if (sa_##n.s##n##_len < sizeof(struct sockaddr_##n)) { \
|
|
printf("invalid"); \
|
|
break; \
|
|
}
|
|
|
|
switch(sa->sa_family) {
|
|
case AF_INET: {
|
|
struct sockaddr_in sa_in;
|
|
|
|
memset(&sa_in, 0, sizeof(sa_in));
|
|
memcpy(&sa_in, sa, sa->sa_len);
|
|
check_sockaddr_len(in);
|
|
inet_ntop(AF_INET, &sa_in.sin_addr, addr, sizeof addr);
|
|
printf("%s:%u", addr, ntohs(sa_in.sin_port));
|
|
break;
|
|
}
|
|
case AF_INET6: {
|
|
struct sockaddr_in6 sa_in6;
|
|
|
|
memset(&sa_in6, 0, sizeof(sa_in6));
|
|
memcpy(&sa_in6, sa, sa->sa_len);
|
|
check_sockaddr_len(in6);
|
|
getnameinfo((struct sockaddr *)&sa_in6, sizeof(sa_in6),
|
|
addr, sizeof(addr), NULL, 0, NI_NUMERICHOST);
|
|
printf("[%s]:%u", addr, htons(sa_in6.sin6_port));
|
|
break;
|
|
}
|
|
case AF_UNIX: {
|
|
struct sockaddr_un sa_un;
|
|
|
|
memset(&sa_un, 0, sizeof(sa_un));
|
|
memcpy(&sa_un, sa, sa->sa_len);
|
|
printf("%.*s", (int)sizeof(sa_un.sun_path), sa_un.sun_path);
|
|
break;
|
|
}
|
|
case AF_NETLINK: {
|
|
struct sockaddr_nl sa_nl;
|
|
|
|
memset(&sa_nl, 0, sizeof(sa_nl));
|
|
memcpy(&sa_nl, sa, sa->sa_len);
|
|
printf("netlink[pid=%u, groups=0x%x]",
|
|
sa_nl.nl_pid, sa_nl.nl_groups);
|
|
break;
|
|
}
|
|
default:
|
|
printf("unknown address family");
|
|
}
|
|
printf(" }\n");
|
|
}
|
|
|
|
static void
|
|
ktrsplice(struct splice *sp)
|
|
{
|
|
printf("struct splice { fd=%d, max=%#jx, idle=%jd.%06jd }\n",
|
|
sp->sp_fd, (uintmax_t)sp->sp_max, (intmax_t)sp->sp_idle.tv_sec,
|
|
(intmax_t)sp->sp_idle.tv_usec);
|
|
}
|
|
|
|
static void
|
|
ktrthrparam(struct thr_param *tp)
|
|
{
|
|
printf("thr param { start=%p arg=%p stack_base=%p "
|
|
"stack_size=%#zx tls_base=%p tls_size=%#zx child_tidp=%p "
|
|
"parent_tidp=%p flags=",
|
|
tp->start_func, tp->arg, tp->stack_base, tp->stack_size,
|
|
tp->tls_base, tp->tls_size, tp->child_tid, tp->parent_tid);
|
|
print_mask_arg(sysdecode_thr_create_flags, tp->flags);
|
|
printf(" rtp=%p }\n", tp->rtp);
|
|
}
|
|
|
|
static void
|
|
ktrstat(struct stat *statp)
|
|
{
|
|
char mode[12], timestr[PATH_MAX + 4];
|
|
struct passwd *pwd;
|
|
struct group *grp;
|
|
struct tm *tm;
|
|
|
|
/*
|
|
* note: ktrstruct() has already verified that statp points to a
|
|
* buffer exactly sizeof(struct stat) bytes long.
|
|
*/
|
|
printf("struct stat {");
|
|
printf("dev=%ju, ino=%ju, ",
|
|
(uintmax_t)statp->st_dev, (uintmax_t)statp->st_ino);
|
|
if (!resolv)
|
|
printf("mode=0%jo, ", (uintmax_t)statp->st_mode);
|
|
else {
|
|
strmode(statp->st_mode, mode);
|
|
printf("mode=%s, ", mode);
|
|
}
|
|
printf("nlink=%ju, ", (uintmax_t)statp->st_nlink);
|
|
if (!resolv) {
|
|
pwd = NULL;
|
|
} else {
|
|
#ifdef WITH_CASPER
|
|
if (cappwd != NULL)
|
|
pwd = cap_getpwuid(cappwd, statp->st_uid);
|
|
else
|
|
#endif
|
|
pwd = getpwuid(statp->st_uid);
|
|
}
|
|
if (pwd == NULL)
|
|
printf("uid=%ju, ", (uintmax_t)statp->st_uid);
|
|
else
|
|
printf("uid=\"%s\", ", pwd->pw_name);
|
|
if (!resolv) {
|
|
grp = NULL;
|
|
} else {
|
|
#ifdef WITH_CASPER
|
|
if (capgrp != NULL)
|
|
grp = cap_getgrgid(capgrp, statp->st_gid);
|
|
else
|
|
#endif
|
|
grp = getgrgid(statp->st_gid);
|
|
}
|
|
if (grp == NULL)
|
|
printf("gid=%ju, ", (uintmax_t)statp->st_gid);
|
|
else
|
|
printf("gid=\"%s\", ", grp->gr_name);
|
|
printf("rdev=%ju, ", (uintmax_t)statp->st_rdev);
|
|
printf("atime=");
|
|
if (!resolv)
|
|
printf("%jd", (intmax_t)statp->st_atim.tv_sec);
|
|
else {
|
|
tm = localtime(&statp->st_atim.tv_sec);
|
|
strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
|
|
printf("\"%s\"", timestr);
|
|
}
|
|
if (statp->st_atim.tv_nsec != 0)
|
|
printf(".%09ld, ", statp->st_atim.tv_nsec);
|
|
else
|
|
printf(", ");
|
|
printf("mtime=");
|
|
if (!resolv)
|
|
printf("%jd", (intmax_t)statp->st_mtim.tv_sec);
|
|
else {
|
|
tm = localtime(&statp->st_mtim.tv_sec);
|
|
strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
|
|
printf("\"%s\"", timestr);
|
|
}
|
|
if (statp->st_mtim.tv_nsec != 0)
|
|
printf(".%09ld, ", statp->st_mtim.tv_nsec);
|
|
else
|
|
printf(", ");
|
|
printf("ctime=");
|
|
if (!resolv)
|
|
printf("%jd", (intmax_t)statp->st_ctim.tv_sec);
|
|
else {
|
|
tm = localtime(&statp->st_ctim.tv_sec);
|
|
strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
|
|
printf("\"%s\"", timestr);
|
|
}
|
|
if (statp->st_ctim.tv_nsec != 0)
|
|
printf(".%09ld, ", statp->st_ctim.tv_nsec);
|
|
else
|
|
printf(", ");
|
|
printf("birthtime=");
|
|
if (!resolv)
|
|
printf("%jd", (intmax_t)statp->st_birthtim.tv_sec);
|
|
else {
|
|
tm = localtime(&statp->st_birthtim.tv_sec);
|
|
strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
|
|
printf("\"%s\"", timestr);
|
|
}
|
|
if (statp->st_birthtim.tv_nsec != 0)
|
|
printf(".%09ld, ", statp->st_birthtim.tv_nsec);
|
|
else
|
|
printf(", ");
|
|
printf("size=%jd, blksize=%ju, blocks=%jd, flags=0x%x",
|
|
(uintmax_t)statp->st_size, (uintmax_t)statp->st_blksize,
|
|
(intmax_t)statp->st_blocks, statp->st_flags);
|
|
printf(" }\n");
|
|
}
|
|
|
|
static void
|
|
ktrbitset(char *name, struct bitset *set, size_t setlen)
|
|
{
|
|
int i, maxi, c = 0;
|
|
|
|
if (setlen > INT32_MAX)
|
|
setlen = INT32_MAX;
|
|
maxi = setlen * CHAR_BIT;
|
|
printf("%s [ ", name);
|
|
for (i = 0; i < maxi; i++) {
|
|
if (!BIT_ISSET(setlen, i, set))
|
|
continue;
|
|
if (c == 0)
|
|
printf("%d", i);
|
|
else
|
|
printf(", %d", i);
|
|
c++;
|
|
}
|
|
if (c == 0)
|
|
printf(" empty ]\n");
|
|
else
|
|
printf(" ]\n");
|
|
}
|
|
|
|
static void
|
|
ktrstruct(char *buf, size_t buflen)
|
|
{
|
|
char *name, *data;
|
|
size_t namelen, datalen;
|
|
int i;
|
|
cap_rights_t rights;
|
|
struct itimerval it;
|
|
struct stat sb;
|
|
struct sockaddr_storage ss;
|
|
struct bitset *set;
|
|
|
|
for (name = buf, namelen = 0;
|
|
namelen < buflen && name[namelen] != '\0';
|
|
++namelen)
|
|
/* nothing */;
|
|
if (namelen == buflen)
|
|
goto invalid;
|
|
if (name[namelen] != '\0')
|
|
goto invalid;
|
|
data = buf + namelen + 1;
|
|
datalen = buflen - namelen - 1;
|
|
if (datalen == 0)
|
|
goto invalid;
|
|
/* sanity check */
|
|
for (i = 0; i < (int)namelen; ++i)
|
|
if (!isalpha(name[i]) && name[i] != '_')
|
|
goto invalid;
|
|
if (strcmp(name, "caprights") == 0) {
|
|
if (datalen != sizeof(cap_rights_t))
|
|
goto invalid;
|
|
memcpy(&rights, data, datalen);
|
|
ktrcaprights(&rights);
|
|
} else if (strcmp(name, "inotify") == 0) {
|
|
struct inotify_event *ev;
|
|
|
|
if (datalen < sizeof(struct inotify_event) ||
|
|
datalen > sizeof(struct inotify_event) + NAME_MAX + 1)
|
|
goto invalid;
|
|
ev = malloc(datalen);
|
|
if (ev == NULL)
|
|
err(1, "malloc");
|
|
memcpy(ev, data, datalen);
|
|
ktrinotify(ev);
|
|
} else if (strcmp(name, "itimerval") == 0) {
|
|
if (datalen != sizeof(struct itimerval))
|
|
goto invalid;
|
|
memcpy(&it, data, datalen);
|
|
ktritimerval(&it);
|
|
} else if (strcmp(name, "stat") == 0) {
|
|
if (datalen != sizeof(struct stat))
|
|
goto invalid;
|
|
memcpy(&sb, data, datalen);
|
|
ktrstat(&sb);
|
|
} else if (strcmp(name, "sockaddr") == 0) {
|
|
if (datalen > sizeof(ss))
|
|
goto invalid;
|
|
memcpy(&ss, data, datalen);
|
|
if (datalen != ss.ss_len)
|
|
goto invalid;
|
|
ktrsockaddr((struct sockaddr *)&ss);
|
|
} else if (strcmp(name, "cpuset_t") == 0) {
|
|
if (datalen < 1)
|
|
goto invalid;
|
|
set = malloc(datalen);
|
|
if (set == NULL)
|
|
errx(1, "%s", strerror(ENOMEM));
|
|
memcpy(set, data, datalen);
|
|
ktrbitset(name, set, datalen);
|
|
free(set);
|
|
} else if (strcmp(name, "splice") == 0) {
|
|
struct splice sp;
|
|
|
|
if (datalen != sizeof(sp))
|
|
goto invalid;
|
|
memcpy(&sp, data, datalen);
|
|
ktrsplice(&sp);
|
|
} else if (strcmp(name, "thrparam") == 0) {
|
|
struct thr_param tp;
|
|
|
|
if (datalen != sizeof(tp))
|
|
goto invalid;
|
|
memcpy(&tp, data, datalen);
|
|
ktrthrparam(&tp);
|
|
} else {
|
|
#ifdef SYSDECODE_HAVE_LINUX
|
|
if (ktrstruct_linux(name, data, datalen) == false)
|
|
#endif
|
|
printf("unknown structure\n");
|
|
}
|
|
return;
|
|
invalid:
|
|
printf("invalid record\n");
|
|
}
|
|
|
|
static void
|
|
ktrcapfail(struct ktr_cap_fail *ktr)
|
|
{
|
|
union ktr_cap_data *kcd = &ktr->cap_data;
|
|
|
|
switch (ktr->cap_type) {
|
|
case CAPFAIL_NOTCAPABLE:
|
|
/* operation on fd with insufficient capabilities */
|
|
printf("operation requires ");
|
|
sysdecode_cap_rights(stdout, &kcd->cap_needed);
|
|
printf(", descriptor holds ");
|
|
sysdecode_cap_rights(stdout, &kcd->cap_held);
|
|
break;
|
|
case CAPFAIL_INCREASE:
|
|
/* requested more capabilities than fd already has */
|
|
printf("attempt to increase capabilities from ");
|
|
sysdecode_cap_rights(stdout, &kcd->cap_held);
|
|
printf(" to ");
|
|
sysdecode_cap_rights(stdout, &kcd->cap_needed);
|
|
break;
|
|
case CAPFAIL_SYSCALL:
|
|
/* called restricted syscall */
|
|
printf("system call not allowed: ");
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
if (syscallabi(ktr->cap_svflags) == SYSDECODE_ABI_FREEBSD) {
|
|
switch (ktr->cap_code) {
|
|
case SYS_sysarch:
|
|
printf(", op: ");
|
|
print_integer_arg(sysdecode_sysarch_number,
|
|
kcd->cap_int);
|
|
break;
|
|
case SYS_fcntl:
|
|
printf(", cmd: ");
|
|
print_integer_arg(sysdecode_fcntl_cmd,
|
|
kcd->cap_int);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case CAPFAIL_SIGNAL:
|
|
/* sent signal to proc other than self */
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": signal delivery not allowed: ");
|
|
print_integer_arg(sysdecode_signal, kcd->cap_int);
|
|
break;
|
|
case CAPFAIL_PROTO:
|
|
/* created socket with restricted protocol */
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": protocol not allowed: ");
|
|
print_integer_arg(sysdecode_ipproto, kcd->cap_int);
|
|
break;
|
|
case CAPFAIL_SOCKADDR:
|
|
/* unable to look up address */
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": restricted address lookup: ");
|
|
ktrsockaddr(&kcd->cap_sockaddr);
|
|
return;
|
|
case CAPFAIL_NAMEI:
|
|
/* absolute or AT_FDCWD path, ".." path, etc. */
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": restricted VFS lookup: %s\n", kcd->cap_path);
|
|
return;
|
|
case CAPFAIL_CPUSET:
|
|
/* modification of an external cpuset */
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": restricted cpuset operation\n");
|
|
return;
|
|
default:
|
|
syscallname(ktr->cap_code, ktr->cap_svflags);
|
|
printf(": unknown capability failure\n");
|
|
return;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
ktrfault(struct ktr_fault *ktr)
|
|
{
|
|
|
|
printf("0x%jx ", (uintmax_t)ktr->vaddr);
|
|
print_mask_arg(sysdecode_vmprot, ktr->type);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
ktrfaultend(struct ktr_faultend *ktr)
|
|
{
|
|
const char *str;
|
|
|
|
str = sysdecode_vmresult(ktr->result);
|
|
if (str != NULL)
|
|
printf("%s", str);
|
|
else
|
|
printf("<invalid=%d>", ktr->result);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
ktrkevent(struct kevent *kev)
|
|
{
|
|
|
|
printf("{ ident=");
|
|
switch (kev->filter) {
|
|
case EVFILT_READ:
|
|
case EVFILT_WRITE:
|
|
case EVFILT_VNODE:
|
|
case EVFILT_PROC:
|
|
case EVFILT_TIMER:
|
|
case EVFILT_PROCDESC:
|
|
case EVFILT_EMPTY:
|
|
printf("%ju", (uintmax_t)kev->ident);
|
|
break;
|
|
case EVFILT_SIGNAL:
|
|
print_signal(kev->ident);
|
|
break;
|
|
default:
|
|
printf("%p", (void *)kev->ident);
|
|
}
|
|
printf(", filter=");
|
|
print_integer_arg(sysdecode_kevent_filter, kev->filter);
|
|
printf(", flags=");
|
|
print_mask_arg0(sysdecode_kevent_flags, kev->flags);
|
|
printf(", fflags=");
|
|
sysdecode_kevent_fflags(stdout, kev->filter, kev->fflags,
|
|
decimal ? 10 : 16);
|
|
printf(", data=%#jx, udata=%p }", (uintmax_t)kev->data, kev->udata);
|
|
}
|
|
|
|
static void
|
|
ktrpollfd(struct pollfd *pfd)
|
|
{
|
|
|
|
printf("{ fd=%d", pfd->fd);
|
|
printf(", events=");
|
|
print_mask_arg0(sysdecode_pollfd_events, pfd->events);
|
|
printf(", revents=");
|
|
print_mask_arg0(sysdecode_pollfd_events, pfd->revents);
|
|
printf("}");
|
|
}
|
|
|
|
static void
|
|
ktrstructarray(struct ktr_struct_array *ksa, size_t buflen)
|
|
{
|
|
struct kevent kev;
|
|
struct pollfd pfd;
|
|
char *name, *data;
|
|
size_t namelen, datalen;
|
|
int i;
|
|
bool first;
|
|
|
|
buflen -= sizeof(*ksa);
|
|
for (name = (char *)(ksa + 1), namelen = 0;
|
|
namelen < buflen && name[namelen] != '\0';
|
|
++namelen)
|
|
/* nothing */;
|
|
if (namelen == buflen)
|
|
goto invalid;
|
|
if (name[namelen] != '\0')
|
|
goto invalid;
|
|
/* sanity check */
|
|
for (i = 0; i < (int)namelen; ++i)
|
|
if (!isalnum(name[i]) && name[i] != '_')
|
|
goto invalid;
|
|
data = name + namelen + 1;
|
|
datalen = buflen - namelen - 1;
|
|
printf("struct %s[] = { ", name);
|
|
first = true;
|
|
for (; datalen >= ksa->struct_size;
|
|
data += ksa->struct_size, datalen -= ksa->struct_size) {
|
|
if (!first)
|
|
printf("\n ");
|
|
else
|
|
first = false;
|
|
if (strcmp(name, "kevent") == 0) {
|
|
if (ksa->struct_size != sizeof(kev))
|
|
goto bad_size;
|
|
memcpy(&kev, data, sizeof(kev));
|
|
ktrkevent(&kev);
|
|
} else if (strcmp(name, "freebsd11_kevent") == 0) {
|
|
struct freebsd11_kevent kev11;
|
|
|
|
if (ksa->struct_size != sizeof(kev11))
|
|
goto bad_size;
|
|
memcpy(&kev11, data, sizeof(kev11));
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = kev11.ident;
|
|
kev.filter = kev11.filter;
|
|
kev.flags = kev11.flags;
|
|
kev.fflags = kev11.fflags;
|
|
kev.data = kev11.data;
|
|
kev.udata = kev11.udata;
|
|
ktrkevent(&kev);
|
|
#ifdef _WANT_KEVENT32
|
|
} else if (strcmp(name, "kevent32") == 0) {
|
|
struct kevent32 kev32;
|
|
|
|
if (ksa->struct_size != sizeof(kev32))
|
|
goto bad_size;
|
|
memcpy(&kev32, data, sizeof(kev32));
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = kev32.ident;
|
|
kev.filter = kev32.filter;
|
|
kev.flags = kev32.flags;
|
|
kev.fflags = kev32.fflags;
|
|
memcpy(&kev.data, &kev32.data, sizeof(kev.data));
|
|
kev.udata = (void *)(uintptr_t)kev32.udata;
|
|
ktrkevent(&kev);
|
|
} else if (strcmp(name, "freebsd11_kevent32") == 0) {
|
|
struct freebsd11_kevent32 kev32;
|
|
|
|
if (ksa->struct_size != sizeof(kev32))
|
|
goto bad_size;
|
|
memcpy(&kev32, data, sizeof(kev32));
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = kev32.ident;
|
|
kev.filter = kev32.filter;
|
|
kev.flags = kev32.flags;
|
|
kev.fflags = kev32.fflags;
|
|
kev.data = kev32.data;
|
|
kev.udata = (void *)(uintptr_t)kev32.udata;
|
|
ktrkevent(&kev);
|
|
#endif
|
|
} else if (strcmp(name, "pollfd") == 0) {
|
|
if (ksa->struct_size != sizeof(pfd))
|
|
goto bad_size;
|
|
memcpy(&pfd, data, sizeof(pfd));
|
|
ktrpollfd(&pfd);
|
|
} else {
|
|
printf("<unknown structure> }\n");
|
|
return;
|
|
}
|
|
}
|
|
printf(" }\n");
|
|
return;
|
|
invalid:
|
|
printf("invalid record\n");
|
|
return;
|
|
bad_size:
|
|
printf("<bad size> }\n");
|
|
return;
|
|
}
|
|
|
|
static const char * const cat_to_filenames[] = {
|
|
#include <exterr_cat_filenames.h>
|
|
};
|
|
|
|
static const char *
|
|
cat_to_file_prefix(int category)
|
|
{
|
|
switch (EXTERR_CAT_SRC(category)) {
|
|
case EXTERR_CAT_SRC_KERN_STATIC:
|
|
return ("sys/");
|
|
default:
|
|
return ("");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
cat_to_filename(int category)
|
|
{
|
|
switch (EXTERR_CAT_SRC(category)) {
|
|
case EXTERR_CAT_SRC_KERN_STATIC:
|
|
if (category < 0 ||
|
|
(unsigned)category >= nitems(cat_to_filenames) ||
|
|
cat_to_filenames[category] == NULL)
|
|
return ("unknown");
|
|
return (cat_to_filenames[category]);
|
|
case EXTERR_CAT_SRC_KERN_DYNAMIC:
|
|
return ("kern:dynamic");
|
|
default:
|
|
return ("unknown");
|
|
}
|
|
}
|
|
|
|
static void
|
|
ktrexterr(struct ktr_exterr *ke)
|
|
{
|
|
char *msg;
|
|
struct uexterror *ue;
|
|
|
|
ue = &ke->ue;
|
|
asprintf(&msg, ue->msg, (uintmax_t)ue->p1, (uintmax_t)ue->p2);
|
|
printf("{ errno %d %s%s:%u \"%s\" (category %u p1 %#jx p2 %#jx) }\n",
|
|
ue->error, cat_to_file_prefix(ue->cat), cat_to_filename(ue->cat),
|
|
ue->src_line, msg, ue->cat,
|
|
(uintmax_t)ue->p1, (uintmax_t)ue->p2);
|
|
free(msg);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "usage: kdump [-dEnlHRrSsTA] [-f trfile] "
|
|
"[-m maxdata] [-p pid] [-t trstr]\n");
|
|
exit(1);
|
|
}
|