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); exit();}
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"); exit(); }
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); exit(); }
EXPECT ef cdef 78abcdef 12345678abcdef

NAME printf_char
PROG BEGIN { printf("%c%c%c%c\n", 0x41, 0x42, 0x43, 0x44); exit(); }
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_]+$
AFTER ping localhost -c 5
TIMEOUT 5

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 /bin/ls
EXPECT_REGEX P: /*.

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 /bin/ls &>/dev/null
EXPECT_REGEX P: /...\.\.

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 /bin/ls &>/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"; exit(); }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 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); exit(); }
EXPECT @[ok_key\x00, 1]:

NAME buf_hist_map_key
PROG BEGIN { @[buf("ok_key", 7)] = hist(1); exit(); }
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)); exit(); }
EXPECT \x48\x65\x6c\x6c\x6f\x00

NAME buf_no_ascii_no_escaping
PROG BEGIN { printf("%rh", buf("Hello\0", 6)); exit(); }
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); exit();}
EXPECT @sum: 6

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

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

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

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

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

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

NAME hist_10g
PROG BEGIN { @ = hist(10 * 1024 * 1024 * 1024); exit(); }
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 Attaching 1 probe...
AFTER ./testprogs/syscall nanosleep  1e8

NAME kstack perf mode
PROG k:do_nanosleep { printf("%s\n", kstack(perf, 1)); exit(); }
EXPECT Attaching 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 Attaching 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]+$
AFTER ./testprogs/uprobe_loop
# This was debugged as far down as std::cout having badbit [0] set after a
# write. It's really confusing why. Cannot be reproduced locally. While
# debugging is ongoing disable in CI. There's a good chance we never figure out
# why. My current best guess is that somewhere in all the layers of shelling
# out there's a misbehaving pipe that hangs up too early.
#
# See https://github.com/bpftrace/bpftrace/issues/3080.
#
# Note that CI is a default environment variable set by GHA [1].
#
# [0]: https://en.cppreference.com/w/cpp/io/ios_base/iostate
# [1]: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
SKIP_IF_ENV_HAS CI=true

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
# See https://github.com/bpftrace/bpftrace/issues/3080
SKIP_IF_ENV_HAS CI=true

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
# See https://github.com/bpftrace/bpftrace/issues/3080
SKIP_IF_ENV_HAS CI=true

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
# See https://github.com/bpftrace/bpftrace/issues/3080
SKIP_IF_ENV_HAS CI=true

NAME ustack_elf_symtable
ENV BPFTRACE_CACHE_USER_SYMBOLS=PER_PROGRAM
PROG 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
# Required to skip function prologue
REQUIRES_FEATURE dwarf

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); exit(); }
EXPECT_REGEX [0-9]{2}\/[0-9]{2}\/[0-9]{2}

NAME strftime_as_map_key
PROG BEGIN { @[strftime("%m/%d/%y", nsecs)] = 1; exit();}
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); exit();}
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
#
# Note we add a `1` before the timestamp b/c leading zeros (eg `0123`) is invalid integer in python.
NAME strftime_microsecond_extension
RUN {{BPFTRACE}} -e 'BEGIN { printf("%s - %s\n", strftime("1%f", 1000123000), strftime("1%f", 0)); exit(); }' | tail -n +2 | xargs -I{} python3 -c "print({})"
EXPECT 123
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("1%f", 1000000123000), strftime("1%f", 0)); exit(); }' | tail -n +2 | xargs -I{} python3 -c "print({})"
EXPECT 123
TIMEOUT 1

NAME print_avg_map_args
PROG BEGIN { print("BEGIN"); @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); print("END"); clear(@); exit(); }
EXPECT BEGIN
       @[c]: 3
       @[d]: 4
       
       END
TIMEOUT 1

NAME print_avg_map_with_large_top
PROG BEGIN { print("BEGIN"); @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); print("END"); clear(@); exit(); }
EXPECT BEGIN
       @[a]: 1
       @[b]: 2
       @[c]: 3
       @[d]: 4
       
       END
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(@); exit(); }
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(@); exit(); }
EXPECT_REGEX BEGIN\n@\[1\]:(.*\n)+@\[2\]:(.*\n)+@\[3\]:(.*\n)+END
TIMEOUT 1

NAME path
RUN {{BPFTRACE}} -ve 'fentry:filp_close { if (!strncmp(path(args.filp->f_path), "/tmp/bpftrace_runtime_test_syscall_gen_read_temp", 49)) { printf("OK\n"); exit(); } }'
EXPECT OK
REQUIRES_FEATURE dpath fentry
AFTER ./testprogs/syscall read

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

NAME strcontains
RUN {{BPFTRACE}} -ve 'fentry:filp_close { if (strcontains(path(args.filp->f_path), "tmp")) { printf("OK\n"); exit(); } }'
EXPECT OK
REQUIRES_FEATURE dpath fentry
AFTER ./testprogs/syscall read

NAME strcontains literals
RUN {{BPFTRACE}} -e 'BEGIN { if (strcontains("abc", "a")) { printf("OK\n"); exit(); } }'
EXPECT OK
TIMEOUT 1

# 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))); exit(); }
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))); exit(); }
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)); exit(); }'
EXPECT Operation not permitted

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

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

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

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

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

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

NAME skboutput
RUN {{BPFTRACE}} -e 'fentry:__dev_queue_xmit { $ret = skboutput("skb.pcap", args.skb, args.skb->len, 14); if ($ret == 0) { printf("OK\n"); exit(); } }'
AFTER ./testprogs/syscall connect 127.0.0.1 80
EXPECT OK
REQUIRES_FEATURE fentry
REQUIRES_FEATURE skboutput
MIN_KERNEL 5.5

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(@)); exit(); }
EXPECT map len: 2
TIMEOUT 3

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

NAME map len keyless
PROG BEGIN { @ = 0; printf("map len: %d\n", len(@)); exit(); }
EXPECT map len: 1

NAME percpu_kaddr
PROG BEGIN { printf("processes: %d\n", *percpu_kaddr("process_counts", 0)); exit(); }
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") } exit(); }
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
