Llijindong (C)重构Clear模块
481191d8创建于 2025年11月28日历史提交
/******************************************************************************
 * 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.Li
 * Create: 2024-08-05
 * Description: start a thread to observe new fork thread.
 ******************************************************************************/
#include <linux/perf_event.h>
#include <sys/mman.h>
#include <vector>
#include <sys/ioctl.h>

#include "log.h"
#include "dummy_event.h"

static const int PAGE_SIZE = sysconf(_SC_PAGESIZE);
static const int MAP_LEN = 69632;

namespace KUNPENG_PMU {
    
    DummyEvent::~DummyEvent()
    {
        dummyFlag = false;
        if (dummyThread.joinable()) {
            dummyThread.join();
        }
        for (auto it = dummyMap.begin(); it != dummyMap.end(); it++) {
            close(it->second.first);
            munmap(it->second.second, MAP_LEN);
        }

        hasDataCond.notify_all();

        if (consumeThread.joinable()) {
            consumeThread.join();
        }
    }

    void DummyEvent::ObserverForkThread()
    {
        dummyThread = std::thread([this]() {
            for (const auto& pid: ppids) {
                this->InitDummy(pid);
            }
            while (dummyFlag) {
                this->HandleDummyData();
            }
        });

        consumeThread = std::thread([this]() {
            while (dummyFlag) {
                ConsumeForkQueue();
            }
        });
    }

    void DummyEvent::InitDummy(pid_t pid)
    {
        struct perf_event_attr attr = {0};
        attr.size = sizeof(attr);
        attr.type = PERF_TYPE_SOFTWARE;
        attr.config = PERF_COUNT_SW_DUMMY;
        attr.exclude_kernel = 1;
        attr.disabled = 1;
        attr.sample_period = 1;
        attr.sample_type = PERF_SAMPLE_TIME;
        attr.sample_id_all = 1;
        attr.read_format = PERF_FORMAT_ID;
        attr.task = 1;
        attr.exclude_guest = 1;
        auto fd = PerfEventOpen(&attr, pid, -1, -1, 0);
        if (fd == -1) {
            ERR_PRINT("Failed open dummy event fd because of %s\n", strerror(errno));
            return;
        }

        if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) != 0) {
            ERR_PRINT("Failed enable dummy event fd\n");
            return;
        }

        auto fdMap = mmap(nullptr, MAP_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (fdMap == MAP_FAILED) {
            ERR_PRINT("Failed mmap dummy fd\n");
            return;
        }

        dummyMap.insert({pid, std::make_pair(fd, fdMap)});
    }

    void DummyEvent::ConsumeForkQueue() {
        std::unique_lock<std::mutex> lg(dummyMutex);
        if (forkPidQueue.empty()) {
            hasDataCond.wait(lg);
        }
        auto &pid = forkPidQueue.front();
        PmuList::GetInstance()->AddNewProcess(pd, pid);
        forkPidQueue.pop();
    }

    void DummyEvent::HandleDummyData()
    {
        if (dummyMap.empty()) {
            dummyFlag = false;
        }
        for (const auto& it: dummyMap) {
            this->ParseDummyData(it.second.second, it.first);
        }
        this->ClearExitProcess();
    }

    void DummyEvent::ParseDummyData(void* page, pid_t pid)
    {
        auto* mapPage = (struct perf_event_mmap_page*) page;
        uint8_t* ringBuf = (uint8_t*) (mapPage) + PAGE_SIZE;
        uint64_t dataHead = mapPage->data_head;
        uint64_t dataTail = mapPage->data_tail;
        while (dataTail < dataHead) {
            uint64_t off = dataTail % mapPage->data_size;
            auto* header = (struct perf_event_header*) (ringBuf + off);
            if (header->type == PERF_RECORD_FORK) {
                auto sample = (KUNPENG_PMU::PerfRecordFork*) header;
                std::lock_guard<std::mutex> lg(dummyMutex);
                if((uint8_t*)page + MAP_LEN > ringBuf + off + sizeof(KUNPENG_PMU::PerfRecordFork)) {
                    forkPidQueue.push(sample->tid);
                    hasDataCond.notify_all();
                }
            }
            if (header->type == PERF_RECORD_EXIT) {
                auto sample = (KUNPENG_PMU::PerfRecordFork*) header;
                if (sample->pid == sample->tid && sample->pid == pid) {
                    exitPids.push_back(pid);
                }
            }
            dataTail += header->size;
        }
        mapPage->data_tail = mapPage->data_head;
    }

    void DummyEvent::ClearExitProcess()
    {
        if (exitPids.empty()) {
            return;
        }
        for (const auto& pid: exitPids) {
            if (dummyMap.find(pid) == dummyMap.end()) {
                continue;
            }
            std::pair<pid_t, void*> pair = dummyMap.at(pid);
            dummyMap.erase(pid);
            close(pair.first);
            munmap(pair.second, MAP_LEN);
        }
        exitPids.clear();
    }
}