Files
Devin Teske af25f24762 truss: add -t to select which system calls are reported
truss reports every system call a process makes, which for anything
larger than a toy program buries the calls of interest.  Add -t, taking
a comma-separated expression naming the system calls to report.

A term is the name of a system call, which may contain the fnmatch(3)
wildcards; a system call number in decimal; or "@group" naming a group
of related system calls.  A term prefixed with '!' excludes what that
one term matches rather than including it, and applies to no other
term.  An expression whose terms are all negated subtracts from the set
of every system call; any other expression selects from an empty one.
Terms apply in order and the last one to match a system call decides
whether it is reported.  Repeating -t appends, so "-t a -t b" and
"-t a,b" are equivalent.  An empty term is ignored, so an empty
expression filters nothing and a stray comma is not an error.

    truss -t @file,@net fetch https://www.freebsd.org/
    truss -t '!@memory' make buildworld
    truss -t '@desc,!@read,!@write' -p 34
    truss -c -t 'readlink*' /bin/ls
    truss -t 3,4 cat file

A number selects the system call with that number in the ABI of the
traced process, so "-t 3" selects read(2) from a native process but
close() from a Linux one.

Thirteen groups are provided to start with: @all, @creds, @desc, @file,
@ipc, @memory, @net, @none, @proc, @read, @signal, @time and @write.
These were derived by working through sys/kern/syscalls.master; the
audit event in that file is too sparse to drive the grouping by itself
(316 distinct events over 509 live system calls, 111 of them AUE_NULL),
so the groups are curated, but they are curated as patterns rather than
as name lists.  A family sharing a naming convention is written as one
pattern -- "extattr_*_file", "__acl_*_fd", "sctp_*" -- so system calls
added later join the right group without further change here.

A group's member list is an expression in exactly the form -t accepts,
so a group can say anything a user can say on the command line: a
member may be a pattern, a number or another group, and may be negated.
@desc is built from @read and @write without repeating them, and @none
is the single member "!*".  Keeping the two languages identical means a
group defined from a -t expression supplied elsewhere needs no
translation to become a member list.  Adding a group is a member list
plus one entry in syscall_groups[].

"truss -t" with no expression prints the groups and exits.

Matching is done against the name truss displays and against that name
with any compatibility or ABI prefix removed, so @file selects
compat11.stat, freebsd32_stat and linux_newstat as well as stat.

A name or pattern matching no system call of any ABI truss understands
is reported with a warning, since it is almost always a typo, but it is
kept and simply never matches.  sysdecode(3) is the oracle: it names
every system call of every such ABI whether or not that ABI's module is
loaded, and names them exactly as truss reports them.  Numbers are not
checked this way.  A process may issue any number the kernel can hold,
whether or not a system call is implemented behind it; one that is not
returns ENOSYS, which truss reports like any other result.  Only a
number too large to be one at all is rejected.

A system call excluded by -t is not decoded, so the filter also removes
the cost of formatting arguments that would never be printed, and it is
left out of the -c summary.

The option letter is the one truss on System V Release 4 and SunOS uses
for this feature, "-t [!]syscall,...", and which truss(1) already names
as its model.  The syntax is deliberately not bug-compatible with it:
there '!' is sticky for the remainder of a list, and a second -t
discards the first when the first began with '!'.

usr.bin/truss/tests is new, so etc/mtree/BSD.tests.dist gains an entry.

Without -t the behaviour is unchanged.

MFC after:	2 weeks
Reviewed by:	fuz
Differential Revision:	https://reviews.freebsd.org/D59275
2026-09-01 14:28:05 -07:00

278 lines
7.3 KiB
C

/*-
* SPDX-License-Identifier: BSD-4-Clause
*
* Copyright 1997 Sean Eric Fagan
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Sean Eric Fagan
* 4. Neither the name of the author may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/*
* System call arguments come in several flavours:
* Hex -- values that should be printed in hex (addresses)
* Octal -- Same as above, but octal
* Int -- normal integer values (file descriptors, for example)
* LongHex -- long value that should be printed in hex
* Name -- pointer to a NULL-terminated string.
* BinString -- pointer to an array of chars, printed via strvisx().
* Ptr -- pointer to some unspecified structure. Just print as hex for now.
* Stat -- a pointer to a stat buffer. Prints a couple fields.
* Stat11 -- a pointer to a freebsd 11 stat buffer. Prints a couple fields.
* StatFs -- a pointer to a statfs buffer. Prints a few fields.
* Ioctl -- an ioctl command. Woefully limited.
* Quad -- a double-word value. e.g., lseek(int, offset_t, int)
* Signal -- a signal number. Prints the signal name (SIGxxx)
* Sockaddr -- a pointer to a struct sockaddr. Prints symbolic AF, and IP:Port
* StringArray -- a pointer to an array of string pointers.
* Timespec -- a pointer to a struct timespec. Prints both elements.
* Timeval -- a pointer to a struct timeval. Prints both elements.
* Timeval2 -- a pointer to two struct timevals. Prints both elements of both.
* Itimerval -- a pointer to a struct itimerval. Prints all elements.
* Pollfd -- a pointer to an array of struct pollfd. Prints .fd and .events.
* Fd_set -- a pointer to an array of fd_set. Prints the fds that are set.
* Sigaction -- a pointer to a struct sigaction. Prints all elements.
* Sigset -- a pointer to a sigset_t. Prints the signals that are set.
* Sigprocmask -- the first argument to sigprocmask(). Prints the name.
* Kevent -- a pointer to an array of struct kevents. Prints all elements.
* Pathconf -- the 2nd argument of pathconf().
* Utrace -- utrace(2) buffer.
* CapRights -- a pointer to a cap_rights_t. Prints all set capabilities.
*
* In addition, the pointer types (String, Ptr) may have OUT masked in --
* this means that the data is set on *return* from the system call -- or
* IN (meaning that the data is passed *into* the system call).
*/
enum Argtype {
None = 1,
/* Scalar integers. */
Socklent,
Octal,
Int,
UInt,
Hex,
Long,
LongHex,
Sizet,
Quad,
QuadHex,
/* Encoded scalar values. */
Accessmode,
Acltype,
AiofsyncOp,
Atfd,
Atflags,
CapFcntlRights,
Closerangeflags,
Extattrnamespace,
Fadvice,
Fcntl,
Fcntlflag,
FileFlags,
Flockop,
Getfsstatmode,
Idtype,
Ioctl,
Inotifyflags,
Itimerwhich,
Kldsymcmd,
Kldunloadflags,
LioMode,
Madvice,
Minherit,
Msgflags,
Mlockall,
Mmapflags,
Mountflags,
Mprot,
Msync,
Open,
Pathconf,
Pipe2,
Procctl,
Priowhich,
Ptraceop,
Sendfileflags,
Sendfilehdtr,
Quotactlcmd,
Reboothowto,
Resource,
Rforkflags,
Rtpriofunc,
RusageWho,
Schedpolicy,
ShmFlags,
Shutdown,
Signal,
Sigprocmask,
Sockdomain,
Sockoptlevel,
Sockoptname,
Sockprotocol,
Socktype,
Sysarch,
Sysctl,
Umtxop,
Waitoptions,
Whence,
/* Pointers to non-structures. */
Ptr,
AiocbArray,
AiocbPointer,
BinString,
CapRights,
ExecArgs,
ExecEnv,
ExitStatus,
Fd_set,
IntArray,
Iovec,
Name,
PipeFds,
PSig,
PQuadHex,
PUInt,
Readlinkres,
ShmName,
StringArray,
/* Pointers to structures. */
Aiocb,
Itimerval,
Kevent,
Kevent11,
LinuxSockArgs,
Msghdr,
Pollfd,
Rlimit,
Rusage,
Schedparam,
Sctpsndrcvinfo,
Sigaction,
Sigevent,
Siginfo,
Sigset,
Sockaddr,
Stat,
Stat11,
StatFs,
Timespec,
Timespec2,
Timeval,
Timeval2,
Utrace,
MAX_ARG_TYPE,
};
#define ARG_MASK 0xff
#define OUT 0x100
#define IN /*0x20*/0
_Static_assert(ARG_MASK > MAX_ARG_TYPE,
"ARG_MASK overlaps with Argtype values");
struct syscall_arg {
enum Argtype type;
int offset;
};
struct syscall_decode {
const char *name; /* Name for calling convention lookup. */
/*
* Syscall return type:
* 0: no return value (e.g. exit)
* 1: normal return value (a single int/long/pointer)
* 2: off_t return value (two values for 32-bit ABIs)
*/
u_int ret_type;
u_int nargs; /* number of meaningful arguments */
struct syscall_arg args[10]; /* Hopefully no syscalls with > 10 args */
};
struct syscall {
STAILQ_ENTRY(syscall) entries;
const char *name; /* Name to be displayed, might be malloc()'d */
struct syscall_decode decode;
struct timespec time; /* Time spent for this call */
int ncalls; /* Number of calls */
int nerror; /* Number of calls that returned with error */
bool trace; /* Selected for reporting by -t */
bool unknown; /* Unknown system call */
};
struct syscall *get_syscall(struct threadinfo *, u_int, u_int);
char *print_arg(struct syscall_arg *, syscallarg_t *, syscallarg_t *,
struct trussinfo *, struct syscall_decode *);
/*
* Linux Socket defines
*/
#define LINUX_SOCKET 1
#define LINUX_BIND 2
#define LINUX_CONNECT 3
#define LINUX_LISTEN 4
#define LINUX_ACCEPT 5
#define LINUX_GETSOCKNAME 6
#define LINUX_GETPEERNAME 7
#define LINUX_SOCKETPAIR 8
#define LINUX_SEND 9
#define LINUX_RECV 10
#define LINUX_SENDTO 11
#define LINUX_RECVFROM 12
#define LINUX_SHUTDOWN 13
#define LINUX_SETSOCKOPT 14
#define LINUX_GETSOCKOPT 15
#define LINUX_SENDMSG 16
#define LINUX_RECVMSG 17
#define PAD_(t) (sizeof(syscallarg_t) <= sizeof(t) ? \
0 : sizeof(syscallarg_t) - sizeof(t))
#if BYTE_ORDER == LITTLE_ENDIAN
#define PADL_(t) 0
#define PADR_(t) PAD_(t)
#else
#define PADL_(t) PAD_(t)
#define PADR_(t) 0
#endif
typedef int l_int;
typedef uint32_t l_ulong;
struct linux_socketcall_args {
char what_l_[PADL_(l_int)]; l_int what; char what_r_[PADR_(l_int)];
char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)];
};
void print_syscall(struct trussinfo *);
void print_syscall_ret(struct trussinfo *, int, syscallarg_t *);
void print_summary(struct trussinfo *trussinfo);