/******************************************************************************
 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
 * sysTrace licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 *PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 *Mulan PSL v2 for more details. Author: curry Create: 2025-06-20 Description:
 *bpf header
 ******************************************************************************/
#ifndef __GOPHER_LIB_BPF_H__
#define __GOPHER_LIB_BPF_H__

#pragma once

#if !defined(BPF_PROG_KERN) && !defined(BPF_PROG_USER)

#include "__compat.h"
#include "common.h"
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <stdlib.h>
#include <sys/resource.h>

#define EBPF_RLIM_LIMITED RLIM_INFINITY
#define EBPF_RLIM_INFINITY (~0UL)
#ifndef EINTR
#define EINTR 4
#endif

static __always_inline int set_memlock_rlimit(unsigned long limit) {
    struct rlimit rlim_new = {
        .rlim_cur = limit,
        .rlim_max = limit,
    };

    if (setrlimit(RLIMIT_MEMLOCK, (const struct rlimit *)&rlim_new) != 0) {
        (void)fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
        return 0;
    }
    return 1;
}

#define GET_MAP_OBJ(probe_name, map_name) (probe_name##_skel->maps.map_name)

#define __MAP_SET_PIN_PATH(probe_name, map_name, map_path)                     \
    do {                                                                       \
        int ret;                                                               \
        struct bpf_map *__map;                                                 \
                                                                               \
        __map = GET_MAP_OBJ(probe_name, map_name);                             \
        ret = bpf_map__set_pin_path(__map, map_path);                          \
        if (ret) {                                                             \
            fprintf(stderr, "Failed to set pin path for map " #map_name        \
                            " in " #probe_name "\n");                          \
        }                                                                      \
    } while (0)

#define INIT_BPF_APP(app_name, limit)                                          \
    static char __init = 0;                                                    \
    do {                                                                       \
        if (!__init) {                                                         \
            /* Bump RLIMIT_MEMLOCK  allow BPF sub-system to do anything */     \
            if (set_memlock_rlimit(limit) == 0) {                              \
                printf("BPF app(" #app_name ") failed to set mem limit.\n");   \
                return -1;                                                     \
            }                                                                  \
            __init = 1;                                                        \
        }                                                                      \
    } while (0)

#define __OPEN_OPTS(probe_name, end, load, opts)                               \
    struct probe_name##_bpf *probe_name##_skel = NULL;                         \
    struct bpf_link *probe_name##_link[PATH_NUM] __maybe_unused = {NULL};      \
    int probe_name##_link_current = 0;                                         \
    do {                                                                       \
        if (load) {                                                            \
            /* Open load and verify BPF application */                         \
            probe_name##_skel = probe_name##_bpf__open_opts(opts);             \
            if (!probe_name##_skel) {                                          \
                printf("Failed to open BPF " #probe_name " skeleton\n");       \
                goto end;                                                      \
            }                                                                  \
        }                                                                      \
    } while (0)

#define OPEN_OPTS(probe_name, end, load)                                       \
    __OPEN_OPTS(probe_name, end, load, &probe_name##_open_opts)

#define MAP_SET_PIN_PATH(probe_name, map_name, map_path, load)                 \
    do {                                                                       \
        if (load) {                                                            \
            __MAP_SET_PIN_PATH(probe_name, map_name, map_path);                \
        }                                                                      \
    } while (0)

#define MAP_INIT_BPF_BUFFER_SHARED(probe_name, map_name, buffer_ptr, load)     \
    do {                                                                       \
        if (load) {                                                            \
            (void)bpf_buffer__new_shared(probe_name##_skel->maps.map_name,     \
                                         probe_name##_skel->maps.heap,         \
                                         (buffer_ptr));                        \
            if (*(buffer_ptr) == NULL) {                                       \
                printf("Failed to initialize bpf_buffer for " #map_name        \
                       " in " #probe_name "\n");                               \
            }                                                                  \
        }                                                                      \
    } while (0)

#define LOAD_ATTACH(app_name, probe_name, end, load)                           \
    do {                                                                       \
        if (load) {                                                            \
            int err;                                                           \
            if (probe_name##_bpf__load(probe_name##_skel)) {                   \
                printf("Failed to load BPF " #probe_name " skeleton\n");       \
                goto end;                                                      \
            }                                                                  \
            /* Attach tracepoint handler */                                    \
            err = probe_name##_bpf__attach(probe_name##_skel);                 \
            if (err) {                                                         \
                printf("Failed to attach BPF " #probe_name " skeleton\n");     \
                probe_name##_bpf__destroy(probe_name##_skel);                  \
                probe_name##_skel = NULL;                                      \
                goto end;                                                      \
            }                                                                  \
        }                                                                      \
    } while (0)

#define UNLOAD(probe_name)                                                     \
    do {                                                                       \
        int err;                                                               \
        if (probe_name##_skel != NULL) {                                       \
            probe_name##_bpf__destroy(probe_name##_skel);                      \
        }                                                                      \
        for (int i = 0; i < probe_name##_link_current; i++) {                  \
            err = bpf_link__destroy(probe_name##_link[i]);                     \
            if (err < 0) {                                                     \
                printf("Failed to detach BPF " #probe_name " %d\n", err);      \
                break;                                                         \
            }                                                                  \
        }                                                                      \
    } while (0)

#define INIT_OPEN_OPTS(probe_name)                                             \
    LIBBPF_OPTS(bpf_object_open_opts, probe_name##_open_opts)

#define SKEL_MAX_NUM 20
typedef void (*skel_destroy_fn)(void *);

struct __bpf_skel_s {
    skel_destroy_fn fn;
    void *skel;
    void *_link[PATH_NUM];
    size_t _link_num;
};
struct bpf_prog_s {
    struct perf_buffer *pb;
    struct ring_buffer *rb;
    struct bpf_buffer *buffer;
    struct perf_buffer *pbs[SKEL_MAX_NUM];
    struct ring_buffer *rbs[SKEL_MAX_NUM];
    struct bpf_buffer *buffers[SKEL_MAX_NUM];
    struct __bpf_skel_s skels[SKEL_MAX_NUM];
    const char *custom_btf_paths[SKEL_MAX_NUM];
    size_t num;
};

static __always_inline __maybe_unused void
free_bpf_prog(struct bpf_prog_s *prog) {
    (void)free(prog);
}

static __always_inline __maybe_unused struct bpf_prog_s *alloc_bpf_prog(void) {
    struct bpf_prog_s *prog = malloc(sizeof(struct bpf_prog_s));
    if (prog == NULL) {
        return NULL;
    }

    (void)memset(prog, 0, sizeof(struct bpf_prog_s));
    return prog;
}

static __always_inline __maybe_unused void
unload_bpf_prog(struct bpf_prog_s **unload_prog) {
    struct bpf_prog_s *prog = *unload_prog;

    *unload_prog = NULL;
    if (prog == NULL) {
        return;
    }

    for (int i = 0; i < prog->num; i++) {
        if (prog->skels[i].skel) {
            prog->skels[i].fn(prog->skels[i].skel);

            for (int j = 0; j < prog->skels[i]._link_num; j++) {
                if (prog->skels[i]._link[j]) {
                    (void)bpf_link__destroy(prog->skels[i]._link[j]);
                }
            }
        }

        if (prog->pbs[i]) {
            perf_buffer__free(prog->pbs[i]);
        }

#if (CURRENT_LIBBPF_VERSION >= LIBBPF_VERSION(0, 8))
        if (prog->rbs[i]) {
            ring_buffer__free(prog->rbs[i]);
        }
#endif

        if (prog->buffers[i]) {
            bpf_buffer__free(prog->buffers[i]);
        }

        free((char *)prog->custom_btf_paths[i]);
    }

    if (prog->pb) {
        perf_buffer__free(prog->pb);
    }

#if (CURRENT_LIBBPF_VERSION >= LIBBPF_VERSION(0, 8))
    if (prog->rb) {
        ring_buffer__free(prog->rb);
    }
#endif

    if (prog->buffer) {
        bpf_buffer__free(prog->buffer);
    }

    free_bpf_prog(prog);
    return;
}

#endif
#endif