UFTRACE-SCRIPT(1)

🌸 uftrace-script β€” bestie edition πŸ’–

okay so this is a page from the General Commands Manual, and the star of the show is uftrace-script. That's the tool that runs a lil script over your recorded function trace. Get comfy, we're doing this together 🫢


✨ NAME ✨

uftrace-script β€” run a script for recorded function trace. yes that's the whole vibe, it's literally that πŸ’…


✨ SYNOPSIS ✨

uftrace script (-S|–script) [options] uftrace script (-S|–script)

[options] –record COMMAND

✨ DESCRIPTION ✨

So! This command runs a script on trace data that you already recorded with the uftrace-record(1) command. That's it, that's the job. You bring the data, she brings the script. iconic behavior tbh πŸ’–


πŸŽ€ SCRIPT OPTIONS πŸŽ€

  • -S SCRIPT_PATH, --script=SCRIPT_PATH β€” run the script you hand her, so it can do extra work at the entry and exit of a function while she's chewing through the recorded trace data. How does she know what kind of script it is? File extension, bestie. Like .py for python and .lua for lua 5.1. Go read SCRIPT EXECUTION for the deets ✨
  • --record COMMAND [command-options] β€” records a whole new trace before running the script you gave her. two things at once, main character energy πŸ‘‘
  • --no-args β€” do not show function arguments and return value. keep it mysterious πŸ™ˆ

πŸ’œ COMMON OPTIONS πŸ’œ

  • -F FUNC, --filter=FUNC β€” set a filter to trace selected functions and their children functions. you can use this option more than once, she doesn't mind. uftrace-replay(1) explains what filters even are.
  • -N FUNC, --notrace=FUNC β€” opposite vibe: set a filter to not trace selected functions and their children functions. also usable more than once. again, uftrace-replay(1) for the filter lore.
  • -C FUNC, --caller-filter=FUNC β€” set a filter to trace callers of selected functions only. use it more than once if you want. uftrace-replay(1) has the explanation, as always.
  • -T TRG, --trigger=TRG β€” set a trigger on selected functions. usable more than once. uftrace-replay has the details, go look πŸ‘€
  • -D DEPTH, --depth DEPTH β€” set the trace limit in nesting level. don't go too deep, it gets scary down there πŸ’€
  • -t TIME, --time-filter=TIME β€” do not run the script for functions that run under the time threshold. but! if some functions explicitly have the trace trigger applied, those are always traced regardless of execution time. no skipping those, periodt.
  • -Z SIZE, --size-filter=SIZE β€” do not show functions smaller than SIZE bytes. small functions found shaking 😭
  • -L LOCATION, --loc-filter=LOCATION β€” set a filter to trace selected source locations. more than once? yes ma'am, allowed.
  • --no-libcall β€” do not run the script for library calls. skip ✨
  • --match=TYPE β€” use pattern match with TYPE. possible types are regex and glob. default is regex.
  • --with-syms=DIR β€” read symbol data from the .sym files in the DIR directory instead of from the binary. super useful for dealing with stripped binaries. important tho: the file name of the main binary should be the same when saved and used. don't rename things mid-way, she will not know who anyone is 🫠

🌈 COMMON ANALYSIS OPTIONS 🌈

  • -H FUNC, --hide=FUNC β€” set a filter to not trace selected functions. key difference: it doesn't affect their subtrees, it hides only the given functions. use it more than once. uftrace-replay(1) explains filters if you're lost.
  • --kernel-full β€” run the script on all kernel functions and events that occurred outside of user functions. the whole kernel situation, no cap.
  • --kernel-only β€” run the script on kernel functions only, no user functions at all. strictly kernel business πŸ‘‘
  • --tid=TID[,TID,...] β€” run the script only for functions called by the given tasks. wanna see the list of tasks in the data file? use uftrace report --task or uftrace info. and yes, this option can also be used more than once.
  • --demangle=TYPE β€” use demangled C++ symbol names for filters, triggers, arguments and/or return values. possible values are β€œfull”, β€œsimple” and β€œno”. default is β€œsimple”, which ignores function arguments and template parameters. cute and simple, love that for her πŸ’…
  • -r RANGE, --time-range=RANGE β€” run the script only for functions executed within the time RANGE. the RANGE can be <start>~<stop> (separated by β€œ~”) and one of <start> and <stop> can be omitted. <start> and <stop> are timestamp or elapsed time if they have a <time_unit> postfix, for example 100us. and you can see that timestamp or elapsed time with the -f time or -f elapsed option respectively in uftrace replay(1). so many units, so little time πŸ¦‹

🧚 SCRIPT EXECUTION 🧚

okay this is the fun part, sit down.

The uftrace tool supports script execution for each function entry and exit. The supported script types are Python 2.7, Python 3 and Lua 5.1 as of now. (yes, Python 2.7 is still invited to the party, we don't judge here.)

The user can write five functions. uftrace_entry and uftrace_exit are executed whenever each function is executed at the entry and exit. However uftrace_begin and uftrace_end are only executed once when the target program begins and ends. uftrace_event is called when it sees an event. five besties, five jobs 🌸

$ cat scripts/simple.py
def uftrace_begin(ctx):
    print("program begins...")

def uftrace_entry(ctx):
    func = ctx["name"]
    print("entry : " + func + "()")

def uftrace_exit(ctx):
    func = ctx["name"]
    print("exit  : " + func + "()")

def uftrace_event(ctx):
    name = ctx["name"]
    print("event : " + name)

def uftrace_end():
    print("program is finished")

The ctx variable is a dictionary type that contains the below information. yes, she's handing you the whole dictionary, take it 🎁

/* context information passed to uftrace_entry(ctx) and uftrace_exit(ctx) */
script_context = {
    int       tid;
    int       depth;
    long      timestamp;
    long      duration;    # exit only
    long      address;
    string    name;
    list      args;        # entry only (if available)
    value     retval;      # exit  only (if available)
};

/* context information passed to uftrace_begin(ctx) */
script_context = {
    bool      record;      # True if it runs at record time, otherwise False
    string    version;     # uftrace version info
    list      cmds;        # execution commands
};

The above script can be executed while reading the recorded data. The usage is as follows. look how clean the output is, I'm obsessed πŸ˜­πŸ’–

$ uftrace record -F main tests/t-abc

$ uftrace script -S scripts/simple.py
program begins...
entry : main()
entry : a()
entry : b()
entry : c()
entry : getpid()
exit  : getpid()
exit  : c()
exit  : b()
exit  : a()
exit  : main()
program is finished

The below is another example that shows the different output compared to previous one for the same recorded data. The output looks similar to uftrace replay this time. same data, different fit, she contains multitudes ✨

$ uftrace script -S scripts/replay.py
# DURATION    TID     FUNCTION
            [25794] | main() {
            [25794] |   a() {
            [25794] |     b() {
            [25794] |       c() {
            [25794] |         getpid() {
      11.037 us [25794] |         } /* getpid */
      44.752 us [25794] |       } /* c */
      70.924 us [25794] |     } /* b */
      98.191 us [25794] |   } /* a */
     124.329 us [25794] | } /* main */

The script above can be modified to do more output customization. go wild, customize away πŸŽ€

A script can have an optional β€œUFTRACE_FUNCS” list which can have name (or pattern depending on the –match option) of functions to run the script. If it exists, only matched functions will run the script. For example, if you add following lines to the script, it will run only for functions with a single letter name. one letter names only, a-listers only πŸ‘‘

$ echo 'UFTRACE_FUNCS = [ "^.$" ]' >> replay.py
$ uftrace script -S replay.py
# DURATION    TID     FUNCTION
            [25794] |   a() {
            [25794] |     b() {
            [25794] |       c() {
      44.752 us [25794] |       } /* c */
      70.924 us [25794] |     } /* b */
      98.191 us [25794] |   } /* a */

Also a script can have options for record if it requires some form of data (i.e. function argument or return value). A comment line started with β€œuftrace-option:” will provide (a part of) such options when recording. yes, your script can place an order πŸ“

$ cat arg.py
#
# uftrace-option: -A a@arg1 -R b@retval
#
def uftrace_entry(ctx):
    if "args" in ctx:
        print(ctx["name"] + " has args")
def uftrace_exit(ctx):
    if "retval" in ctx:
        print(ctx["name"] + " has retval")

$ uftrace record -S arg.py abc
a has args
b has retval
$ uftrace script -S arg.py
a has args
b has retval

Also a script can handle event records and optional event data in β€œargs”. The argument is a string containing KEY=VALUE pairs. Currently it only works with uftrace script and not for record. heads up, this is a script-only situation, no cap. πŸ”₯

$ cat event.py
def uftrace_entry(ctx):
    pass
def uftrace_exit(ctx):
    pass
def uftrace_event(ctx):
    if "args" in ctx:
        print(ctx["name"] + " ::: " + ctx["args"])
    else:
        print(ctx["name"])

$ uftrace record -T a@read=proc/statm abc
$ uftrace script -S event.py
read:proc/statm ::: vmsize=31060KB vmrss=15412KB shared=11064KB
diff:proc/statm ::: vmsize=+0KB vmrss=+0KB shared=+0KB

πŸ’« SEE ALSO πŸ’«

uftrace(1), uftrace-record(1), uftrace-replay(1), uftrace-live(1)

go read the whole family, they're all slaying 🫢


πŸ‘‘ AUTHORS πŸ‘‘

Honggyu Kim <honggyu.kp@gmail.com>, Namhyung Kim <namhyung@gmail.com>

thank you kings for your service πŸ«‘πŸ’–


and that's the page bestie!! this came out of the Uftrace User Manuals, dated Sep, 2018. yeah she's an older queen but she still ate, fr fr. mwah πŸ’…βœ¨πŸŒΈπŸ’–

πŸ“¦ where this page comes from