From 00504bab2c0233a5dcf0c6af9864514de09f4fa9 Mon Sep 17 00:00:00 2001 From: Galaxy Date: Sat, 13 Apr 2024 03:03:09 -0700 Subject: [PATCH 1/2] Add some unit tests for API, test for: - Input different cpu list, pid list to PmuOpen - Input different duration to PmuCollect - Sample process and check pmu data for SAMPLING and SPE_SAMPLING - Sample system and check pmu data for SAMPLING and SPE_SAMPLING --- CMakeLists.txt | 3 +- Common.cmake | 5 + pmu/evt.cpp | 4 + pmu/sample_process.cpp | 5 + pmu/spe.h | 6 + pmu_main.cpp | 207 +++++++++++++ test/CMakeLists.txt | 3 + test/test_perf/CMakeLists.txt | 14 + test/test_perf/case/CMakeLists.txt | 11 + test/test_perf/case/pwritev_file.cpp | 27 ++ test/test_perf/case/simple.cpp | 27 ++ test/test_perf/case/test_12threads.cpp | 31 ++ test/test_perf/case/test_create_thread.cpp | 37 +++ test/test_perf/case/test_fork.cpp | 38 +++ test/test_perf/case/write_on_numa2.cpp | 20 ++ test/test_perf/test_api.cpp | 337 +++++++++++++++++++++ test/test_perf/test_common.cpp | 154 ++++++++++ test/test_perf/test_common.h | 44 +++ 18 files changed, 972 insertions(+), 1 deletion(-) create mode 100644 pmu_main.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/test_perf/CMakeLists.txt create mode 100644 test/test_perf/case/CMakeLists.txt create mode 100644 test/test_perf/case/pwritev_file.cpp create mode 100644 test/test_perf/case/simple.cpp create mode 100644 test/test_perf/case/test_12threads.cpp create mode 100644 test/test_perf/case/test_create_thread.cpp create mode 100644 test/test_perf/case/test_fork.cpp create mode 100644 test/test_perf/case/write_on_numa2.cpp create mode 100644 test/test_perf/test_api.cpp create mode 100644 test/test_perf/test_common.cpp create mode 100644 test/test_perf/test_common.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c7be2..b3bc961 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,5 +34,6 @@ message("TOP_DIR is ${TOP_DIR}") include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) add_subdirectory(symbol) add_subdirectory(pmu) +add_subdirectory(test) -set(CMAKE_EXPORT_COMPILE_COMMANDS True) \ No newline at end of file +set(CMAKE_EXPORT_COMPILE_COMMANDS True) diff --git a/Common.cmake b/Common.cmake index 030dac1..357aacb 100644 --- a/Common.cmake +++ b/Common.cmake @@ -32,3 +32,8 @@ add_library(dwarf_static STATIC IMPORTED) set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${THIRD_PARTY}/local/elfin-parser/libdwarf++.a) include_directories(${THIRD_PARTY}/elfin-parser/dwarf) include_directories(${THIRD_PARTY}/elfin-parser/elf) + +add_library(gtest_main STATIC IMPORTED) +set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION /usr/local/lib64/libgtest_main.a) +add_library(gtest STATIC IMPORTED) +set_property(TARGET gtest PROPERTY IMPORTED_LOCATION /usr/local/lib64/libgtest.a) diff --git a/pmu/evt.cpp b/pmu/evt.cpp index 84fdeea..e3cd34e 100644 --- a/pmu/evt.cpp +++ b/pmu/evt.cpp @@ -88,6 +88,9 @@ int KUNPENG_PMU::PerfEvt::Pause() __u64 KUNPENG_PMU::ReadOnce(__u64 *head) { +#ifndef __aarch64__ + return *head; +#else union { typeof(*head) val; char charHead[1]; @@ -122,4 +125,5 @@ __u64 KUNPENG_PMU::ReadOnce(__u64 *head) break; } return pointerUnion.val; +#endif } diff --git a/pmu/sample_process.cpp b/pmu/sample_process.cpp index 79a20e7..d223ee6 100644 --- a/pmu/sample_process.cpp +++ b/pmu/sample_process.cpp @@ -33,9 +33,14 @@ static constexpr int MAX_DATA_SIZE = 8192; void KUNPENG_PMU::PerfMmapConsume(PerfMmap &map) { + __u64 prev = map.prev; struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base; +#ifndef __aarch64__ + base->data_tail = prev; +#else PerfRingbufferSmpStoreRelease(&base->data_tail, prev); +#endif } void KUNPENG_PMU::PerfMmapReadDone(PerfMmap &map) diff --git a/pmu/spe.h b/pmu/spe.h index 7e5697f..579ab13 100644 --- a/pmu/spe.h +++ b/pmu/spe.h @@ -28,9 +28,15 @@ #include "pmu_event.h" #include "symbol.h" +#ifndef __aarch64__ +#define MB() +#define RMB() +#define WMB() +#else #define MB() asm volatile("dsb sy") #define RMB() asm volatile("dsb ld") #define WMB() asm volatile("dsb st") +#endif #define EVENT_EXCEPTION_GEN 0x1 #define EVENT_RETIRED 0x2 diff --git a/pmu_main.cpp b/pmu_main.cpp new file mode 100644 index 0000000..9d8547a --- /dev/null +++ b/pmu_main.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +using namespace std; + +static pid_t pid = 0; + +void Sample2() +{ + int numCpu = 1; + int numPid = 1; + int numEvt = 1; + int *cpuList = (int*)malloc(sizeof(int)*numCpu); + + int *pidList = (int*)malloc(sizeof(int)*numPid); + pidList[0] = pid; + char *evtList[1]; + evtList[0] = "cycles"; + + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 1; + attr.pidList = pidList; + attr.numPid = 1; + attr.freq = 1000; + attr.useFreq = 1; + + int pd = PmuOpen(SAMPLING, &attr); + if (pd == -1) { + cout << Perror() << "\n"; + return; + } + + int i=3; + int err = PmuEnable(pd); + if (err != SUCCESS) { + cout << "Error pmuEnable\n"; + return; + } + + PmuData *sum = nullptr; + int total = 0; + while(i>0) { + sleep(1); + struct PmuData *pmuData = nullptr; + int len = PmuRead(pd, &pmuData); + total = PmuAppendData(pmuData, &sum); + + for (int j=0;jcomm << " cpu: " << data->cpu << " pid: " << data->pid << " tid: " << data->tid + << " ts: " << data->ts << endl; + Stack *stack = data->stack; + while( stack) { + if (stack->symbol) { + cout << std::hex << stack->symbol->addr << std::dec << " " << stack->symbol->symbolName << " " << stack->symbol->module << endl; + } + stack = stack->next; + } + cout << endl; + break; + } + + printf("len: %d\n", len); + PmuDataFree(pmuData); + i--; + } + err = PmuDisable(pd); + printf("total: %d\n", total); + PmuClose(pd); + +} + +void SpeSample2() +{ + int numCpu = 1; + int numPid = 1; + int numEvt = 1; + int *cpuList = (int*)malloc(sizeof(int)*numCpu); + + int *pidList = (int*)malloc(sizeof(int)*numPid); + pidList[0] = pid; + + PmuAttr attr = {0}; + attr.evtList = nullptr; + attr.numEvt = 0; + attr.pidList = pidList; + attr.numPid = 1; + attr.period = 1000; + attr.dataFilter = SPE_DATA_ALL; + attr.evFilter = SPE_EVENT_RETIRED; + attr.minLatency = 0x40; + + int pd = PmuOpen(SPE_SAMPLING, &attr); + if (pd == -1) { + cout << Perror() << "\n"; + return; + } + + int i=3; + int err = PmuEnable(pd); + if (err != SUCCESS) { + cout << "Error pmuEnable\n"; + return; + } + + PmuData *sum = nullptr; + int total = 0; + while(i>0) { + sleep(1); + struct PmuData *pmuData = nullptr; + int len = PmuRead(pd, &pmuData); + total = PmuAppendData(pmuData, &sum); + + for (int j=0;jcomm << " cpu: " << data->cpu << " pid: " << data->pid << " tid: " << data->tid + << " ts: " << data->ts << endl; + Stack *stack = data->stack; + while( stack) { + if (stack->symbol) { + cout << std::hex << stack->symbol->addr << std::dec << " " << stack->symbol->symbolName << " " << stack->symbol->module << endl; + } + stack = stack->next; + } + cout << endl; + break; + } + + printf("len: %d\n", len); + PmuDataFree(pmuData); + i--; + } + err = PmuDisable(pd); + printf("total: %d\n", total); + PmuClose(pd); + +} + +void Waas() +{ + vector traceEvts = { + "net:netif_rx", + "hisi_sccl7_hha5/rx_outer/", + "hisi_sccl7_hha5/rx_sccl/", + }; + + int cpuList[1]; + cpuList[0] = 0; + PmuAttr attr = {0}; + attr.evtList = traceEvts.data(); + attr.numEvt = traceEvts.size(); + + int pd = PmuOpen(COUNTING, &attr); + + for (int i=0;i<10;++i) { + int ret = PmuCollect(pd, 1000); + PmuData *data = nullptr; + int len = PmuRead(pd, &data); + map evtMap; + for (int j=0;j +#include +#include +#include +#include +#include + +int main() +{ + char *str0 = "hello "; + char *str1 = "world\n"; + + struct iovec iov[2]; + ssize_t nwritten; + + iov[0].iov_base = str0; + iov[0].iov_len = strlen(str0); + iov[1].iov_base = str1; + iov[1].iov_len = strlen(str1); + + int fd = open("/tmp/libperf_ut_write", O_RDWR | O_CREAT); + for (int i = 0;i < 1024 * 64 * 1024; ++i) { + pwritev(fd, iov, 2, 0); + } + close(fd); + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/simple.cpp b/test/test_perf/case/simple.cpp new file mode 100644 index 0000000..f3bd6de --- /dev/null +++ b/test/test_perf/case/simple.cpp @@ -0,0 +1,27 @@ +#include +#include + +using namespace std; + +int len = 1000000; + +struct Data { + volatile int a; + volatile int b; +}; + +void func(struct Data *data) +{ + while (1) { + for (int i = 0; i< len;++i) + for (int j = 0; j< len;++j) + data->a++; + } +} + +int main() +{ + struct Data data; + func(&data); + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/test_12threads.cpp b/test/test_perf/case/test_12threads.cpp new file mode 100644 index 0000000..949c7e6 --- /dev/null +++ b/test/test_perf/case/test_12threads.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +#define MAX 1000000 + +int primes[MAX]; +int num_primes = 0; + +int func() { + int ret = 0; + for (int i=0;;++i) { + ret += rand()%7; + } + return ret; +} + +int main() +{ + pthread_t threads[12]; + + for (int i=0;i<12;++i) { + pthread_create(&threads[i], NULL, (void* (*)(void*))func, NULL); + } + + for (int i=0;i<12;++i) { + pthread_join(threads[i], NULL); + } + + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/test_create_thread.cpp b/test/test_perf/case/test_create_thread.cpp new file mode 100644 index 0000000..fc6611e --- /dev/null +++ b/test/test_perf/case/test_create_thread.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +int func() +{ + int ret = 0; + for (int i=0;;++i) { + ret += rand() % 7; + } + return ret; +} + +void *thread_func1(void *arg) +{ + func(); + return NULL; +} + +void *thread_func2(void *arg) +{ + func(); + return NULL; +} + +int main() +{ + raise(SIGSTOP); + pthread_t tid1, tid2; + pthread_create(&tid1, NULL, thread_func1, NULL); + pthread_create(&tid2, NULL, thread_func2, NULL); + pthread_join(tid1, NULL); + pthread_join(tid2, NULL); + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/test_fork.cpp b/test/test_perf/case/test_fork.cpp new file mode 100644 index 0000000..3be22bc --- /dev/null +++ b/test/test_perf/case/test_fork.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +struct Data { + volatile int a; + volatile int b; +}; + +int func(struct Data *data) +{ + while (1) { + for (int i=0;i<1000000;++i) { + data->a++; + } + } + return 1; +} + +int main() +{ + raise(SIGSTOP); + pid_t pid_c; + pid_c = fork(); + if (pid_c == 0) { + struct Data data; + int ret = func(&data); + } else if (pid_c > 0) { + wait(NULL); + } else { + perror("fork"); + exit(1); + } + + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/write_on_numa2.cpp b/test/test_perf/case/write_on_numa2.cpp new file mode 100644 index 0000000..7241995 --- /dev/null +++ b/test/test_perf/case/write_on_numa2.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include +#include + +int main() { + raise(SIGSTOP); + usleep(10000); + + int len = 1024*256; + for (int j=0;j<64;++j) { + int *data = (int *)numa_alloc_onnode(len * sizeof(int), 2); + for (int i=0;i +#include +#include "util_time.h" +#include "process_map.h" +#include "common.h" +#include "test_common.h" + +using namespace std; +class TestAPI : public testing::Test { +public: + static void SetUpTestCase() + { + // Bind test_perf to cpu 0 and bind test app to cpu 1, for stable collection. + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(0, &mask); + sched_setaffinity(0, sizeof(mask), &mask); + + // Create a simple process. + demoPid = RunTestApp(exePath); + cout << "pid: " << demoPid << endl; + } + + static void TearDownTestCase() + { + KillApp(demoPid); + } + + void TearDown() + { + if (data != nullptr) { + PmuDataFree(data); + data = nullptr; + } + PmuClose(pd); + for (int i = 0; i< pdNums; ++i) { + PmuClose(pds[i]); + } + } + +protected: + bool HasExpectSource(PmuData *data, int len) const + { + // Check whether pmu data contains expected filename and line number. + bool foundData = false; + for (int i = 0; i < len; ++i) { + auto stack = data[i].stack; + while (stack) { + if (stack->symbol) { + if (basename(stack->symbol->fileName) == expectFilename && stack->symbol->lineNum == expectLine) { + foundData = true; + break; + } + } + stack = stack->next; + } + if (foundData) { + break; + } + } + + return foundData; + } + + bool HasExpectSymbol(PmuData *data, int len) const + { + bool foundData = false; + for (int i = 0; i < len; ++i) { + auto stack = data[i].stack; + while (stack) { + if (stack->symbol) { + if (basename(stack->symbol->module) == exePath) { + foundData = true; + break; + } + } + stack = stack->next; + } + if (foundData) { + break; + } + } + + return foundData; + } + + bool CheckDataEvent(PmuData *data, int len, string evtName) const + { + for (int i = 0; i < len; ++i) { + if (string(data[i].evt) != evtName) { + return false; + } + } + + return true; + } + + PmuAttr GetPmuAttribute() + { + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = numEvt; + attr.pidList = pidList; + attr.numPid = numPid; + attr.cpuList = cpuList; + attr.numCpu = numCpu; + attr.freq = 1000; + attr.useFreq = 1; + return attr; + } + + PmuAttr GetSpeAttribute() + { + PmuAttr attr = {0}; + attr.pidList = pidList; + attr.numPid = numPid; + attr.cpuList = cpuList; + attr.numCpu = numCpu; + attr.period = 1000; + attr.dataFilter = SPE_DATA_ALL; + attr.evFilter = SPE_EVENT_RETIRED; + attr.minLatency = 0x40; + return attr; + } + +protected: + static const string exePath; + static pid_t demoPid; + + static const unsigned numCpu = 0; + static const unsigned numPid = 1; + static const unsigned numEvt = 1; + static const string expectFilename; + static const unsigned expectLine = 17; + + int *cpuList = nullptr; + char *evtList[numEvt] = {"cycles"}; + int pidList[numPid] = {demoPid}; + int pd = 0; + static const int pdNums = 2; + int pds[pdNums] = {0}; + PmuData *data = nullptr; +}; + +pid_t TestAPI::demoPid; +const string TestAPI::exePath = "simple"; +const string TestAPI::expectFilename = "simple.cpp"; + +TEST_F(TestAPI, SampleInitSuccess) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen(SAMPLING, &attr); + ASSERT_TRUE(pd != -1); +} + +TEST_F(TestAPI, SampleCollectSuccess) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen(SAMPLING, &attr); + int ret = PmuCollect(pd, 10); + ASSERT_TRUE(ret == SUCCESS); +} + +TEST_F(TestAPI, SampleReadSuccess) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen(SAMPLING, &attr); + int ret = PmuCollect(pd, 1000); + int len = PmuRead(pd, &data); + EXPECT_TRUE(data != nullptr); + ASSERT_TRUE(HasExpectSource(data, len)); +} + +TEST_F(TestAPI, SpeInitSuccess) +{ + auto attr = GetSpeAttribute(); + pd = PmuOpen(SPE_SAMPLING, &attr); + ASSERT_TRUE(pd != -1); +} + +TEST_F(TestAPI, SpeCollectSuccess) +{ + auto attr = GetSpeAttribute(); + pd = PmuOpen(SPE_SAMPLING, &attr); + ASSERT_TRUE(pd != -1); + int ret = PmuCollect(pd, 10); + ASSERT_TRUE(ret == SUCCESS); +} + +TEST_F(TestAPI, SpeReadSuccess) +{ + auto attr = GetSpeAttribute(); + pd = PmuOpen(SPE_SAMPLING, &attr); + int ret = PmuCollect(pd, 1000); + ASSERT_TRUE(pd != -1); + int len = PmuRead(pd, &data); + EXPECT_TRUE(data != nullptr); + ASSERT_TRUE(HasExpectSymbol(data, len)); +} + +TEST_F(TestAPI, InitSampleNullEvt) +{ + auto attr = GetPmuAttribute(); + attr.evtList = nullptr; + pd = PmuOpen(SAMPLING, &attr); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_EVTLIST); +} + +TEST_F(TestAPI, InitCountNullEvt) +{ + auto attr = GetPmuAttribute(); + attr.evtList = nullptr; + pd = PmuOpen(COUNTING, &attr); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_EVTLIST); +} + +TEST_F(TestAPI, InitBadPid) +{ + auto attr = GetPmuAttribute(); + attr.pidList[0] = -1; + pd = PmuOpen(COUNTING, &attr); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_PIDLIST); +} + +TEST_F(TestAPI, InitBadCpu) +{ + auto attr = GetPmuAttribute(); + attr.cpuList = new int[4]; + attr.numCpu = 4; + attr.cpuList[0] = 5000; + pd = PmuOpen(COUNTING, &attr); + delete[] attr.cpuList; + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_CPULIST); +} + +TEST_F(TestAPI, SampleCollectBadEvt) +{ + auto attr = GetPmuAttribute(); + attr.evtList[0] = "abc"; + pd = PmuOpen(SAMPLING, &attr); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_EVENT); +} + +TEST_F(TestAPI, SampleCollectBadPd) +{ + auto ret = PmuCollect(3, 1000); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_PD); +} + +TEST_F(TestAPI, SpeInitBusy) +{ + auto attr = GetSpeAttribute(); + pd = PmuOpen(SPE_SAMPLING, &attr); + ASSERT_TRUE(pd != -1); + int badPd = PmuOpen(SPE_SAMPLING, &attr); + ASSERT_TRUE(badPd == -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_DEVICE_BUSY); + PmuClose(badPd); +} + +TEST_F(TestAPI, SampleSystem) +{ + auto attr = GetPmuAttribute(); + attr.pidList = nullptr; + attr.numPid = 0; + pd = PmuOpen(SAMPLING, &attr); + int ret = PmuCollect(pd, 100); + int len = PmuRead(pd, &data); + EXPECT_TRUE(data != nullptr); + ASSERT_TRUE(HasExpectSource(data, len)); +} + +TEST_F(TestAPI, SpeSystem) +{ + auto attr = GetSpeAttribute(); + attr.pidList = nullptr; + attr.numPid = 0; + pd = PmuOpen(SPE_SAMPLING, &attr); + ASSERT_TRUE(pd != -1); + int ret = PmuCollect(pd, 1000); + int len = PmuRead(pd, &data); + EXPECT_TRUE(data != nullptr); + ASSERT_TRUE(HasExpectSymbol(data, len)); +} + +static void Stop(int pd) +{ + sleep(2); + PmuStop(pd); +} + +TEST_F(TestAPI, StopSuccess) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen(SAMPLING, &attr); + thread th(Stop, pd); + auto start = GetCurrentTime(); + int ret = PmuCollect(pd, 1000 * 10); + auto end = GetCurrentTime(); + th.join(); + ASSERT_LE(end - start, 5000); +} + +TEST_F(TestAPI, OpenInvalidTaskType) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen((PmuTaskType)99, &attr); + ASSERT_TRUE(pd == -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_TASK_TYPE); +} + +TEST_F(TestAPI, CollectInvalidTime) +{ + auto attr = GetPmuAttribute(); + pd = PmuOpen(SAMPLING, &attr); + int ret = PmuCollect(pd, -2); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_TIME); +} + +TEST_F(TestAPI, RaiseNumFd) +{ + struct rlimit currentlim; + ASSERT_NE(getrlimit(RLIMIT_NOFILE, ¤tlim), -1); + auto err = RaiseNumFd(currentlim.rlim_max + 1); + ASSERT_EQ(err, LIBPERF_ERR_TOO_MANY_FD); + err = RaiseNumFd(currentlim.rlim_cur - 1); + ASSERT_EQ(err, SUCCESS); + err = RaiseNumFd(currentlim.rlim_max - 1); + ASSERT_EQ(err, SUCCESS); + err = RaiseNumFd(currentlim.rlim_max - 51); + ASSERT_EQ(err, SUCCESS); +} \ No newline at end of file diff --git a/test/test_perf/test_common.cpp b/test/test_perf/test_common.cpp new file mode 100644 index 0000000..052b93b --- /dev/null +++ b/test/test_perf/test_common.cpp @@ -0,0 +1,154 @@ +#include +#include +#include "process_map.h" +#include "test_common.h" +using namespace std; + +pid_t RunTestApp(const string &name) +{ + char myDir[PATH_MAX] = {0}; + readlink("/proc/self/exe", myDir, sizeof(myDir) - 1); + auto pid = vfork(); + if (pid == 0) { + // Bind test_perf to cpu 0 and bind test app to cpu 1. + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(1, &mask); + sched_setaffinity(0, sizeof(mask), &mask); + setpgid(0, 0); + string fullPath = string(dirname(myDir)) + "/case/" + name; + char *const *dummy = nullptr; + execvp(fullPath.c_str(), dummy); + _exit(errno); + } + + return pid; +} + +void KillApp(pid_t pid) +{ + killpg(pid, SIGKILL); +} + +void DumpPmuData(const PmuData *data) +{ + cout << "evt: " << data->evt << " pid: " << data->pid << " comm: " << data->comm; + auto stack = data->stack; + if (stack != nullptr && stack->symbol != nullptr) { + cout << " sym: " << stack->symbol->symbolName << " mod: " << stack->symbol->module + << " src: " << stack->symbol->fileName << ":" << stack->symbol->lineNum; + } + cout << "\n"; +} + +bool FoundAllTids(PmuData *data, int len, pid_t pid) +{ + set sampledTid; + for (int i=0;i GetChildPid(pid_t pid) +{ + string childPath = "/proc/" + to_string(pid) + "/task/" + to_string(pid) + "/children"; + ifstream in(childPath); + vector ret; + while (!in.eof()) { + string pidStr; + in >> pidStr; + if (pidStr.empty()) { + continue; + } + ret.push_back(stoi(pidStr)); + } + + return ret; +} + + +bool FoundAllChildren(PmuData *data, int len, pid_t pid) +{ + // Find all subprocess pids from in pmu data. + // Return true if all children are found. + set sampledPid; + for (int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcerrc.h" +#include "symbol.h" +#include "pmu.h" + +pid_t RunTestApp(const std::string &name); + +void KillApp(pid_t pid); + +void DumpPmuData(const PmuData *data); + +bool FoundAllTids(PmuData *data, int len, pid_t pid); + +std::vector GetChildPid(pid_t pid); + +bool FoundAllChildren(PmuData *data, int len, pid_t pid); + +void DelayContinue(pid_t pid, int milliseconds); + +unsigned GetCpuNums(); + +// Check whether event names of all data are . +bool CheckDataEvt(PmuData *data, int len, std::string evt); + +// Check whether pid of all data are . +bool CheckDataPid(PmuData *data, int len, int pid); + +// Check whether at least one event name of data is . +bool HasEvent(PmuData *data, int len, std::string evt); + +#endif \ No newline at end of file -- Gitee From ad016f42943394398c86c8ed7309f7e5f9a3e00a Mon Sep 17 00:00:00 2001 From: Galaxy Date: Sun, 21 Apr 2024 17:40:32 -0700 Subject: [PATCH 2/2] Update README --- CMakeLists.txt | 1 - Common.cmake | 16 ++- README.en.md | 65 ++++++++- build.sh | 3 +- pmu/CMakeLists.txt | 2 +- pmu/evt.cpp | 4 - pmu/pmu_list.cpp | 11 +- pmu/sample_process.cpp | 4 - pmu/spe.h | 6 - pmu_main.cpp | 207 ----------------------------- symbol/CMakeLists.txt | 2 +- test/test_perf/case/CMakeLists.txt | 5 +- 12 files changed, 85 insertions(+), 241 deletions(-) delete mode 100644 pmu_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b3bc961..761fc49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,5 @@ message("TOP_DIR is ${TOP_DIR}") include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) add_subdirectory(symbol) add_subdirectory(pmu) -add_subdirectory(test) set(CMAKE_EXPORT_COMPILE_COMMANDS True) diff --git a/Common.cmake b/Common.cmake index 357aacb..2aac046 100644 --- a/Common.cmake +++ b/Common.cmake @@ -33,7 +33,19 @@ set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${THIRD_PARTY}/local include_directories(${THIRD_PARTY}/elfin-parser/dwarf) include_directories(${THIRD_PARTY}/elfin-parser/elf) +find_library(gtest_main_path libgtest_main.a /usr/local/lib64 /usr/local/lib) +if (${gtest_main_path} STREQUAL "gtest_main_path-NOTFOUND") + message (STATUS "required gtest_main library but not found!") +else() + message (STATUS "gtest_main library found in ${gtest_main_path}") +endif() add_library(gtest_main STATIC IMPORTED) -set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION /usr/local/lib64/libgtest_main.a) +set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${gtest_main_path}) +find_library(gtest_path libgtest.a /usr/local/lib64 /usr/local/lib) +if (${gtest_path} STREQUAL "gtest_path-NOTFOUND") + message (STATUS "required gtest library but not found!") +else() + message (STATUS "gtest library found in ${gtest_path}") +endif() add_library(gtest STATIC IMPORTED) -set_property(TARGET gtest PROPERTY IMPORTED_LOCATION /usr/local/lib64/libgtest.a) +set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${gtest_path}) diff --git a/README.en.md b/README.en.md index 408cd64..f9ee122 100644 --- a/README.en.md +++ b/README.en.md @@ -4,19 +4,70 @@ Implement a low overhead pmu collection library, providing abstract interfaces for counting, sampling and symbol resolve. #### Software Architecture -Software architecture description +This repo includes two modules: pmu collections and symbol resolve. + +Pmu collection module is developed on syscall perf_event_open to enable kernel pmu counting and sampling, using -thread or per-core mode depending on user input. +Pmu data packets are read from ring buffer and are parsed to different structure for counting, sampling and spe sampling. +For sampling, symbols are resolved according to ips or pc from data packet. Each symbol contains symbol name, address, source file path and line number if possible. + +Symbol resolve module is developed on elfin-parser, a library for parsing elf and dwarf. The module manages all symbol data in well-designed data structures for fast query. #### Installation +Run bash script: -1. xxxx -2. xxxx -3. xxxx +```sh +sh build.sh +``` +Headers and libraries will be installed to ./output directory. #### Instructions +All pmu functions are accomplished by the following interfaces: +* PmuOpen + Input pid, core id and event and Open pmu device. +* PmuEnable + Start collection. +* PmuRead + Read pmu data and a list is returned. +* PmuDisable + Stop collection. +* PmuClose + Close pmu device. + +Here are some examples: +* Get pmu count for a process. +``` +int pidList[1]; +pidList[0] = pid; +char *evtList[1]; +evtList[0] = "cycles"; +// Initialize event list and pid list in PmuAttr. +// There is one event in list, named 'cycles'. +PmuAttr attr = {0}; +attr.evtList = evtList; +attr.numEvt = 1; +attr.pidList = pidList; +attr.numPid = 1; +// Call PmuOpen and pmu descriptor is return. +// is an identity for current task. +int pd = PmuOpen(COUNTING, &attr); +// Start collection. +PmuEnable(pd); +// Collect for one second. +sleep(1); +// Stop collection. +PmuDisable(pd); +PmuData *data = NULL; +// Read pmu data. You can also read data before PmuDisable. +int len = PmuRead(pd, &data); +for (int i = 0; i < len; ++i) { + ... +} +// To free PmuData, call PmuDataFree. +PmuDataFree(data); +// Like fd, call PmuClose if pd will not be used. +PmuClose(pd); +``` -1. xxxx -2. xxxx -3. xxxx #### Contribution diff --git a/build.sh b/build.sh index 0d56a7d..ae8924a 100644 --- a/build.sh +++ b/build.sh @@ -73,4 +73,5 @@ main() { build_libprof } -main $@ \ No newline at end of file +main $@ + diff --git a/pmu/CMakeLists.txt b/pmu/CMakeLists.txt index d344009..189e43e 100644 --- a/pmu/CMakeLists.txt +++ b/pmu/CMakeLists.txt @@ -33,6 +33,6 @@ include_directories(${PMU_DECODER_DIR}) ADD_LIBRARY(kperf SHARED ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) target_link_libraries(kperf numa sym) target_compile_options(kperf PRIVATE -fPIC) -install(TARGETS kperf DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(TARGETS kperf DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) file(GLOB HEADER_FILES ${PROJECT_TOP_DIR}/include/*.h) install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/pmu/evt.cpp b/pmu/evt.cpp index e3cd34e..84fdeea 100644 --- a/pmu/evt.cpp +++ b/pmu/evt.cpp @@ -88,9 +88,6 @@ int KUNPENG_PMU::PerfEvt::Pause() __u64 KUNPENG_PMU::ReadOnce(__u64 *head) { -#ifndef __aarch64__ - return *head; -#else union { typeof(*head) val; char charHead[1]; @@ -125,5 +122,4 @@ __u64 KUNPENG_PMU::ReadOnce(__u64 *head) break; } return pointerUnion.val; -#endif } diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index 691ecfb..42a4382 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -73,13 +73,18 @@ namespace KUNPENG_PMU { if (err != SUCCESS) { return err; } - fdNum += cpuTopoList.size(); + procTopoList.size(); + fdNum += cpuTopoList.size() + procTopoList.size(); std::shared_ptr evtList = std::make_shared(cpuTopoList, procTopoList, pmuTaskAttrHead->pmuEvt); InsertEvtList(pd, evtList); pmuTaskAttrHead = pmuTaskAttrHead->next; } + auto err = CheckRlimit(fdNum); + if (err != SUCCESS) { + return err; + } + for (auto evtList : GetEvtList(pd)) { auto err = evtList->Init(); if (err != SUCCESS) { @@ -91,10 +96,6 @@ namespace KUNPENG_PMU { } } - auto err = CheckRlimit(fdNum); - if (err != SUCCESS) { - return err; - } return SUCCESS; } diff --git a/pmu/sample_process.cpp b/pmu/sample_process.cpp index d223ee6..56a92a4 100644 --- a/pmu/sample_process.cpp +++ b/pmu/sample_process.cpp @@ -36,11 +36,7 @@ void KUNPENG_PMU::PerfMmapConsume(PerfMmap &map) __u64 prev = map.prev; struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base; -#ifndef __aarch64__ - base->data_tail = prev; -#else PerfRingbufferSmpStoreRelease(&base->data_tail, prev); -#endif } void KUNPENG_PMU::PerfMmapReadDone(PerfMmap &map) diff --git a/pmu/spe.h b/pmu/spe.h index 579ab13..7e5697f 100644 --- a/pmu/spe.h +++ b/pmu/spe.h @@ -28,15 +28,9 @@ #include "pmu_event.h" #include "symbol.h" -#ifndef __aarch64__ -#define MB() -#define RMB() -#define WMB() -#else #define MB() asm volatile("dsb sy") #define RMB() asm volatile("dsb ld") #define WMB() asm volatile("dsb st") -#endif #define EVENT_EXCEPTION_GEN 0x1 #define EVENT_RETIRED 0x2 diff --git a/pmu_main.cpp b/pmu_main.cpp deleted file mode 100644 index 9d8547a..0000000 --- a/pmu_main.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "symbol.h" -#include "pmu.h" -#include "pcerrc.h" - -using namespace std; - -static pid_t pid = 0; - -void Sample2() -{ - int numCpu = 1; - int numPid = 1; - int numEvt = 1; - int *cpuList = (int*)malloc(sizeof(int)*numCpu); - - int *pidList = (int*)malloc(sizeof(int)*numPid); - pidList[0] = pid; - char *evtList[1]; - evtList[0] = "cycles"; - - PmuAttr attr = {0}; - attr.evtList = evtList; - attr.numEvt = 1; - attr.pidList = pidList; - attr.numPid = 1; - attr.freq = 1000; - attr.useFreq = 1; - - int pd = PmuOpen(SAMPLING, &attr); - if (pd == -1) { - cout << Perror() << "\n"; - return; - } - - int i=3; - int err = PmuEnable(pd); - if (err != SUCCESS) { - cout << "Error pmuEnable\n"; - return; - } - - PmuData *sum = nullptr; - int total = 0; - while(i>0) { - sleep(1); - struct PmuData *pmuData = nullptr; - int len = PmuRead(pd, &pmuData); - total = PmuAppendData(pmuData, &sum); - - for (int j=0;jcomm << " cpu: " << data->cpu << " pid: " << data->pid << " tid: " << data->tid - << " ts: " << data->ts << endl; - Stack *stack = data->stack; - while( stack) { - if (stack->symbol) { - cout << std::hex << stack->symbol->addr << std::dec << " " << stack->symbol->symbolName << " " << stack->symbol->module << endl; - } - stack = stack->next; - } - cout << endl; - break; - } - - printf("len: %d\n", len); - PmuDataFree(pmuData); - i--; - } - err = PmuDisable(pd); - printf("total: %d\n", total); - PmuClose(pd); - -} - -void SpeSample2() -{ - int numCpu = 1; - int numPid = 1; - int numEvt = 1; - int *cpuList = (int*)malloc(sizeof(int)*numCpu); - - int *pidList = (int*)malloc(sizeof(int)*numPid); - pidList[0] = pid; - - PmuAttr attr = {0}; - attr.evtList = nullptr; - attr.numEvt = 0; - attr.pidList = pidList; - attr.numPid = 1; - attr.period = 1000; - attr.dataFilter = SPE_DATA_ALL; - attr.evFilter = SPE_EVENT_RETIRED; - attr.minLatency = 0x40; - - int pd = PmuOpen(SPE_SAMPLING, &attr); - if (pd == -1) { - cout << Perror() << "\n"; - return; - } - - int i=3; - int err = PmuEnable(pd); - if (err != SUCCESS) { - cout << "Error pmuEnable\n"; - return; - } - - PmuData *sum = nullptr; - int total = 0; - while(i>0) { - sleep(1); - struct PmuData *pmuData = nullptr; - int len = PmuRead(pd, &pmuData); - total = PmuAppendData(pmuData, &sum); - - for (int j=0;jcomm << " cpu: " << data->cpu << " pid: " << data->pid << " tid: " << data->tid - << " ts: " << data->ts << endl; - Stack *stack = data->stack; - while( stack) { - if (stack->symbol) { - cout << std::hex << stack->symbol->addr << std::dec << " " << stack->symbol->symbolName << " " << stack->symbol->module << endl; - } - stack = stack->next; - } - cout << endl; - break; - } - - printf("len: %d\n", len); - PmuDataFree(pmuData); - i--; - } - err = PmuDisable(pd); - printf("total: %d\n", total); - PmuClose(pd); - -} - -void Waas() -{ - vector traceEvts = { - "net:netif_rx", - "hisi_sccl7_hha5/rx_outer/", - "hisi_sccl7_hha5/rx_sccl/", - }; - - int cpuList[1]; - cpuList[0] = 0; - PmuAttr attr = {0}; - attr.evtList = traceEvts.data(); - attr.numEvt = traceEvts.size(); - - int pd = PmuOpen(COUNTING, &attr); - - for (int i=0;i<10;++i) { - int ret = PmuCollect(pd, 1000); - PmuData *data = nullptr; - int len = PmuRead(pd, &data); - map evtMap; - for (int j=0;j