KSNOOP(8)

๐Ÿ“ฆ version 0.36.1, in alpine 3.24, nixpkgs unstable, opensuse-leap 16.0

๐Ÿ” KSNOOP(8) ๐Ÿ” ksnoop: the kernel function snoop ๐Ÿฆ‹

NAME โœจ

KSNOOP is a tool for tracing kernel function entry and return. It shows the arguments and return values, bestie ๐Ÿ’–


๐ŸŽ€ SYNOPSIS ๐ŸŽ€

ksnoop [OPTIONS] { COMMAND  FUNC | help }

OPTIONS := { { -V | --version } | { -h | --help } | { [-P | --pages]
nr_pages} | { [-p | --pid] pid} | [{ -s | --stack }] | [{ -d |
--debug }] }

COMMAND := { trace | info }

FUNC := { name | name(arg[,**arg]) }

๐Ÿ’… DESCRIPTION ๐Ÿ’…

okay so ksnoop lets you inspect the arguments and return values that go along with function entry and return. it's giving detective energy ๐Ÿ•ต๏ธโ€โ™€๏ธโœจ

๐ŸŒธ Commands ๐ŸŒธ

  • ksnoop info FUNC โ€” show the function description, its arguments, and the return value types. Very informative, very useful ๐Ÿง 
  • ksnoop trace FUNC [FUNC] โ€” trace function entry and return, and show the arguments and return values. You can just give a function name, or a function name along with named arguments and return values. return is the keyword you use to grab the return value ๐Ÿฆ‹

๐Ÿซถ Requirements ๐Ÿซถ

ksnoop needs the kernel to provide BTF for itself. If you want to trace module data, module BTF has to be there too. Check /sys/kernel/btf to see if BTF is present ๐Ÿ‘€

ksnoop also needs the CAP_BPF and CAP_TRACING capabilities. no cap, you need these or it won't work ๐Ÿ’€


๐ŸŒˆ OPTIONS ๐ŸŒˆ

  • -h, --help โ€” shows the help info ๐Ÿ™‹โ€โ™€๏ธ
  • -V, --version โ€” shows the version ๐Ÿ”–
  • -d, --debug โ€” shows debug output, for when you want to see behind the curtain ๐Ÿ›
  • -p, --pid โ€” filters events by pid, so you only see the process you care about ๐ŸŽฏ
  • -P, --pages โ€” specifies the number of pages used per-CPU for perf event collection, as nr_pages. The default is 8. โœจ
  • -s, --stack โ€” the set of functions you name gets traced if and only if they're hit in the order you listed them. the main character order ๐Ÿ‘‘

๐Ÿฆ‹ EXAMPLES ๐Ÿฆ‹

๐Ÿงš Show a function's description

# ksnoop info ip_send_skb

   int  ip_send_skb(struct net  * net, struct sk_buff  * skb);

Shows the function description for ip_send_skb. ๐Ÿ’–

๐Ÿ“ Trace entry and return

# ksnoop trace ip_send_skb

            TIME  CPU      PID FUNCTION/ARGS
78101668506811    1     2813 ip_send_skb(
                                   net = *(0xffffffffb5959840)
                                    (struct net){
                                     .passive = (refcount_t){
                                      .refs = (atomic_t){
                                       .counter = (int)0x2,
                                      },
                                     },
                                     .dev_base_seq = (unsigned int)0x18,
                                     .ifindex = (int)0xf,
                                     .list = (struct list_head){
                                      .next = (struct list_head *)0xffff9895440dc120,
                                      .prev = (struct list_head *)0xffffffffb595a8d0,
                                     },
                                   ...

79561322965250    1     2813 ip_send_skb(
                                   return =
                                    (int)0x0
                               );

Shows entry and return for ip_send_skb(), with the arguments and return values. the whole vibe ๐ŸŒธ

๐Ÿซง Trace one argument

# ksnoop trace "ip_send_skb(skb)"

           TIME  CPU      PID FUNCTION/ARGS
78142420834537    1     2813 ip_send_skb(
                                   skb = *(0xffff989750797c00)
                                    (struct sk_buff){
                                     (union){
                                      .sk = (struct sock *)0xffff98966ce19200,
                                      .ip_defrag_offset = (int)0x6ce19200,
                                     },
                                     (union){
                                      (struct){
                                       ._skb_refdst = (long unsigned int)0xffff98981dde2d80,
                                       .destructor = (void (*)(struct sk_buff *))0xffffffffb3e1beb0,
                                      },
                                  ...

Shows the entry argument skb. just the one you wanted ๐ŸŽ€

๐Ÿ”ฎ Trace only the return value

# ksnoop trace "ip_send_skb(return)"

           TIME  CPU      PID FUNCTION/ARGS
78178228354796    1     2813 ip_send_skb(
                                   return =
                                    (int)0x0
                               );

Shows the return value from ip_send_skb(). โœจ

๐Ÿฉท Trace a member of an argument

# ksnoop trace "ip_send_skb(skb->sk)"

            TIME  CPU      PID FUNCTION/ARGS
78207649138829    2     2813 ip_send_skb(
                                   skb->sk = *(0xffff98966ce19200)
                                    (struct sock){
                                     .__sk_common = (struct sock_common){
                                      (union){
                                       .skc_addrpair = (__addrpair)0x1701a8c017d38f8d,
                                       (struct){
                                        .skc_daddr = (__be32)0x17d38f8d,
                                        .skc_rcv_saddr = (__be32)0x1701a8c0,
                                       },
                                      },
                                    ...

Traces the member information tied to an argument. heads up, only one level of membership is supported, so no digging three layers deep ๐Ÿ™ˆ

๐Ÿ’œ Filter by pid and trace an argument

# ksnoop -p 2813 "ip_rcv(dev)"

            TIME  CPU      PID FUNCTION/ARGS
78254803164920    1     2813 ip_rcv(
                                   dev = *(0xffff9895414cb000)
                                    (struct net_device){
                                     .name = (char[16])[
                                      'l',
                                      'o',
                                     ],
                                     .name_node = (struct netdev_name_node *)0xffff989541515ec0,
                                     .state = (long unsigned int)0x3,
                                   ...

Traces the dev argument of ip_rcv(). Passing -p 2813 means you only get events from process id 2813. focused ๐ŸŽฏ

๐Ÿ‘‘ Trace functions in call order

# ksnoop -s tcp_sendmsg __tcp_transmit_skb  ip_output

           TIME  CPU      PID FUNCTION/ARGS
71827770952903    1     4777 __tcp_transmit_skb(
                                   sk = *(0xffff9852460a2300)
                                    (struct sock){
                                     .__sk_common = (struct sock_common){
                                      (union){
                                       .skc_addrpair = (__addrpair)0x61b2af0a35cbfe0a,

Traces entry and return of tcp_sendmsg, __tcp_transmit_skb and ip_output, but only when tcp_sendmsg leads to a call to __tcp_transmit_skb, and that in turn leads to a call to ip_output. so the call graph has to match the order you gave. The order doesn't have to be direct calls, either: function A can call some other function that then calls function B. the chain just has to exist ๐Ÿ”—

๐ŸŒท Filter with a condition

# ksnoop "ip_send_skb(skb->len > 100, skb)"

            TIME  CPU      PID FUNCTION/ARGS
39267395709745    1     2955 ip_send_skb(
                                   skb->len =
                                    (unsigned int)0x89,
                                   skb = *(0xffff89c8be81e500)
                                    (struct sk_buff){
                                     (union){
                                      .sk = (struct sock *)0xffff89c6c59e5580,
                                      .ip_defrag_offset = (int)0xc59e5580,
                                     },

Traces ip_send_skb() for skbs where len is greater than 100. ๐Ÿงƒ


๐Ÿ”— SEE ALSO ๐Ÿ”—


Thanks for hanging with me through the whole KSNOOP(8) page, bestie ๐Ÿซถ๐Ÿ’– Source and version details are all tucked in the footer, and that's a wrap! ๐Ÿ’…โœจ