// SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
 #include "test_attach_kprobe_sleepable.skel.h"
+#include "test_attach_probe_manual.skel.h"
 #include "test_attach_probe.skel.h"
 
 /* this is how USDT semaphore is actually defined, except volatile modifier */
 static char test_data[] = "test_data";
 
 /* manual attach kprobe/kretprobe/uprobe/uretprobe testings */
-static void test_attach_probe_manual(struct test_attach_probe *skel)
+static void test_attach_probe_manual(enum probe_attach_mode attach_mode)
 {
        DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
+       DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts);
        struct bpf_link *kprobe_link, *kretprobe_link;
        struct bpf_link *uprobe_link, *uretprobe_link;
+       struct test_attach_probe_manual *skel;
        ssize_t uprobe_offset;
 
+       skel = test_attach_probe_manual__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "skel_kprobe_manual_open_and_load"))
+               return;
+
        uprobe_offset = get_uprobe_offset(&trigger_func);
        if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset"))
                goto cleanup;
 
        /* manual-attach kprobe/kretprobe */
-       kprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kprobe,
-                                                false /* retprobe */,
-                                                SYS_NANOSLEEP_KPROBE_NAME);
+       kprobe_opts.attach_mode = attach_mode;
+       kprobe_opts.retprobe = false;
+       kprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe,
+                                                     SYS_NANOSLEEP_KPROBE_NAME,
+                                                     &kprobe_opts);
        if (!ASSERT_OK_PTR(kprobe_link, "attach_kprobe"))
                goto cleanup;
        skel->links.handle_kprobe = kprobe_link;
 
-       kretprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kretprobe,
-                                                   true /* retprobe */,
-                                                   SYS_NANOSLEEP_KPROBE_NAME);
+       kprobe_opts.retprobe = true;
+       kretprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe,
+                                                        SYS_NANOSLEEP_KPROBE_NAME,
+                                                        &kprobe_opts);
        if (!ASSERT_OK_PTR(kretprobe_link, "attach_kretprobe"))
                goto cleanup;
        skel->links.handle_kretprobe = kretprobe_link;
 
        /* manual-attach uprobe/uretprobe */
+       uprobe_opts.attach_mode = attach_mode;
        uprobe_opts.ref_ctr_offset = 0;
        uprobe_opts.retprobe = false;
        uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe,
        ASSERT_EQ(skel->bss->uprobe_byname_res, 5, "check_uprobe_byname_res");
 
 cleanup:
+       test_attach_probe_manual__destroy(skel);
 }
 
 static void test_attach_probe_auto(struct test_attach_probe *skel)
        if (!ASSERT_OK_PTR(skel->bss, "check_bss"))
                goto cleanup;
 
-       if (test__start_subtest("manual"))
-               test_attach_probe_manual(skel);
+       if (test__start_subtest("manual-default"))
+               test_attach_probe_manual(PROBE_ATTACH_MODE_DEFAULT);
+       if (test__start_subtest("manual-legacy"))
+               test_attach_probe_manual(PROBE_ATTACH_MODE_LEGACY);
+       if (test__start_subtest("manual-perf"))
+               test_attach_probe_manual(PROBE_ATTACH_MODE_PERF);
+       if (test__start_subtest("manual-link"))
+               test_attach_probe_manual(PROBE_ATTACH_MODE_LINK);
+
        if (test__start_subtest("auto"))
                test_attach_probe_auto(skel);
        if (test__start_subtest("kprobe-sleepable"))
 
 #include <bpf/bpf_core_read.h>
 #include "bpf_misc.h"
 
-int kprobe_res = 0;
 int kprobe2_res = 0;
-int kretprobe_res = 0;
 int kretprobe2_res = 0;
-int uprobe_res = 0;
-int uretprobe_res = 0;
 int uprobe_byname_res = 0;
 int uretprobe_byname_res = 0;
 int uprobe_byname2_res = 0;
 int uretprobe_byname3_res = 0;
 void *user_ptr = 0;
 
-SEC("kprobe")
-int handle_kprobe(struct pt_regs *ctx)
-{
-       kprobe_res = 1;
-       return 0;
-}
-
 SEC("ksyscall/nanosleep")
 int BPF_KSYSCALL(handle_kprobe_auto, struct __kernel_timespec *req, struct __kernel_timespec *rem)
 {
        return 0;
 }
 
-SEC("kretprobe")
-int handle_kretprobe(struct pt_regs *ctx)
-{
-       kretprobe_res = 2;
-       return 0;
-}
-
 SEC("kretsyscall/nanosleep")
 int BPF_KRETPROBE(handle_kretprobe_auto, int ret)
 {
        return ret;
 }
 
-SEC("uprobe")
-int handle_uprobe(struct pt_regs *ctx)
-{
-       uprobe_res = 3;
-       return 0;
-}
-
-SEC("uretprobe")
-int handle_uretprobe(struct pt_regs *ctx)
-{
-       uretprobe_res = 4;
-       return 0;
-}
-
 SEC("uprobe")
 int handle_uprobe_ref_ctr(struct pt_regs *ctx)
 {