argdist(8)

๐Ÿ“ฆ version 0.36.1, in centos-stream 10, centos-stream 9, fedora 43, fedora 44

โœจ argdist(8) โ€” bestie's guide to argdist โœจ

๐ŸŒธ NAME ๐ŸŒธ

argdist - Okay so this one traces a function and shows you a histogram or frequency count of its parameter values. It's Linux eBPF/bcc, so it's giving low-level main character energy ๐Ÿ’…

๐ŸŽ€ SYNOPSIS ๐ŸŽ€

argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-d DURATION] [-n COUNT] [-v] [-T TOP] [-H specifier] [-C specifier] [-I header] [-t TID]

๐Ÿ’– DESCRIPTION ๐Ÿ’–

Okay bestie, argdist attaches to function entry and exit points, grabs the parameter values you care about, and stashes them in either a histogram or a frequency collection. The frequency one counts how many times each parameter value showed up ๐Ÿ“Š It can also filter the values, and it can instrument multiple entry points at once โœจ

Since this runs on BPF, only the root user can use this tool. No root, no argdist, sorry bestie ๐Ÿ™ˆ

๐Ÿงš REQUIREMENTS ๐Ÿงš

You'll need CONFIG_BPF and bcc. Make sure you've got both before you try to slay with it ๐Ÿ”ฅ

๐Ÿ“ OPTIONS ๐Ÿ“

  • -h โ€” Prints the usage message ๐Ÿ“–
  • -p PID โ€” Only traces functions in the process PID ๐ŸŽฏ
  • -t TID โ€” Only traces functions in the thread TID ๐Ÿงต
  • -z STRING_SIZE โ€” When you're collecting string arguments (type char*), it grabs up to STRING_SIZE characters. Anything longer gets truncated, so it's not gonna be the full tea ๐Ÿต
  • -i INTERVAL โ€” Prints the collected data every INTERVAL seconds. The default is 1 second โฑ๏ธ
  • -d DURATION โ€” The total duration of the trace, in seconds โŒ›
  • -n NUMBER โ€” Prints the collected data COUNT times and then exits. Bye bye ๐Ÿ‘‹
  • -v โ€” Shows the generated BPF program, for debugging purposes. Nerd mode activated ๐Ÿค“
  • -T TOP โ€” When you're collecting frequency counts, it only displays the top TOP entries. Only the best of the best ๐Ÿ‘‘
  • -H specifiers, -C specifiers โ€” One or more probe specifications that tell argdist which functions to probe, which parameters to collect, how to aggregate them, and whether to filter anything. See SPECIFIER SYNTAX below ๐Ÿ’…
  • -I header โ€” One or more header files to include in the BPF program. This lets you use structure definitions, enumerations, and constants from those headers. Use the same path you'd include in the BPF program, like 'linux/blkdev.h' or 'linux/time.h'. Note: in lots of cases, argdist will figure out the needed header files on its own ๐Ÿซถ

๐Ÿฆ‹ SPECIFIER SYNTAX ๐Ÿฆ‹

Okay so the general specifier syntax looks like this, and yes it's a lot, but we're gonna break it down together ๐Ÿฅน

{p,r,t,u}:{[library],category}:function(signature):type[,type...]:expr[,expr...][:filter]][#label]

{p,r,t,u}

This is the probe type. p is for function entry, r is for function return, t is for a kernel tracepoint, and u is for a USDT probe. It's also where you pick -H for a histogram or -C for a frequency count. It tells argdist where to put the probe and whether to collect a frequency count or stuff the values into a histogram. Counting probes record how many times each parameter value was seen, while histogram probes put the parameter values into a histogram. Only integral types work with histogram probes. Counting probes have no such limit, so they're more chill ๐Ÿ˜Œ

[library]

This is the library that contains the probe. Give the full path to the .so or executable where the function lives. Or, if you're lazy (no judgment), just give the lib name. For example, "c" means libc. If you don't name a library, argdist assumes the kernel ๐Ÿง

category

The category of the kernel tracepoint. For example: net, sched, block ๐Ÿ—‚๏ธ

function(signature)

The function to probe, plus its signature. The function name has to match exactly or the probe won't get placed, so no typos bestie ๐Ÿ™…. The signature is only needed if you want to collect parameter values based on it. If you only want the first parameter, you don't have to list the rest. For kernel tracepoints, this should be the name of the event, like net_dev_start_xmit, and the signature should be empty. For USDT probes, this should be the name of the probe, like reloc_complete, and the signature should also be empty ๐Ÿซ 

[type[,type...]]

The type(s) of the expression(s) you're capturing. These are the types of the keys in the histogram or raw event collection the probes collect ๐Ÿ”‘

[expr[,expr...]]

The expression(s) to capture. These are the values that get assigned to the histogram or raw event collection. You can use the parameters directly, or valid C expressions that use them, like "size % 10". Tracepoints can access a special structure called "args", which is formatted according to the tracepoint format (you can grab that with tplist). For example, the block:block_rq_complete tracepoint can access args->nr_sector. USDT probes can access the arguments the tracing program defines through the special arg1, arg2, ... variables. To find their types, use the tplist tool ๐Ÿง

Return probes can use the argument values the function received when it was entered, through the $entry(paramname) special variable. They can also access the function's return value in $retval, and the function's execution time in nanoseconds in $latency. Heads up though: adding $latency or $entry(paramname) to the expression adds an extra probe at the function's entry to collect that data, so it adds extra overhead ๐Ÿข

[filter]

The filter applied to the captured data. Only parameter values that pass the filter get collected. It's any valid C expression that refers to the parameter values, like "fd == 1 && length > 16". The $entry, $retval, and $latency variables work here too, in return probes. The filter can also use the STRCMP pseudo-function to compare a predefined string to a string argument, like STRCMP("test.txt", file). Argument order matters here: the first argument MUST be a quoted literal string, and the second can be a runtime string ๐Ÿงถ

[label]

The label that shows up when the probed values get printed. By default, it's the probe specifier itself ๐Ÿท๏ธ

๐ŸŽจ EXAMPLES ๐ŸŽจ

Okay, now the fun part! Here are the examples, bestie ๐Ÿฅณ

Print a histogram of allocation sizes passed to kmalloc:

# argdist -H 'p::__kmalloc(u64 size):u64:size'

Print a count of how many times process 1005 called malloc with an allocation size of 16 bytes:

# argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16'

Snoop on all strings returned by gets():

# argdist -C 'r:c:gets():char*:$retval'

Print a histogram of read sizes that were longer than 1ms:

# argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t:$entry(count):$latency > 1000000'

Print frequency counts of how many times writes were issued to a particular file descriptor number, in process 1005:

# argdist -p 1005 -C 'p:c:write(int fd):int:fd'

Print a histogram of error codes returned by read() in process 1005:

# argdist -p 1005 -H 'r:c:read()'

Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT):

# argdist -H 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1'

Count fork() calls in libc across all processes, grouped by pid:

# argdist -C 'p:c:fork():int:$PID;fork per process'

Print a histogram of the number of sectors in completing block I/O requests:

# argdist -H 't:block:block_rq_complete():u32:nr_sector'

Aggregate interrupts by interrupt request (IRQ):

# argdist -C 't:irq:irq_handler_entry():int:irq'

Print the functions used as thread entry points and how common they are:

# argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337

Print histograms of sleep() and nanosleep() parameter values:

# argdist -H 'p:c:sleep(u32 seconds):u32:seconds' -H 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec'

Spy on writes to STDOUT performed by process 2780, up to a string size of 120 characters:

# argdist -p 2780 -z 120 -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1'

Group files being read from and the read sizes from __vfs_read:

# argdist -C 'p::__vfs_read(struct file *file, void *buf, size_t count):char*,size_t:file->f_path.dentry->d_iname,count:file->f_path.dentry->d_iname[0]!=0'

๐ŸŒˆ SOURCE ๐ŸŒˆ

This one's from bcc ๐Ÿซง

https://github.com/iovisor/bcc

Also, go check the bcc distribution for a companion _examples.txt file. It has example usage, output, and commentary for this tool, and it's honestly so helpful ๐Ÿ“š

๐Ÿฆ„ OS ๐Ÿฆ„

Linux ๐Ÿง

๐Ÿ”ฎ STABILITY ๐Ÿ”ฎ

Unstable - in development. So it's a work in progress, bestie ๐Ÿšง

๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Sasha Goldshtein ๐Ÿ’œ


Okay bestie, that's the whole tea โ˜• This page is from the USER COMMANDS section, dated 2016-02-11, and it's argdist(8) being iconic as always ๐Ÿฉทโœจ

๐Ÿ“ฆ where this page comes from