Libbpf eBPF macro BPF_PROG2
The BPF_PROG2 macro makes it easier to write programs for program types that receive []u64 contexts such as BPF_PROG_TYPE_TRACING programs. It improves upon the older BPF_PROG macro.
Definition
#define BPF_PROG2(name, args...)                        \
name(unsigned long long *ctx);                          \
static __always_inline typeof(name(0))                      \
____##name(unsigned long long *ctx ___bpf_ctx_decl(args));          \
typeof(name(0)) name(unsigned long long *ctx)                   \
{                                       \
    return ____##name(ctx ___bpf_ctx_arg(args));                \
}                                       \
static __always_inline typeof(name(0))                      \
____##name(unsigned long long *ctx ___bpf_ctx_decl(args))
Usage
This macro is useful when using program types that have a []u64 context type (typically written as unsigned long long *). 
Conventionally with these program contexts, the arguments to the program are put in this array. So the first argument would be in ctx[0], the second in ctx[1]. It is up to the program author to cast them into their actual type.
The BPF_PROG2 macro allows you to write your program with a normal function signature, the macro will then do the casting for you.
This macro also accounts for an edge case in the struct{u64, u64} for example. Since the context is a translation of the arguments passed, it to can use one or two slots depending on the type. The BPF_PROG2 handles this in the background, which is what improved over the BPF_PROG version of this macro.
Note
The original context will stay available as ctx, if you ever wish to access it manually or need to pass it to a helper or kfunc. Therefor, the variable name ctx should not be reused in arguments or function body.
Example
The bpfptr_t is actually the following type:
struct {
    union {
        void        *kernel;
        void __user *user;
    };
    bool        is_kernel : 1;
}
Which, with padding is 16 bytes and thus requires BPF_PROG2 to correctly cast the arguments.
SEC("fexit/__sys_bpf")
int BPF_PROG2(sys_bpf, int, cmd, bpfptr_t, uattr, unsigned int, size, int, ret)
{
    bpf_printf("BPF syscall returned with: %d", ret);
    return 0;
}