* 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 reading and managing data from a ring buffer in the KUNPENG_PMU namespace
******************************************************************************/
#include <cstring>
#include <unistd.h>
#include <sys/mman.h>
#include "pcerrc.h"
#include "pcerr.h"
#include "evt.h"
#include "sample_process.h"
#define PAGE_SIZE (sysconf(_SC_PAGESIZE))
using namespace pcerr;
void KUNPENG_PMU::PerfMmapReadDone(PerfMmap &map)
{
struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base;
map.prev = ReadOnce(&base->data_head);
}
void CopyDataInWhileLoop(KUNPENG_PMU::PerfMmap& map, __u64 offset, unsigned char* data, __u64 len)
{
* Here, as long as we still have data inside the mmap, we continue to wrap around
* the head and tail pointer to a point that
*/
char *tmpDataPtr = map.copiedEvent;
__u64 copiedData = 0;
while (len) {
__u64 restSize = offset & map.mask;
copiedData = map.mask + 1 - restSize < len ? map.mask + 1 - restSize : len;
memcpy(tmpDataPtr, &data[restSize], copiedData);
offset += copiedData;
tmpDataPtr += copiedData;
len -= copiedData;
}
}
static inline union KUNPENG_PMU::PerfEvent *PerfMmapRead(KUNPENG_PMU::PerfMmap &map, __u64 *startPointer, __u64 end)
{
* Logic for reading ringbuffer
*/
unsigned char *data = (unsigned char *)map.base + PAGE_SIZE;
union KUNPENG_PMU::PerfEvent *event = nullptr;
__u64 diff = end - *startPointer;
if (diff >= sizeof(event->header)) {
size_t size;
event = (union KUNPENG_PMU::PerfEvent *)&data[*startPointer & map.mask];
size = event->header.size;
if (__glibc_unlikely(size == 0)) {
New(LIBPERF_ERR_BUFFER_CORRUPTED);
return nullptr;
}
if (__glibc_unlikely(size < sizeof(event->header) || diff < size)) {
return nullptr;
}
size_t event_size = sizeof(*event);
if ((*startPointer & map.mask) + size != ((*startPointer + size) & map.mask)) {
__u64 offset = *startPointer;
__u64 len = event_size < size ? event_size : size;
CopyDataInWhileLoop(map, offset, data, len);
event = (union KUNPENG_PMU::PerfEvent *)map.copiedEvent;
}
*startPointer += size;
}
return event;
}
union KUNPENG_PMU::PerfEvent *KUNPENG_PMU::ReadEvent(PerfMmap &map)
{
if (!map.overwrite) {
struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base;
map.end = ReadOnce(&base->data_head);
}
union KUNPENG_PMU::PerfEvent *event = PerfMmapRead(map, &map.start, map.end);
if (!map.overwrite) {
map.prev = map.start;
}
return event;
}
int KUNPENG_PMU::RingbufferReadInit(PerfMmap &map)
{
__u64 head = ReadOnce(&map.base->data_head);
__u64 prev = map.prev;
unsigned long size;
map.start = map.overwrite ? head : prev;
map.end = map.overwrite ? prev : head;
if (__glibc_unlikely((map.end - map.start) < map.flush)) {
return UNKNOWN_ERROR;
}
size = map.end - map.start;
if (size > (unsigned long)(map.mask) + 1) {
if (!map.overwrite) {
map.prev = head;
PerfMmapConsume(map);
}
}
return SUCCESS;
}