NAME printf
PROG i:ms:1 { printf("hi!\n"); exit();}
EXPECT hi!

NAME printf_long_fmt
PROG i:ms:1 { printf("hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz!\n"); exit();}
EXPECT hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz!

NAME printf_argument
PROG i:ms:1 { printf("value: %dms100\n", 100); exit();}
EXPECT value: 100ms100

NAME printf_llu
PROG begin { @start = nsecs; } i:s:1 { printf("Elapsed time: %llus\n", (nsecs - @start)/1000000000); clear(@start); }
EXPECT Elapsed time: 1s

NAME printf_argument_alignment
RUN {{BPFTRACE}} -e 'struct Foo { int a; char b[10]; } uprobe:testprogs/uprobe_test:uprobeFunction2 { $foo = (struct Foo *)arg0; $foo2 = (struct Foo *)arg1; printf("%d %s %d %s\n", $foo->a, $foo->b, $foo2->a, $foo2->b) }' -c ./testprogs/uprobe_test
EXPECT 123 hello 456 world

NAME printf_more_arguments
PROG begin { printf("%dst: %sa; %dnd: %sb;; %drd: %sc;;; %dth: %sd;;;;\n", 1, "a", 2, "ab", 3, "abc", 4, "abcd");  }
EXPECT 1st: aa; 2nd: abb;; 3rd: abcc;;; 4th: abcdd;;;;

NAME printf_length_modifiers
PROG begin { $x = 0x12345678abcdef; printf("%hhx %hx %x %jx\n", $x, $x, $x, $x);  }
EXPECT ef cdef 78abcdef 12345678abcdef

NAME printf_char
PROG begin { printf("%c%c%c%c\n", 0x41, 0x42, 0x43, 0x44);  }
EXPECT ABCD

NAME printf_enum_symbolize
PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; begin { printf("%d %s %d %s %d %s\n", ONE, ONE, TWO, TWO, OTHER, OTHER); exit() }
EXPECT 1 ONE 2 TWO 99999 OTHER

NAME printf_enum_symbolize_var
PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; begin { $one = ONE; $two = TWO; $other = OTHER; printf("%d %s %d %s %d %s\n", $one, $one, $two, $two, $other, $other); exit() }
EXPECT 1 ONE 2 TWO 99999 OTHER

NAME printf_enum_symbolize_map
PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; begin { @one = ONE; @two = TWO; @other = OTHER; printf("%d %s %d %s %d %s\n", @one, @one, @two, @two, @other, @other); exit() }
EXPECT 1 ONE 2 TWO 99999 OTHER

NAME printf_enum_symbolize_width
PROG enum Foo { A, B, C, }; begin { printf("%-5s %5s %s\n", A, B, C); exit() }
EXPECT A         B C

NAME printf_enum_symbolize_tracepoint
PROG tracepoint:skb:kfree_skb { $r = args->reason; printf("%d %s\n", args->reason, $r); exit() }
EXPECT_REGEX ^\d+ [A-Z_]+$
SETUP ip link set lo up
AFTER ping localhost -c 5
TIMEOUT 5

NAME printf_enum_symbolize_cast
PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; begin { printf("%d %s\n", ONE, (enum Foo)1); exit() }
EXPECT 1 ONE

NAME printf_invalid_enum_symbolize_cast
PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; begin { $x = 100; printf("%d %s\n", ONE, (enum Foo)$x); exit() }
EXPECT 1 100

NAME printf_lead_zeros
PROG begin { printf("%5s %04x", "ba", 2); }
EXPECT    ba 0002

NAME errorf
PROG begin { errorf("Something bad with args: %d, %s", 10, "arg2"); }
EXPECT stdin:1:9-62: ERROR: Something bad with args: 10, arg2

# Just a quick smoke test to show that errorf works
# the same as printf
NAME errorf_length_modifiers
PROG begin { $x = 0x12345678abcdef; errorf("%hhx %hx %x %jx\n", $x, $x, $x, $x);  }
EXPECT stdin:1:32-75: ERROR: ef cdef 78abcdef 12345678abcdef

NAME errorf_macro_expansion
PROG macro bad() { errorf("Something bad"); } begin { bad(); }
EXPECT stdin:1:15-38: ERROR: Something bad
EXPECT stdin:1:50-55: ERROR: expanded from
EXPECT macro bad() { errorf("Something bad"); } begin { bad(); }

NAME time
PROG i:ms:1 { time("%H:%M:%S\n"); exit();}
EXPECT_REGEX [0-9]*:[0-9]*:[0-9]*

NAME time_short
PROG i:ms:1 { time("%H-%M:%S\n"); exit();}
EXPECT_REGEX [0-9]*-[0-9]*

NAME join
RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args.argv); exit();}'
EXPECT _A_

NAME join_delim
RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args.argv, ","); exit();}'
EXPECT _A_

NAME str
PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();}
AFTER ./testprogs/syscall execve ./testprogs/true
EXPECT_REGEX P: /*.

NAME str_data_loc
PROG t:sched:sched_process_exec { printf("P: %s\n", str(args.filename)); }
AFTER ./testprogs/syscall execve ./testprogs/true
EXPECT_REGEX P: .*testprogs/true
TIMEOUT 1

NAME str_truncated
PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();}
ENV BPFTRACE_MAX_STRLEN=5
AFTER ./testprogs/syscall execve ./testprogs/true &>/dev/null
EXPECT_REGEX P: /....\.\.

NAME explicit string truncation
PROG tracepoint:syscalls:sys_enter_execve / str(args.argv[0]) == "./testprogs/true" / { print(str(args.argv[1], 5)); exit(); }
AFTER ./testprogs/true zztest
EXPECT zzte

NAME str_truncated_custom
PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();}
ENV BPFTRACE_MAX_STRLEN=5 BPFTRACE_STR_TRUNC_TRAILER=_xxx
AFTER ./testprogs/syscall execve ./testprogs/true &>/dev/null
EXPECT_REGEX P: /...._xxx

NAME str_big
PROG t:syscalls:sys_enter_execve { @[str(args.filename)] = count() }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT_REGEX @\[/X{5555}\]: 1
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel
# We rely on the timeout to terminate the script
TIMEOUT 1

NAME str_big_strncmp
PROG t:syscalls:sys_enter_execve { if (strncmp(str(args.filename), "/XXX", 4)) { print("matched"); exit(); } }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT matched
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel

NAME str_big_printf
PROG t:syscalls:sys_enter_execve { printf("%s\n", str(args.filename)) }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT_REGEX /X{5555}
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel

NAME str_big_print
PROG t:syscalls:sys_enter_execve { print(str(args.filename)) }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT_REGEX /X{5555}
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel

NAME str_big_read
PROG begin { @s = str(0); @ss = @s; print("success!"); exit() }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT success!
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 1

NAME str_big_tuple
PROG t:syscalls:sys_enter_execve { print((1, (2, str(args.filename)))) }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT_REGEX \(1, \(2, /X{5555}\)\)
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 1

NAME tuple_scratch_buf
PROG config = { on_stack_limit = 0 } begin { print((1, (2, "XXXX"))) }
EXPECT (1, (2, XXXX))

NAME str_scratch_buf
PROG config = { on_stack_limit = 0 } t:syscalls:sys_enter_execve { print(str(args.filename)) }
EXPECT /XXXX
AFTER ./testprogs/syscall execve /XXXX
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 1

NAME fmt_str_args_scratch_buf
PROG config = { on_stack_limit = 0 } begin { printf("%s %d\n", "XXXX", 1); exit() }
EXPECT XXXX 1

NAME map_val_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @x = 1; @y = @x; print(@y); exit() }
EXPECT @y: 1

NAME variable_scratch_buf
PROG config = { on_stack_limit = 0 } begin { $x = "XXXX"; $y = $x; print($y); exit() }
EXPECT XXXX

# Verify enough buffer space is allocated for same variable name in multiple scopes
NAME variable_for_loop_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @[1] = 1; for ($kv : @) { $y = $kv } for ($kv : @) { $y = $kv; print($y) } exit() }
EXPECT (1, 1)

# Verify enough buffer space is allocated for same variable name in multiple subprograms
NAME variable_probe_subprog_scratch_buf
PROG config = { on_stack_limit = 0 } begin { $x = "XXXX" } i:ms:100 { $x = "YYYY";  }end { $x = "ZZZZ"; print($x) }
EXPECT ZZZZ

NAME scalar_map_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @x = "xxxx"; print(@x); exit() }
EXPECT @x: xxxx

NAME map_key_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @x[1, 2] = 1; @[1, 1] = 3; @x[2, 1] = 2; delete(@x[1, 2]); delete(@x, (1, 1)); print(@x); exit() }
EXPECT @x[2, 1]: 2

NAME hist_map_key_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @["ok_key"] = hist(1); exit() }
EXPECT @[ok_key]:

NAME hist_scalar_map_key_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @ = hist(1); exit() }
EXPECT @:

NAME lhist_map_key_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @["ok_key"] = lhist(2,0,10,2); exit() }
EXPECT @[ok_key]:

NAME lhist_scalar_map_key_scratch_buf
PROG config = { on_stack_limit = 0 } begin { @ = lhist(2,0,10,2); exit() }
EXPECT @:

NAME tseries_map_key_scratch_buf
PROG begin { @["ok_key"] = tseries(2,1s,1); exit() }
EXPECT @[ok_key]:

NAME tseries_scalar_map_key_scratch_buf
PROG begin { @ = tseries(2,1s,2); exit() }
EXPECT @:

NAME str_big_for
PROG t:syscalls:sys_enter_execve { @map[str(args.filename)] = str(args.filename); for ($kv : @map) { print($kv); } }
ENV BPFTRACE_MAX_STRLEN=9999
EXPECT_REGEX \(/X{5555}, /X{5555}\)
AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)")
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 1

NAME buf
RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct
EXPECT P: \x09\x08\x07\x06-\x05\x04\x03\x02-\x01\x02\x03\x04-\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00-\x09\x00\x0a\x00\x0b\x00\x0c\x00-\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00
ARCH x86_64|ppc64le|aarch64|armv7l

NAME buf
RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct
EXPECT P: \x09\x08\x07\x06-\x05\x04\x03\x02-\x01\x02\x03\x04-\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08-\x00\x09\x00\x0a\x00\x0b\x00\x0c-\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x10
ARCH s390x|ppc64

NAME buf_map_key
PROG i:ms:100 { @[buf("ok_key", 6)] = 1; exit(); }
EXPECT @[ok_key]: 1

NAME buf_map_multikey
PROG begin { @[buf("ok_key", 7), 1] = hist(1);  }
EXPECT @[ok_key\x00, 1]:

NAME buf_hist_map_key
PROG begin { @[buf("ok_key", 7)] = hist(1);  }
EXPECT @[ok_key\x00]:

NAME buf_map_value
PROG i:ms:100 { @ = buf("ok_value", 8); exit(); }
EXPECT @: ok_value

NAME buf_no_ascii
PROG begin { printf("%rx", buf("Hello\0", 6));  }
EXPECT \x48\x65\x6c\x6c\x6f\x00

NAME buf_no_ascii_no_escaping
PROG begin { printf("%rh", buf("Hello\0", 6));  }
EXPECT 48 65 6c 6c 6f 00

NAME ksym
PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); exit();}
EXPECT do_nanosleep
ARCH x86_64
AFTER ./testprogs/syscall nanosleep 1e8

NAME ksym
PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pswaddr"))); exit();}
EXPECT do_nanosleep
ARCH s390x
AFTER ./testprogs/syscall nanosleep 1e8

NAME ksym
PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pc"))); exit();}
EXPECT do_nanosleep
ARCH aarch64|armv7l
AFTER ./testprogs/syscall nanosleep 1e8

NAME ksym
PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("nip"))); exit();}
EXPECT do_nanosleep
ARCH ppc64|ppc64le
AFTER ./testprogs/syscall nanosleep 1e8

NAME system
RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo 'ok_system'"); exit();}'
EXPECT ok_system

NAME system_more_args
RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); exit();}'
EXPECT 1 2 3 4 5 6 7

NAME count
PROG i:ms:100 { @ = count(); exit();}
EXPECT_REGEX @:\s[0-9]+

NAME sum
PROG begin { @sum = sum(1); @sum = sum(2); @sum = sum(3); }
EXPECT @sum: 6

NAME avg
PROG begin { @avg = avg(1); @avg = avg(2); @avg = avg(3); }
EXPECT @avg: 2

NAME min
PROG begin { @min = min(2); @min = min(1); @min = min(3); }
EXPECT @min: 1

NAME max
PROG begin { @max = max(1); @max = max(3); @max = max(2); }
EXPECT @max: 3

NAME stats
PROG begin { @stats = stats(1); @stats = stats(2); @stats = stats(3); }
EXPECT @stats: { .count = 3, .average = 2, .total = 6 }

NAME hist
PROG begin { @=hist(-1); @=hist(2); @=hist(3); @=hist(7); @=hist(20); }
EXPECT_FILE runtime/outputs/hist.txt
TIMEOUT 1

NAME hist_2_values
PROG begin { @=hist(3);@=hist(20);}
EXPECT_FILE runtime/outputs/hist_2_values.txt
TIMEOUT 1

NAME hist_10g
PROG begin { @ = hist(10 * 1024 * 1024 * 1024);  }
EXPECT_JSON runtime/outputs/hist_10g.json
TIMEOUT 1

NAME lhist
PROG begin { @=lhist(2,0,10,2); @=lhist(3,0,10,2); @=lhist(7,0,10,2); @=lhist(-1,0,10,2); @=lhist(11,0,10,2); exit()}
EXPECT_FILE runtime/outputs/lhist.txt

NAME kstack
PROG k:do_nanosleep { printf("%s\n%s\n", kstack(), kstack(1)); exit(); }
EXPECT Attached 1 probe
AFTER ./testprogs/syscall nanosleep  1e8

NAME kstack perf mode
PROG k:do_nanosleep { printf("%s\n", kstack(perf, 1)); exit(); }
EXPECT Attached 1 probe
AFTER ./testprogs/syscall nanosleep  1e8

NAME ustack
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n%s\n", ustack(), ustack(1)); exit(); }
EXPECT Attached 1 probe
AFTER ./testprogs/uprobe_loop

NAME ustack_stack_mode_env_bpftrace
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); }
ENV BPFTRACE_STACK_MODE=bpftrace
EXPECT_REGEX ^\s+uprobeFunction1\+[0-9]+@[\w\/]+.c:[0-9]+$
AFTER ./testprogs/uprobe_loop

# file and line are not available for perf stack mode
NAME ustack_stack_mode_env_perf
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); }
ENV BPFTRACE_STACK_MODE=perf
EXPECT_REGEX ^\s+[0-9a-f]+ uprobeFunction1\+[0-9]+ \(.*/uprobe_loop\)$
AFTER ./testprogs/uprobe_loop

NAME ustack_with_no_file_and_line_env_bpftrace
PROG config = { show_debug_info=0 } u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); }
ENV BPFTRACE_STACK_MODE=bpftrace
EXPECT_REGEX ^\s+uprobeFunction1\+[0-9]+$
AFTER ./testprogs/uprobe_loop

NAME ustack_stack_mode_env_raw
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); }
ENV BPFTRACE_STACK_MODE=raw
EXPECT_REGEX ^[\da-fA-F]+$
AFTER ./testprogs/uprobe_loop

NAME ustack_stack_mode_env_override
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(raw, 1)); exit(); }
ENV BPFTRACE_STACK_MODE=perf
EXPECT_REGEX ^[\da-fA-F]+$
AFTER ./testprogs/uprobe_loop

NAME ustack_elf_symtable
ENV BPFTRACE_CACHE_USER_SYMBOLS=PER_PROGRAM
PROG config = { show_debug_info=0 } uprobe:./testprogs/uprobe_symres_exited_process:test { print(ustack); exit(); }
EXPECT_REGEX ^\s+test\+[0-9]+\s+test2\+[0-9]+\s+main\+[0-9]+
AFTER ./testprogs/disable_aslr ./testprogs/uprobe_symres_exited_process
# The stack output expects prologue to be skipped on uprobes. See kernel:
# cfa7f3d2c526 ("perf,x86: avoid missing caller address in stack traces captured in uprobe")
MIN_KERNEL 6.12

NAME ustack_with_inlined_funcs
RUN {{BPFTRACE}} -e 'config = { show_debug_info=1 } usdt:./testprogs/usdt_inlined:tracetest:testprobe { if (arg1 == 100) { printf("%s\n", ustack()); exit(); } }' -c ./testprogs/usdt_inlined
EXPECT_REGEX ^\s+\[inlined\] myclock@[\w\/]+.c:[0-9]+\s*\[inlined\] mywrapper_inlined@[\w\/]+.c:[0-9]+\s*mywrapper.*
TIMEOUT 1

NAME ustack_with_inlined_funcs_with_limit
RUN {{BPFTRACE}} -e 'config = { show_debug_info=1 } usdt:./testprogs/usdt_inlined:tracetest:testprobe { if (arg1 == 100) { printf("%s\n", ustack(2)); exit(); } }' -c ./testprogs/usdt_inlined
EXPECT_REGEX ^\s+\[inlined\] myclock@[\w\/]+.c:[0-9]+\s*\[inlined\] mywrapper_inlined@[\w\/]+.c:[0-9]+$
TIMEOUT 1

NAME ustack_with_inlined_funcs_env_perf
RUN {{BPFTRACE}} -e 'config = { show_debug_info=1 } usdt:./testprogs/usdt_inlined:tracetest:testprobe { if (arg1 == 100) { printf("%s\n", ustack()); exit(); } }' -c ./testprogs/usdt_inlined
ENV BPFTRACE_STACK_MODE=perf
EXPECT_REGEX ^\s+[0-9a-f]+ myclock \(inlined\)\s*[0-9a-f]+ mywrapper_inlined \(inlined\)\s*[0-9a-f]+ mywrapper.*
TIMEOUT 1

NAME cat
PROG i:ms:1 { cat("/proc/uptime"); exit();}
EXPECT_REGEX [0-9]*.[0-9]* [0-9]*.[0-9]*

NAME cat_more_args
PROG i:ms:1 { cat("/%s%s%s%s/%s%s%s", "p", "r", "o", "c", "u", "p", "time"); exit();}
EXPECT_REGEX [0-9]*.[0-9]* [0-9]*.[0-9]*

NAME uaddr
RUN {{BPFTRACE}} -e 'uprobe:testprogs/uprobe_test:uprobeFunction1 { printf("0x%lx -- 0x%lx\n", *uaddr("GLOBAL_A"), *uaddr("GLOBAL_C")); exit(); }' -c ./testprogs/uprobe_test
EXPECT 0x55555555 -- 0x33333333

NAME ntop static ip
PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(1), ntop(0x0100007f), ntop(0xffff0000), ntop(0x0000ffff)); exit() }
EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0
ARCH x86_64|aarch64|ppc64le|armv7l

NAME ntop static ip
PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(0x01000000), ntop(0x7f000001), ntop(0x0000ffff), ntop(0xffff0000)); exit() }
EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0
ARCH s390x|ppc64

NAME pton ipv4
PROG i:ms:100 { printf("IP: %s\n", ntop(2, pton("127.0.0.1"))); exit(); }
EXPECT IP: 127.0.0.1

NAME pton ipv6
PROG i:ms:100 { printf("IP: %s\n", ntop(10, pton("::1"))); exit(); }
EXPECT IP: ::1

NAME usym
PROG i:ms:100 { @=usym(1); @a=@; exit(); }
EXPECT @a: 0x1

NAME print_non_map
PROG begin { $x = 5; print($x); exit() }
EXPECT 5
TIMEOUT 1

NAME print_non_map_builtin
PROG begin { print(comm); exit() }
EXPECT bpftrace
TIMEOUT 1

NAME print_non_map_tuple
PROG begin { $t = (1, 2, "string"); print($t); exit() }
EXPECT (1, 2, string)
TIMEOUT 1

NAME print_non_map_array
PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = ((struct A *) arg0)->x; print($a); exit(); }
EXPECT [1,2,3,4]
AFTER ./testprogs/array_access

NAME print_non_map_multi_dimensional_array
PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $b = ((struct B *) arg1)->y; print($b); exit(); }
EXPECT [[5,6],[7,8]]
AFTER ./testprogs/array_access

NAME print_non_map_struct
PROG struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); }
EXPECT { .m = 2, .n = 3 }
AFTER ./testprogs/simple_struct

NAME print_non_map_struct_fentry
PROG fentry:vfs_open { print(*args.path); exit(); }
EXPECT_REGEX { .mnt = 0x[0-9a-f]+, .dentry = 0x[0-9a-f]+ }
REQUIRES_FEATURE fentry
AFTER ./testprogs/syscall open

NAME print_map_item
PROG begin { @x[1] = 5; print(@x[1]); exit() }
EXPECT 5
TIMEOUT 1

NAME print_map_item_tuple
PROG begin { @x[1] = "hi"; print((1, 2, @x[1])); exit() }
EXPECT (1, 2, hi)
TIMEOUT 1

NAME strftime
PROG begin { $ts = strftime("%m/%d/%y", nsecs); printf("%s\n", $ts);  }
EXPECT_REGEX [0-9]{2}\/[0-9]{2}\/[0-9]{2}

NAME strftime_as_map_key
PROG begin { @[strftime("%m/%d/%y", nsecs)] = 1; }
EXPECT_REGEX \[[0-9]{2}\/[0-9]{2}\/[0-9]{2}\]: 1

NAME strftime_as_map_value
PROG begin { @[nsecs] = strftime("%m/%d/%y", nsecs); }
EXPECT_REGEX @\[[0-9]*\]: [0-9]{2}\/[0-9]{2}\/[0-9]{2}

# Output two microsecond timestamps, 123000 nsecs apart. Use python to evaluate and verify there's a 123us delta
NAME strftime_microsecond_extension
RUN {{BPFTRACE}} -e 'begin { printf("%s - %s\n", strftime("%s.%f", 123000), strftime("%s.%f", 0));  }' | bc
EXPECT .000123
TIMEOUT 1

# Similar to above test but test that rolling over past 1s works as expected
NAME strftime_microsecond_extension_rollover
RUN {{BPFTRACE}} -e 'begin { printf("%s - %s\n", strftime("%s.%f", 1000123000), strftime("%s.%f", 0));  }' | bc
EXPECT 1.000123
TIMEOUT 1

NAME print_avg_map_args
PROG begin { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); clear(@);  }
EXPECT_FILE  runtime/outputs/print_avg_map_args.txt
TIMEOUT 1

NAME print_avg_map_with_large_top
PROG begin { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); clear(@);  }
EXPECT_FILE runtime/outputs/print_avg_map_with_large_top.txt
TIMEOUT 1

NAME print_hist_with_top_arg
PROG begin { print("begin"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 2); print("end"); clear(@);  }
EXPECT_REGEX begin\n@\[2\]:(.*\n)+@\[3\]:(.*\n)+end
TIMEOUT 1

NAME print_hist_with_large_top_arg
PROG begin { print("begin"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 10); print("end"); clear(@);  }
EXPECT_REGEX begin\n@\[1\]:(.*\n)+@\[2\]:(.*\n)+@\[3\]:(.*\n)+end
TIMEOUT 1

NAME path
RUN {{BPFTRACE}} -ve 'fentry:security_file_open { if (!strncmp(path(args.file->f_path), "/tmp/bpftrace_runtime_test_syscall_gen_open_temp", 49)) { printf("OK\n"); exit(); } }'
EXPECT OK
REQUIRES_FEATURE dpath fentry
AFTER TMPDIR=/tmp ./testprogs/syscall open

NAME path_with_optional_size
RUN {{BPFTRACE}} -ve 'fentry:security_file_open { $p = path(args.file->f_path, 48);  if ( sizeof($p) == 48 && !strncmp($p, "tmp/bpftrace_runtime_test_syscall_gen_open_temp", 48)) { printf("OK\n"); exit(); } }'
EXPECT OK
REQUIRES_FEATURE dpath fentry
AFTER TMPDIR=/tmp ./testprogs/syscall open

# The extra prints are here to confirm that we report on the correct helper, not
# just the first or the last one.
NAME path_in_unsupported_fentry
PROG fentry:vfs_read { print("a"); print(path(args.file->f_path)); print("b"); }
EXPECT stdin:1:31-60: ERROR: helper bpf_d_path not allowed in probe
EXPECT fentry:vfs_read { print("a"); print(path(args.file->f_path)); print("b"); }
EXPECT                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
REQUIRES_FEATURE dpath
REQUIRES_FEATURE fentry
WILL_FAIL
AFTER ./testprogs/syscall read

NAME macaddr
RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); printf("P: %s\n", macaddr($s->mac)); exit(); }' -c ./testprogs/complex_struct
EXPECT P: 05:04:03:02:01:02

NAME macaddr as map key
RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[macaddr($s->mac)] = 1; exit(); }' -c ./testprogs/complex_struct
EXPECT @[05:04:03:02:01:02]: 1

NAME macaddr as map value
RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[1] = macaddr($s->mac); exit(); }' -c ./testprogs/complex_struct
EXPECT @[1]: 05:04:03:02:01:02

NAME iter:task
PROG iter:task { printf("comm: %s\n", ctx->task->comm); exit(); }
EXPECT comm: bpftrace
REQUIRES_FEATURE iter

NAME iter:task_file
PROG iter:task_file { printf("comm: %s\n", ctx->task->comm); exit(); }
EXPECT comm: bpftrace
REQUIRES_FEATURE iter

NAME iter:task_vma
PROG iter:task_vma { printf("comm: %s\n", ctx->task->comm); exit(); }
EXPECT comm: bpftrace
REQUIRES_FEATURE iter

NAME iter printf multiple values
PROG iter:task { printf("%s: %d\n", "hello", 0); exit(); }
EXPECT hello: 0
REQUIRES_FEATURE iter

NAME cgroup_path
PROG begin { print(cgroup_path(cgroup & ((1 << 32) - 1)));  }
EXPECT_REGEX ([a-z]*:\/.*;)*[a-z]*:\/.*
REQUIRES grep -q '^cgroup2' /proc/mounts
MIN_KERNEL 4.18

NAME cgroup_path printf
PROG begin { printf("path: %s", cgroup_path(cgroup & ((1 << 32) - 1)));  }
EXPECT_REGEX path: ([a-z]*:\/.*;)*[a-z]*:\/.*
REQUIRES grep -q '^cgroup2' /proc/mounts
MIN_KERNEL 4.18

NAME strerror
RUN {{BPFTRACE}} --include "errno.h" -e 'begin { print(strerror(EPERM));  }'
EXPECT Operation not permitted

NAME bswap_int64
RUN {{BPFTRACE}} -e 'begin { printf("%llx", bswap(0x1122334455667788));  }'
EXPECT 8877665544332211
TIMEOUT 1

NAME bswap_int32
RUN {{BPFTRACE}} -e 'begin { printf("%x", bswap((int32)0x12345678));  }'
EXPECT 78563412
TIMEOUT 1

NAME bswap_int16
RUN {{BPFTRACE}} -e 'begin { printf("%x", bswap((int16)0x1234));  }'
EXPECT 3412
TIMEOUT 1

NAME bswap_int8
RUN {{BPFTRACE}} -e 'begin { printf("%x", bswap((int8)0x12));  }'
EXPECT 12
TIMEOUT 1

NAME bswap_int64_op
RUN {{BPFTRACE}} -e 'begin { printf("%llx", bswap(0x12340000 + 0x5678));  }'
EXPECT 7856341200000000
TIMEOUT 1

NAME bswap_twice
RUN {{BPFTRACE}} -e 'begin { printf("%x", bswap(bswap(0x1234)));  }'
EXPECT 1234
TIMEOUT 1

NAME skboutput
RUN {{BPFTRACE}} -e 'fentry:__dev_queue_xmit { $ret = skboutput("skb.pcap", args.skb, args.skb->len, 14); printf("ret: %d\n", $ret); exit(); }'
SETUP ip netns add bpftrace-test && ip -netns bpftrace-test link set lo up
AFTER ip netns exec bpftrace-test ./testprogs/syscall connect 127.0.0.1 80
CLEANUP ip netns delete bpftrace-test
EXPECT ret: 0
REQUIRES_FEATURE fentry
REQUIRES_FEATURE skboutput
MIN_KERNEL 5.5

# Just test that the verifier doesn't reject this
NAME skboutput rawtracepoint
RUN {{BPFTRACE}} -e 'rawtracepoint:consume_skb { $ret = skboutput("receive.pcap", (struct sk_buff *)arg0, 1, 0); printf("ret: %d\n", $ret); exit(); }'
EXPECT Attached 1 probe
REQUIRES_FEATURE skboutput
MIN_KERNEL 5.5

NAME socket_cookie
RUN {{BPFTRACE}} -e 'fentry:tcp_v4_connect { $ret = socket_cookie(args.sk); printf("ret: %llu\n", $ret); exit(); }'
SETUP ip netns add bpftrace-test && ip -netns bpftrace-test link set lo up
AFTER ip netns exec bpftrace-test ./testprogs/syscall connect 127.0.0.1 80
CLEANUP ip netns delete bpftrace-test
EXPECT_REGEX ret: [0-9]+
REQUIRES_FEATURE fentry

# Just test that the verifier doesn't reject this
NAME socket_cookie rawtracepoint
RUN {{BPFTRACE}} -e 'rawtracepoint:tcp_probe { $ret = socket_cookie(args->sk); printf("ret: %llu\n", $ret); exit(); }'
EXPECT Attached 1 probe

NAME debugf
RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { debugf("debugf"); exit();}'; cat /sys/kernel/debug/tracing/trace
EXPECT_REGEX bpf_trace_printk: debugf
TIMEOUT 3

NAME debugf_with_arguments
RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { debugf("debugf %s %d %x", "a", 1, 16); exit();}'; cat /sys/kernel/debug/tracing/trace
EXPECT_REGEX bpf_trace_printk: debugf a 1 10
TIMEOUT 3

NAME debugf_with_seq_printf
RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { printf("printf %d", 0); debugf("debugf %d", 1); exit();}'; cat /sys/kernel/debug/tracing/trace
EXPECT_REGEX bpf_trace_printk: debugf 1
TIMEOUT 3

NAME nsecs
PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs()); exit(); }
EXPECT_REGEX SUCCESS [0-9]+

NAME nsecs_monotonic
PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(monotonic)); exit(); }
EXPECT_REGEX SUCCESS [0-9]+

NAME nsecs_boot
PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(boot)); exit(); }
EXPECT_REGEX SUCCESS [0-9]+

NAME nsecs_tai
PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(tai)); exit(); }
EXPECT_REGEX SUCCESS [0-9]+
REQUIRES_FEATURE get_tai_ns
MIN_KERNEL 6.1

NAME nsecs_sw_tai
PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(sw_tai)); exit(); }
EXPECT_REGEX SUCCESS [0-9]+

NAME map len
PROG begin { @[0] = 0; @[1] = 1; printf("map len: %d\n", len(@));  }
EXPECT map len: 2
TIMEOUT 3

NAME map lencmp
PROG begin { @[1] = 1; @[2] = 2; $count = len(@); if ($count > 1) { print("true"); }  }
EXPECT true
TIMEOUT 3

NAME map len keyless
PROG begin { @ = 0; printf("map len: %d\n", len(@));  }
EXPECT stdin:1:40-45: ERROR: call to len() expects a map with explicit keys (non-scalar map)
       begin { @ = 0; printf("map len: %d\n", len(@));  }
                                              ~~~~~
WILL_FAIL

NAME percpu_kaddr
PROG begin { printf("processes: %d\n", *percpu_kaddr("process_counts", 0));  }
EXPECT_REGEX processes: -?[0-9]+

NAME percpu_kaddr this cpu
PROG begin { if (*percpu_kaddr("process_counts") == *percpu_kaddr("process_counts", cpu)) { printf("SUCCESS\n") }  }
EXPECT SUCCESS

NAME percpu_kaddr field access
PROG begin { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); if ($runq != 0) { printf("nr_running: %d\n", $runq->nr_running) } exit() }
EXPECT_REGEX nr_running: [0-9]+

NAME percpu_kaddr field access no NULL check
PROG begin { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); printf("nr_running: %d\n", $runq->nr_running); exit() }
EXPECT stdin:1:17-60: ERROR: helper bpf_per_cpu_ptr: result needs to be null-checked before accessing fields
       begin { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); printf("nr_running: %d\n", $runq->nr_running); exit() }
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WILL_FAIL

NAME clear on scalar map prevents printing
PROG begin { @xx = 5; clear(@xx); exit() }
EXPECT_NONE @xx: 5

NAME ustack len
PROG u:./testprogs/uprobe_loop:uprobeFunction1 { $x = ustack(3); @ = len($x); exit(); }
EXPECT @: 3
AFTER ./testprogs/uprobe_loop

NAME kstack len
PROG k:do_nanosleep { @ = len(kstack); exit() }
EXPECT_REGEX @: \d+$
AFTER ./testprogs/syscall nanosleep  1e8

NAME block expression with map assignment
PROG begin { @ = { let $a = 100; avg($a) };  }
EXPECT @: 100
