/******************************************************************************
 * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
 * libkperf 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: Mr.Zhang
 * Create: 2024-04-03
 * Description: functions for managing and interacting with performance events using system calls and inline assembly
 ******************************************************************************/
#include <climits>
#include <unistd.h>
#include <iostream>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <linux/perf_event.h>
#include "pcerrc.h"
#include "evt.h"


int KUNPENG_PMU::PerfEventOpen(struct perf_event_attr *attr, pid_t pid, int cpu, int groupFd, unsigned long flags)
{
    return syscall(__NR_perf_event_open, attr, pid, cpu, groupFd, flags);
}

int KUNPENG_PMU::PerfEvt::Enable()
{
    if (ioctl(this->fd, PERF_EVENT_IOC_ENABLE, 0) == 0) {
        return SUCCESS;
    }
    return LIBPERF_ERR_FAILED_PMU_ENABLE;
}

int KUNPENG_PMU::PerfEvt::Reset()
{
    if (ioctl(this->fd, PERF_EVENT_IOC_RESET, 0) == 0) {
        return SUCCESS;
    }
    return LIBPERF_ERR_FAILED_PMU_RESET;
}

int KUNPENG_PMU::PerfEvt::Disable()
{
    if (ioctl(this->fd, PERF_EVENT_IOC_DISABLE, 0) == 0) {
        return SUCCESS;
    }
    return LIBPERF_ERR_FAILED_PMU_DISABLE;
}

int KUNPENG_PMU::PerfEvt::Close()
{
    close(this->fd);
    return SUCCESS;
}

int KUNPENG_PMU::PerfEvt::BeginRead()
{
    return SUCCESS;
}

int KUNPENG_PMU::PerfEvt::EndRead()
{
    return SUCCESS;
}

int KUNPENG_PMU::PerfEvt::Start()
{
    this->Reset();
    return this->Enable();
}

int KUNPENG_PMU::PerfEvt::Pause()
{
    return this->Disable();
}

__u64 KUNPENG_PMU::ReadOnce(__u64 *head)
{
    union {
        typeof(*head) val;
        char charHead[1];
    } pointerUnion = {.charHead = {0}};

#ifdef IS_X86
    asm volatile("mov %0, %1"
                 : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#elif defined(IS_ARM)
    asm volatile("ldar %0, %1"
                 : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#elif defined(IS_RISCV64)
    asm volatile("ld %0, %1\nfence"
                 : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "m"(*head)
                 : "memory");
#else
#error "Unsupported architecture"
#endif

    return pointerUnion.val;
}

__s64 KUNPENG_PMU::ReadOnce(__s64 *head)
{
    union {
        typeof(*head) val;
        char charHead[1];
    } pointerUnion = {.charHead = {0}};

#ifdef IS_X86
    asm volatile("mov %0, %1"
                 : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#elif defined(IS_ARM)
    asm volatile("ldar %0, %1"
                 : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#elif defined(IS_RISCV64)
    asm volatile("ld %0, %1\nfence"
                 : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "m"(*head)
                 : "memory");
#else
#error "Unsupported architecture"
#endif

    return pointerUnion.val;
}

__u32 KUNPENG_PMU::ReadOnce(__u32 *head)
{
    union {
        typeof(*head) val;
        char charHead[1];
    } pointerUnion = {.charHead = {0}};
#if defined(IS_ARM)
    asm volatile("ldar %w0, %1"
                 : "=r"(*(__u32 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#endif
    return pointerUnion.val;
}

__u16 KUNPENG_PMU::ReadOnce(__u16 *head)
{
    union {
        typeof(*head) val;
        char charHead[1];
    } pointerUnion = {.charHead = {0}};
#if defined(IS_ARM)
    asm volatile("ldarh %w0, %1"
                 : "=r"(*(__u16 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#endif
    return pointerUnion.val;
}

__u8 KUNPENG_PMU::ReadOnce(__u8 *head)
{
    union {
        typeof(*head) val;
        char charHead[1];
    } pointerUnion = {.charHead = {0}};
#if defined(IS_ARM)
    asm volatile("ldarb %w0, %1"
                 : "=r"(*(__u8 __attribute__((__may_alias__)) *)pointerUnion.charHead)
                 : "Q"(*head)
                 : "memory");
#endif
    return pointerUnion.val;
}