diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c7be2b4cb5084a02a2dd474bdd659b0c0616f3..761fc49544a61ffd734122da82b7ff233996cbf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,4 +35,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) add_subdirectory(symbol) add_subdirectory(pmu) -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 030dac19e57a27c8ed557790b35dee19a37d2979..2aac0461ef95f82e5110274486bca13ccb5f8962 100644 --- a/Common.cmake +++ b/Common.cmake @@ -32,3 +32,20 @@ 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) + +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 ${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 ${gtest_path}) diff --git a/README.en.md b/README.en.md index 408cd6424e68d2f7de66286215eda01f9eeabfe6..f9ee1221ea603e00d31e92195a9e1cc442f75182 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 0d56a7def05a20550bf522d89338e78639a914cb..ae8924a15a06d049a216b729fd775781d9506395 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 d3440095412eb1e8c339d82a791ef9f7f570ef6e..189e43e8eb2c3cabda7d8cfcd4632e450eafaaf7 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/pmu_list.cpp b/pmu/pmu_list.cpp index 691ecfb8b2ba42266bda54cde46d38242b4cd6f7..42a4382f0447659ea79326663e15b8c347145cb0 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 79a20e76d489c934aa623c50375e55548fa34ca0..56a92a4556052c541287dfbb0a0f44b290331792 100644 --- a/pmu/sample_process.cpp +++ b/pmu/sample_process.cpp @@ -33,6 +33,7 @@ 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; PerfRingbufferSmpStoreRelease(&base->data_tail, prev); diff --git a/symbol/CMakeLists.txt b/symbol/CMakeLists.txt index e17614b6ff4f4a4c693617bfc24c977d5e97a751..920d59e34034f97592b4676276cbf76ce099a689 100644 --- a/symbol/CMakeLists.txt +++ b/symbol/CMakeLists.txt @@ -17,5 +17,5 @@ include_directories(${INCLUDE_DIR}) message(${THIRD_PARTY}/elfin-parser/elf) ADD_LIBRARY(sym SHARED ${SYMBOL_SRC}) target_link_libraries(sym elf_static dwarf_static pthread) -install(TARGETS sym DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(TARGETS sym DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install(FILES ${SYMBOL_FILE_DIR}/symbol.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2cc6aba2f78d6d4f5cdfef83d4099dd0769d4497 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,3 @@ +project(test) +find_package(Threads) +add_subdirectory(test_perf) diff --git a/test/test_perf/CMakeLists.txt b/test/test_perf/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..74f71021538bc56c72c0513c1cfba0ebe67a80bd --- /dev/null +++ b/test/test_perf/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../symbol) +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../util) +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../pmu) +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../pmu/pfm) +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../pmu/analyzer/metric) +include_directories(${PROJECT_TOP_DIR}/include) +add_compile_options(-g) +set(CMAKE_CXX_STANDARD 14) +aux_source_directory(. SOURCE_SRC) +add_executable(test_perf ${SOURCE_SRC} ${CMAKE_CURRENT_LIST_DIR}/../../util/pcerr.cpp) +target_link_libraries(test_perf sym kperf gtest m gtest_main elf_static dwarf_static pthread -g) +install(TARGETS test_perf DESTINATION ${CMAKE_INSTALL_PREFIX}/ COMPONENT test) + +add_subdirectory(case) diff --git a/test/test_perf/case/CMakeLists.txt b/test/test_perf/case/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..092c611a7e3f2be9f02d5259365bbff73d09cad5 --- /dev/null +++ b/test/test_perf/case/CMakeLists.txt @@ -0,0 +1,12 @@ +set(source_files simple.cpp test_12threads.cpp test_create_thread.cpp test_fork.cpp write_on_numa2.cpp pwritev_file.cpp) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") +set(CMAKE_CXX_FLAGS_RELEASE "") +set(CMAKE_EXE_LINKER_FLAGS_RELEASE "") + +foreach(source_file IN LISTS source_files) + get_filename_component(target_name ${source_file} NAME_WE) + + add_executable("${target_name}" "${source_file}") + target_link_libraries("${target_name}" pthread numa) +endforeach() diff --git a/test/test_perf/case/pwritev_file.cpp b/test/test_perf/case/pwritev_file.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08d55afed4e6ca3dc796bd642dad05d920cf9b0f --- /dev/null +++ b/test/test_perf/case/pwritev_file.cpp @@ -0,0 +1,27 @@ +#include +#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 0000000000000000000000000000000000000000..f3bd6de1c3ad5d12a552811d8cc151865bd8e564 --- /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 0000000000000000000000000000000000000000..949c7e671a9d12c02b7ce03e6dcdc966e537ba79 --- /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 0000000000000000000000000000000000000000..fc6611e41ff18183f9edc746be45f429ad7f6bef --- /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 0000000000000000000000000000000000000000..3be22bcbcbd7e93dad34f86d57dc3354f78dd62c --- /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 0000000000000000000000000000000000000000..724199528077aad7f2a8f61c2d8fcb406fca1896 --- /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 0000000000000000000000000000000000000000..052b93b45d5dc99ba4fda4c0757cb3150f8ae142 --- /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