Demonstrations of funccount, the Linux eBPF/bcc version.


This program traces functions, tracepoints, or USDT probes that match a
specified pattern, and when Ctrl-C is hit prints a summary of their count
while tracing. Eg, tracing all kernel functions that begin with "vfs_":

# ./funccount 'vfs_*'
Tracing... Ctrl-C to end.
^C
FUNC                          COUNT
vfs_create                        1
vfs_rename                        1
vfs_fsync_range                   2
vfs_lock_file                    30
vfs_fstatat                     152
vfs_fstat                       154
vfs_write                       166
vfs_getattr_nosec               262
vfs_getattr                     262
vfs_open                        264
vfs_read                        470
Detaching...

The above output shows that while tracing the vfs_read() function was called 470
times, and vfs_open() 264 times, etc.

This is useful for exploring kernel code, to figure out which functions are in
use and which are not. This can narrow down an investigation to just a few
functions, whose counts are similar to the workload investigated.


Tracing all tcp functions:

# ./funccount 'tcp_*'
Tracing... Ctrl-C to end.
^C
FUNC                          COUNT
tcp_try_undo_recovery             1
tcp_twsk_destructor               1
tcp_enter_recovery                1
tcp_xmit_retransmit_queue         1
tcp_update_scoreboard             1
tcp_verify_retransmit_hint        1
tcp_tsq_handler.part.31           1
tcp_sacktag_write_queue           1
tcp_match_skb_to_sack             1
tcp_time_wait                     1
tcp_mark_head_lost                1
tcp_init_cwnd_reduction           1
tcp_sacktag_one                   1
tcp_sacktag_walk                  1
tcp_retransmit_skb                1
tcp_tasklet_func                  1
tcp_resume_early_retransmit       1
tcp_dsack_set                     1
tcp_v4_syn_recv_sock              2
tcp_ca_openreq_child              2
tcp_try_fastopen                  2
tcp_openreq_init_rwin             2
tcp_v4_init_req                   2
tcp_create_openreq_child          2
tcp_v4_send_synack                2
tcp_v4_init_sequence              2
tcp_fragment                      2
tcp_v4_conn_request               2
tcp_conn_request                  2
tcp_v4_route_req                  2
tcp_fragment_tstamp               2
tcp_try_keep_open                 2
tcp_v4_reqsk_destructor           2
tcp_may_send_now                  2
tcp_make_synack                   2
tcp_child_process                 2
tcp_check_req                     2
tcp_fastretrans_alert             2
tcp_set_keepalive                 2
tcp_finish_connect                3
tcp_connect_queue_skb             3
tcp_v4_connect                    3
tcp_init_sock                     3
tcp_v4_init_sock                  3
tcp_connect                       3
tcp_any_retrans_done.part.35        3
tcp_clear_retrans                 3
tcp_setsockopt                    4
tcp_update_metrics                5
tcp_done                          5
tcp_initialize_rcv_mss            5
tcp_sndbuf_expand                 5
tcp_fin                           5
tcp_init_xmit_timers              5
tcp_close                         5
tcp_init_congestion_control        5
tcp_init_metrics                  5
tcp_gro_complete                  5
tcp_free_fastopen_req             5
tcp_v4_destroy_sock               5
tcp_cleanup_congestion_control        5
tcp_send_fin                      5
tcp_init_buffer_space             5
tcp_init_cwnd                     5
tcp_select_initial_window         5
tcp_check_oom                     5
tcp_default_init_rwnd             5
tcp_assign_congestion_control        5
tcp_getsockopt                    6
tcp_ioctl                         6
tcp_mtup_init                     8
tcp_parse_options                 8
tcp_mss_to_mtu                    8
tcp_try_rmem_schedule             8
tcp_get_metrics                  10
tcp_try_coalesce                 10
tcp_rcv_state_process            14
tcp_sync_mss                     14
tcp_write_timer_handler          15
tcp_write_timer                  16
tcp_grow_window.isra.27          22
tcp_set_state                    23
tcp_send_ack                     37
tcp_delack_timer                 42
tcp_delack_timer_handler         42
tcp_validate_incoming            91
tcp_prequeue_process            112
tcp_v4_early_demux              117
tcp_gro_receive                 146
tcp_queue_rcv                   167
tcp_data_queue                  215
tcp_urg                         219
tcp_send_delayed_ack            257
tcp_send_mss                    275
tcp_push                        275
tcp_sendmsg                     275
tcp_event_data_recv             275
tcp_nagle_check                 279
tcp_write_xmit                  282
tcp_event_new_data_sent         282
tcp_current_mss                 284
tcp_init_tso_segs               284
tcp_wfree                       286
tcp_schedule_loss_probe         305
tcp_v4_send_check               323
tcp_transmit_skb                323
tcp_recvmsg                     323
tcp_options_write               325
tcp_rcv_space_adjust            328
tcp_check_space                 332
tcp_rcv_established             337
tcp_ack                         337
tcp_parse_aligned_timestamp.part.43      345
tcp_prequeue                    346
tcp_v4_do_rcv                   351
tcp_v4_rcv                      351
tcp_parse_md5sig_option         351
tcp_cleanup_rbuf                436
tcp_poll                        468
tcp_established_options         604
tcp_v4_md5_lookup               615
tcp_release_cb                  736
tcp_rearm_rto                   843
tcp_md5_do_lookup               968
Detaching...

The current implementation can take many seconds to detach from tracing, after
Ctrl-C has been hit.


User functions can be traced in executables or libraries, and per-process
filtering is allowed:

# ./funccount -p 1442 /home/ubuntu/contentions:*
Tracing 15 functions for "/home/ubuntu/contentions:*"... Hit Ctrl-C to end.
^C
FUNC                                           COUNT
main                                               1
_start                                             1
primes_thread                                      2
insert_result                                  87186
is_prime                                     1252772
Detaching...

If /home/ubuntu is in the $PATH, then the following command will also work:

# ./funccount -p 1442 contentions:*


Counting libc write and read calls using regular expression syntax (-r):

# ./funccount -r 'c:(write|read)$'
Tracing 2 functions for "c:(write|read)$"... Hit Ctrl-C to end.
^C
FUNC                                    COUNT
read                                        2
write                                       4
Detaching...


Kernel tracepoints are also available as targets. For example, trace common
block I/O tracepoints and see how often they are invoked:

# ./funccount t:block:*
Tracing 19 functions for "t:block:*"... Hit Ctrl-C to end.
^C
FUNC                                    COUNT
block:block_rq_complete                     7
block:block_rq_issue                        7
block:block_getrq                           7
block:block_rq_insert                       7
Detaching...


Likewise, user-mode statically defined traces (USDT) can also be probed. For
example, count mutex-related events in pthreads:

# ./funccount u:pthread:*mutex* -p 1442
Tracing 7 functions for "u:pthread:*mutex*"... Hit Ctrl-C to end.
^C
FUNC                                    COUNT
mutex_init                                  1
mutex_entry                            547122
mutex_acquired                         547175
mutex_release                          547185
Detaching...


An interval can be provided. Eg, printing output every 1 second for vfs calls:

# ./funccount -i 1 'vfs_*'
Tracing... Ctrl-C to end.

FUNC                          COUNT
vfs_fstatat                       1
vfs_fstat                        16
vfs_getattr_nosec                17
vfs_getattr                      17
vfs_write                        52
vfs_read                         79
vfs_open                         98

FUNC                          COUNT
vfs_fstatat                      10
vfs_fstat                        10
vfs_open                         13
vfs_getattr_nosec                20
vfs_getattr                      20
vfs_write                        28
vfs_read                         39

FUNC                          COUNT
vfs_fsync_range                   2
vfs_lock_file                    30
vfs_write                       107
vfs_fstatat                     129
vfs_fstat                       130
vfs_open                        154
vfs_getattr_nosec               222
vfs_getattr                     222
vfs_read                        384
^C
Detaching...

This can be useful for making some ad hoc tools, exposing new counts of
kernel activity that aren't visible in other metrics.

Include -T to print timestamps on output.


A maximum duration can be set. For example, to print 5 x 1 second summaries
of vfs_read() calls:

# ./funccount -i 1 -d 5 vfs_read
Tracing 1 functions for "vfs_read"... Hit Ctrl-C to end.

FUNC                                    COUNT
vfs_read                                   30

FUNC                                    COUNT
vfs_read                                   26

FUNC                                    COUNT
vfs_read                                   54

FUNC                                    COUNT
vfs_read                                   25

FUNC                                    COUNT
vfs_read                                   31
Detaching...

By leaving off the "-i 1", this will print a single 5 second summary:

# funccount.py -d 5 vfs_read
Tracing 1 functions for "vfs_read"... Hit Ctrl-C to end.

FUNC                                    COUNT
vfs_read                                  167
Detaching...

This can be useful for finding out rates: trace all functions for ten seconds
and then divide by ten for the per-second rate.


The "*" wildcard can be used multiple times. Eg, matching functions that contain
the word "readdir":

# ./funccount '*readdir*'
Tracing... Ctrl-C to end.
^C
FUNC                          COUNT
ext4_readdir                      4
Detaching...

Matching "tcp" then "send":

# ./funccount '*tcp*send*'
Tracing... Ctrl-C to end.
^C
FUNC                          COUNT
tcp_send_ack                      4
tcp_send_delayed_ack             19
tcp_send_mss                     26
tcp_sendmsg                      26
tcp_v4_send_check                30
__tcp_v4_send_check              30
Detaching...


Full USAGE:

# ./funccount -h
usage: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] [-D]
                    pattern

Count functions, tracepoints, and USDT probes

positional arguments:
  pattern               search expression for events

optional arguments:
  -h, --help            show this help message and exit
  -p PID, --pid PID     trace this PID only
  -i INTERVAL, --interval INTERVAL
                        summary interval, seconds
  -d DURATION, --duration DURATION
                        total duration of trace, seconds
  -T, --timestamp       include timestamp on output
  -r, --regexp          use regular expressions. Default is "*" wildcards
                        only.
  -D, --debug           print BPF program before starting (for debugging
                        purposes)

examples:
    ./funccount 'vfs_*'             # count kernel fns starting with "vfs"
    ./funccount -r '^vfs.*'         # same as above, using regular expressions
    ./funccount -Ti 5 'vfs_*'       # output every 5 seconds, with timestamps
    ./funccount -d 10 'vfs_*'       # trace for 10 seconds only
    ./funccount -p 185 'vfs_*'      # count vfs calls for PID 181 only
    ./funccount t:sched:sched_fork  # count calls to the sched_fork tracepoint
    ./funccount -p 185 u:node:gc*   # count all GC USDT probes in node, PID 185
    ./funccount c:malloc            # count all malloc() calls in libc
    ./funccount go:os.*             # count all "os.*" calls in libgo
    ./funccount -p 185 go:os.*      # count all "os.*" calls in libgo, PID 185
    ./funccount ./test:read*        # count "read*" calls in the ./test binary
