Page MenuHomeFreeBSD

truss: add -t to select which system calls are reported
AcceptedPublic

Authored by dteske on Sun, Aug 30, 6:53 PM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Aug 31, 9:03 AM
Unknown Object (File)
Mon, Aug 31, 2:23 AM
Unknown Object (File)
Mon, Aug 31, 2:14 AM
Unknown Object (File)
Sun, Aug 30, 10:47 PM
Unknown Object (File)
Sun, Aug 30, 10:39 PM
Subscribers

Details

Reviewers
adrian
emaste
fuz
Group Reviewers
manpages
Summary

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

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 76335
Build 73218: arc lint + arc unit

Event Timeline

Great work! I like it!

usr.bin/truss/main.c
145

This should exit with a nonzero status so that callers doing truss -t $filter where filter ends up unset get a loud failure instead of garbage output.

usr.bin/truss/syscall_filter.c
209
333

Something is buggy with this logic. truss -t "" fails with “empty term in -t” for me, but should just set up no filters.

usr.bin/truss/main.c
145

Good catch. Thanks. I'll change this to return (1);

usr.bin/truss/syscall_filter.c
209

Good catch, I missed that

usr.bin/truss/syscall_filter.c
333

Discussed offline -- we agree this should silently succeed and act as a nop

Other points we discussed to implement:

  1. -t call1,,,,call2 should not produce the empty set error. Just silently ignore empty elements between comma
  2. Because SunOS/Illumos/Solaris have -t [!]syscall[,...] but treat ! differently that we do, we should document in our man-page that ! is for the single comma-separated element only, not a modifier carried-forward to later comma-separated elements
  3. Implement a @none which maps to !@all
  4. Add tests for @none and !@none
  5. Add test for -t ''
  6. Make "truss -t" return 2 so it is unique from errors (that return 1)
usr.bin/truss/main.c
145

Correction. Based on offline discussion, you said you'd like to see return (2); here to make it distinct. I like that idea.

Also worth mentioning, we can prevent a level of astonishment if we issue a warning if a named syscall is unknown.

Also going to make a shot at supporting syscalls by-number, with validation

Implement what was discussed offline and mentioned as comments above

dteske marked 3 inline comments as done.

I've seen senior colleagues use truss but I'm not advanced enough in that area to use it myself, so I have no frame of reference on the "if we should do this" side of things.

usr.bin/truss/truss.1
83

Since you can specify -t without an expression to list the groups it understands.

197–201

I feel like this needs to be in the first paragraph (like line 80s) somehow, if you want to do this.

usr.bin/truss/truss.1
83

That's not quite accurate. You cannot, for example, say:

truss -c -t /bin/ls

Because then /bin/ls will become the argument to -t

The optionality of argument to -t is predicated on no-expression. That is why I have placed the example of truss -t separate from synopsis showing expressions.

usr.bin/truss/truss.1
83

I should add, that even if I wanted to be cute and add some kind of detection as to when the positional argument that follows -t does not conform to the expected opt-arg, that would create conflicts that we don't want to exist. For example:

truss -t readlink

It's not clear whether someone wants to pass readlink as the name of the syscall and the command-to-execute was missing, or if no argument was given to -t and the command-to-execute given is in-fact readlink.

That would also make it impossible to make any command that collides with the name of a syscall.

Not to mention, that if the intent was in-fact to pass no argument to -t to get the list go groups, that passing a command-to-execute would be unneeded in that case.

Sorry if this was too much information. Just playing devil's advocate by thinking-through all the possibilities.

usr.bin/truss/truss.1
197–201

Good call. I'll make it happen.

Improve truss(1) man-page (ziaee)

dteske marked an inline comment as done.

I think this looks good! More features can always be added in subsequent iterations.

This revision is now accepted and ready to land.Tue, Sep 1, 2:25 PM