diff --git a/example/pmu_perfdata.cpp b/example/pmu_perfdata.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53e026b20b6b4b29cd59bef70d52c57ef0c3c94a --- /dev/null +++ b/example/pmu_perfdata.cpp @@ -0,0 +1,281 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2025. 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.Gan + * Create: 2025-07-08 + * Description: Generate perf.data for sampling. + ******************************************************************************/ +// g++ -g pmu_perfdata.cpp -I /path/to/install/include/ -L /path/to/install/lib/ -lkperf -lsym -O3 -o pmu_perfdata +#include "pmu.h" +#include "symbol.h" +#include "pcerrc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +int pd = 0; +atomic toRead(false); +bool running = true; +PmuFile file = NULL; +int lastErr = SUCCESS; + +struct Param { + vector events; + bool useBrbe = false; + vector command; + string dataPath = "./libkperf.data"; + vector pidList; + unsigned duration = UINT32_MAX; + unsigned freq = 4000; + unsigned interval = 1000; // ms +}; + +int64_t GetTime() +{ + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); +} + +void Split(const string &eventStr, vector &events) +{ + stringstream ss(eventStr); + string item; + + while (getline(ss, item, ',')) { + item.erase(remove_if(item.begin(), item.end(), ::isspace), item.end()); + if (!item.empty()) { + events.push_back(item); + } + } +} + +static void PrintHelp() +{ + std::cerr << "Samping and generate perf.data: pmu_perfdata -e , -d -o -- \n"; + std::cerr << "Sampling an existed pid: pmu_perfdata -e , -p -o \n"; + std::cerr << "Use brbe: pmu_perfdata -e , -b -p -o \n"; +} + +static Param ParseArgs(int argc, char** argv) +{ + Param param; + bool inCmd = false; + for (int i = 1; i < argc; ++i) { + if (inCmd) { + param.command.push_back(argv[i]); + } else if (strcmp(argv[i], "-e") == 0 && i+1 < argc) { + Split(argv[i+1], param.events); + ++i; + } else if (strcmp(argv[i], "-b") == 0) { + param.useBrbe = true; + } else if (strcmp(argv[i], "--") == 0) { + inCmd = true; + } else if (strcmp(argv[i], "-o") == 0 && i+1 < argc) { + param.dataPath = argv[i+1]; + ++i; + } else if (strcmp(argv[i], "-d") == 0 && i+1 < argc) { + param.duration = atoi(argv[i+1]); + ++i; + } else if (strcmp(argv[i], "-p") == 0 && i+1 < argc) { + param.pidList.push_back(atoi(argv[i+1])); + ++i; + } else if (strcmp(argv[i], "-F") == 0 && i+1 < argc) { + param.freq = atoi(argv[i+1]); + ++i; + } else if (strcmp(argv[i], "-I") == 0 && i+1 < argc) { + param.interval = atoi(argv[i+1]); + ++i; + } else { + PrintHelp(); + exit(0); + } + } + + if (param.command.empty() && param.pidList.empty()) { + PrintHelp(); + exit(0); + } + + if (param.events.empty()) { + param.events.push_back("cycles"); + } + + return param; +} + +bool AllPidExit(const Param ¶m) +{ + bool allExit = true; + for (auto &pid : param.pidList) { + if (kill(pid, 0) == 0) { + allExit = false; + break; + } + } + return allExit; +} + +void FreeEvtList(char **evtlist, size_t size) +{ + for (int i = 0;i < size; ++i) { + free(evtlist[i]); + } +} + +void AsyncReadAndWrite() +{ + while(running) { + if (!toRead.load(memory_order_acquire)) { + usleep(1000); + continue; + } + + PmuData *data = nullptr; + int len = PmuRead(pd, &data); + if (len < 0) { + lastErr = Perrorno(); + continue; + } + if (PmuWriteData(file, data, len) != SUCCESS) { + lastErr = Perrorno(); + continue; + } + toRead.store(false, memory_order_release); + } +} + +int Collect(const Param ¶m) +{ + pthread_t t; + pthread_create(&t, NULL, (void*(*)(void*))AsyncReadAndWrite, NULL); + + PmuAttr attr = {0}; + char *evtlist[param.events.size()]; + for (int i = 0;i < param.events.size(); ++i) { + evtlist[i] = strdup(param.events[i].c_str()); + } + int pidlist[param.pidList.size()]; + for (int i = 0;i < param.pidList.size(); ++i) { + pidlist[i] = param.pidList[i]; + } + attr.evtList = evtlist; + attr.numEvt = param.events.size(); + attr.pidList = pidlist; + attr.numPid = param.pidList.size(); + attr.excludeKernel = 1; + attr.useFreq = 1; + attr.freq = param.freq; + attr.symbolMode = RESOLVE_ELF; + + if (param.useBrbe) { + attr.branchSampleFilter = KPERF_SAMPLE_BRANCH_ANY | KPERF_SAMPLE_BRANCH_USER; + } + + pd = PmuOpen(SAMPLING, &attr); + if (pd < 0) { + FreeEvtList(evtlist, param.events.size()); + return Perrorno(); + } + file = PmuBeginWrite(param.dataPath.c_str(), &attr); + if (file == NULL) { + FreeEvtList(evtlist, param.events.size()); + return Perrorno(); + } + PmuEnable(pd); + for (int i = 0; i < param.duration; ++i) { + auto start = GetTime(); + usleep(1000 * param.interval); + if (lastErr != SUCCESS) { + cerr << Perror() << "\n"; + break; + } + while(toRead.load(memory_order_acquire)); + toRead.store(true, memory_order_release); + + if (AllPidExit(param)) { + break; + } + auto end = GetTime(); + cout << " iteration time: " << end - start << "\n"; + } + PmuDisable(pd); + while(toRead.load(memory_order_acquire)); + PmuEndWrite(file); + PmuClose(pd); + running = false; + pthread_join(t, NULL); + + FreeEvtList(evtlist, param.events.size()); + return 0; +} + +bool ExecuteCommand(Param ¶m) +{ + auto &command = param.command; + pid_t pid = fork(); + if (pid == -1) { + perror("fork failed"); + return false; + } else if (pid == 0) { // Child process + char **argv = new char *[command.size() + 1]; + for (size_t i = 0; i < command.size(); ++i) { + argv[i] = strdup(command[i].c_str()); + } + argv[command.size()] = NULL; + + execve(argv[0], argv, NULL); + + perror("execve failed"); + for (size_t i = 0; i < command.size(); ++i) { + free(argv[i]); + } + delete[] argv; + exit(EXIT_FAILURE); + } else { + param.pidList.push_back(pid); + } + + return true; +} + +int main(int argc, char** argv) +{ + try { + auto start = GetTime(); + auto param = ParseArgs(argc, argv); + if (!param.command.empty() && !ExecuteCommand(param)) { + return 0; + } + auto err = Collect(param); + if (err != 0) { + cout << "Failed to collect: " << Perror() << "\n"; + } + if (!param.command.empty() && !param.pidList.empty()) { + kill(param.pidList[0], SIGTERM); + } + auto end = GetTime(); + cout << " elapsed time: " << end - start << "\n"; + + } catch (exception &ex) { + cout << "Failed: " << ex.what() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/include/pcerrc.h b/include/pcerrc.h index d8a11097e605e1fc9c2e8311fa9e1949a4e8d5c6..3b03821c89069f2a1ebfb63619184c6072588262 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -20,6 +20,7 @@ extern "C" { // default code #define SUCCESS 0 #define COMMON_ERR_NOMEM 1 // not enough memory +#define COMMON_ERR_WRITE 2 // libsym 100-1000 #define LIBSYM_ERR_BASE 100 @@ -40,6 +41,8 @@ extern "C" { #define LIBSYM_ERR_START_SMALLER_END 114 #define LIBSYM_ERR_STOUL_OPERATE_FAILED 115 #define LIBSYM_ERR_FILE_INVALID 116 +#define LIBSYM_ERR_READ_BUILDID 117 +#define LIBSYM_ERR_BUILDID_TOO_LONG 118 // libperf 1000-3000 #define LIBPERF_ERR_NO_AVAIL_PD 1000 #define LIBPERF_ERR_CHIP_TYPE_INVALID 1001 @@ -111,6 +114,8 @@ extern "C" { #define LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD 1067 #define LIBPERF_ERR_PMU_DATA_NO_FOUND 1068 #define LIBPERF_ERR_INVALID_CGROUP_LIST 1069 +#define LIBPERF_ERR_NOT_SUPPORT_PMU_FILE 1070 +#define LIBPERF_ERR_INVALID_PMU_FILE 1071 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index 3c2da995e4c6f0e70b0c0644bbf5b8f9a731b3a4..784e07ea17221f8b175b71019b0707cdec82aaf9 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -674,6 +674,34 @@ int PmuOpenCpuFreqSampling(unsigned period); */ void PmuCloseCpuFreqSampling(); +typedef void* PmuFile; + +/** + * @brief Begin to write PmuData list to perf.data file. + * It is a simplified perf.data only include basic fields for perf sample, + * including id, cpu, tid, pid, addr and branch stack. + * It also includes sample like mmap, mmap2, comm, fork. + * @param path path of perf.data + * @param pattr PmuAttr of collection task + * @return a handle of file to write. If error, return NULL and check Perrorno. + */ +PmuFile PmuBeginWrite(const char *path, const PmuAttr *pattr); + +/** + * @brief Write PmuData list to file. + * @param file file handle + * @param data PmuData list + * @param len length of data + * @return On success, return SUCCESS. on error, return error code. + */ +int PmuWriteData(PmuFile file, PmuData *data, int len); + +/** + * @brief End to write file. + * @param file file handle + */ +void PmuEndWrite(PmuFile file); + #pragma GCC visibility pop #ifdef __cplusplus } diff --git a/pmu/dump_perf_data.cpp b/pmu/dump_perf_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21250ca92df5e625fc19a8c21bdb4ae0c1a7d100 --- /dev/null +++ b/pmu/dump_perf_data.cpp @@ -0,0 +1,668 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2025. 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.Gan + * Create: 2025-07-09 + * Description: Write PmuData list to file like perf.data. + ******************************************************************************/ +#include "pmu.h" +#include "pcerrc.h" +#include "pcerr.h" +#include "symbol.h" +#include "pmu_event.h" +#include "pmu_list.h" +#include "process_map.h" +#include "pfm.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace KUNPENG_PMU; +using namespace pcerr; + +#define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1) +#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +constexpr static size_t BUILD_ID_LEN = 20; + +// These structs mostly come from linux tools/perf/util/header.h +struct PerfFileSection { + uint64_t offset; /* offset from start of file */ + uint64_t size; /* size of the section */ +}; + +struct PerfFileAttr { + struct perf_event_attr attr; + struct PerfFileSection ids; +}; + +struct PerfFileHeader { + char magic[8]; /* PERFILE2 */ + uint64_t size; /* size of the header */ + uint64_t attrSize; /* size of an attribute in attrs */ + struct PerfFileSection attrs; + struct PerfFileSection data; + struct PerfFileSection event_types; + uint64_t addsFeatures[4]; +}; + +struct PerfBuildId { + struct perf_event_header header; + pid_t pid; + union { + uint8_t buildId[24]; + struct { + uint8_t data[BUILD_ID_LEN]; + uint8_t size; + uint8_t reserved1__; + uint16_t reserved2__; + }; + }; + char filename[]; +}; + +// Simplified perf sample struct, only includes essential fields. +struct PerfSample { + perf_event_header header; + __u64 sampleid; + __u64 ip; + __u32 pid, tid; + __u64 time; + __u64 id; + __u32 cpu; + __u64 period; + __u64 bnr; + perf_branch_entry lbr[]; +}; + +class PerfDataDumper { +public: + PerfDataDumper(const char *path) :path(path){ + } + ~PerfDataDumper() = default; + + int Start(const PmuAttr *pattr) { + // Layout of perf.data: + // ------------------------ + // | PerfFileHeader | + // ------------------------ + // | event id | PerfFileAttr.ids.offset + // | event id | + // | ... | + // ------------------------ PerfFileHeader.attrs.offset + // | PerfFileAttr | + // | PerfFileAttr | + // | ... | + // ------------------------ PerfFileHeader.data.offset + // | perf_event | + // | perf_event | + // | perf_event | + // | ... | + // ------------------------ + // | PerfFileSection | // the only one feature section which is for build-id + // ------------------------ PerfFileSection.offset + // | PerfBuildId | // build-id is here + // | PerfBuildId | // one module, one build-id struct + // | ... | + // ------------------------ + + int err = SUCCESS; + err = CheckAttr(pattr); + if (err != SUCCESS) { + return err; + } + + fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd < 0) { + return LIBPERF_ERR_OPEN_INVALID_FILE; + } + // Calculate essential id header size, used for synthesized events. + idHdrSize = GetIdHeaderSize(); + + // Start to write perf.data, refer to perf_session__write_header in linux. + + // Use perf_magic2 for kernel 5.10. + long magic2 = 0x32454c4946524550ULL; + memcpy(ph.magic, &magic2, 8); + ph.size = sizeof(ph); + ph.attrSize = sizeof(PerfFileAttr); + + // Header will be written in the very end. + lseek(fd, sizeof(ph), SEEK_SET); + // Map event to an ID, used for mapping sample to event. + // Different from perf tool, we don't calculate hash, but use a simple index. + // Another difference is that perf tool assign an ID to each event for each core, + // but we assign an ID to each event for all cores. Maybe it has no impact. + PrepareEvt2Id(pattr); + // Write event id and get offset of event id in the file. + map evt2offset; + err = WriteEvtIds(evt2id, fd, evt2offset); + if (err != SUCCESS) { + return err; + } + + // We are going to write PerfFileAttr which contains PmuAttr. + PerfFileSection attrs = {0}; + attrs.size = sizeof(PerfFileAttr) * pattr->numEvt; + attrs.offset = lseek(fd, 0, SEEK_CUR); + // Let header know where PerfFileAttr is. + ph.attrs = attrs; + err = WriteFileAttrs(fd, pattr, evt2offset); + if (err != SUCCESS) { + return err; + } + + ph.data.offset = lseek(fd, 0, SEEK_CUR); + // Going to write synthesized events. + // When attaching a process, some perf_events are missing, including mmap, comm... + // These events appeare at the beginning of a process. + // Then we should synthesize events for better knowing processes. + err = SynthesizeEvents(pattr); + return err; + } + + int Dump(PmuData *data, const int len) + { + int err = SUCCESS; + // Write events like mmap, mmap2, comm, fork... + err = WriteInfoSamples(fd, data, ph.data.size); + if (err != SUCCESS) { + return err; + } + // Write PmuData list. + for (int i = 0; i < len; ++i) { + auto &d = data[i]; + err = WritePmuData(d); + if (err != SUCCESS) { + return err; + } + } + + return SUCCESS; + } + + int End() { + // Going to write build-id. + // Refer to perf_header__adds_write in util/header.c + int err = SUCCESS; + PerfFileSection featSec = {0}; + // Refer to Layout of perf.data to know why. + auto secStart = lseek(fd, 0, SEEK_CUR); + lseek(fd, secStart + sizeof(featSec), SEEK_SET); + featSec.offset = lseek(fd, 0, SEEK_CUR); + + for (auto &modName : modules) { + err = WriteBuildId(modName); + if (err != SUCCESS) { + return err; + } + } + featSec.size = lseek(fd, 0, SEEK_CUR) - featSec.offset; + lseek(fd, secStart, SEEK_SET); + write(fd, &featSec, sizeof(featSec)); + + const static size_t HEADER_BUILD_ID = 2; + // Refer to tools/include/asm-generic/bitops/atomic.h + // Only build-id is set in features now for pgo. + ph.addsFeatures[HEADER_BUILD_ID / __BITS_PER_LONG] |= 1UL << (HEADER_BUILD_ID % __BITS_PER_LONG); + + lseek(fd, 0, SEEK_SET); + err = write(fd, &ph, sizeof(ph)); + if (err < 0) { + return COMMON_ERR_WRITE; + } + close(fd); + return SUCCESS; + } + +private: + int CheckAttr(const PmuAttr *pattr) + { + if (pattr->numEvt == 0) { + New(LIBPERF_ERR_NOT_SUPPORT_PMU_FILE, "No events in PmuAttr, maybe it is not sampling PmuAttr"); + return LIBPERF_ERR_NOT_SUPPORT_PMU_FILE; + } + if (pattr->period == 0) { + New(LIBPERF_ERR_NOT_SUPPORT_PMU_FILE, "Period ofr freq is zero, maybe it is not sampling PmuAttr"); + return LIBPERF_ERR_NOT_SUPPORT_PMU_FILE; + } + return SUCCESS; + } + + int WritePmuData(const PmuData &d) + { + int err = SUCCESS; + size_t branchNr = 0; + if (d.ext && d.ext->nr) { + branchNr = d.ext->nr; + } + + PerfSample sample = {0}; + sample.header.type = PERF_RECORD_SAMPLE; + sample.header.misc = PERF_RECORD_MISC_USER; + sample.header.size = sizeof(sample) + branchNr * sizeof(perf_branch_entry); + sample.sampleid = evt2id[d.evt]; + sample.ip = d.stack->symbol->addr; + sample.tid = d.tid; + sample.pid = d.pid; + sample.time = d.ts; + sample.id = sample.sampleid; + sample.cpu = d.cpu; + sample.period = d.period; + sample.bnr = branchNr; + + modules.insert(d.stack->symbol->module); + + // Write perf sample except brbe data. + err = write(fd, &sample, sizeof(sample)); + if (err < 0) { + return COMMON_ERR_WRITE; + } + // Write brbe data. + for (size_t i = 0;i < branchNr; ++i) { + perf_branch_entry bentry = {0}; + bentry.from = d.ext->branchRecords[i].fromAddr; + bentry.to = d.ext->branchRecords[i].toAddr; + bentry.cycles = d.ext->branchRecords[i].cycles; + bentry.mispred = d.ext->branchRecords[i].misPred; + bentry.predicted = d.ext->branchRecords[i].predicted; + err = write(fd, &bentry, sizeof(bentry)); + if (err < 0) { + return COMMON_ERR_WRITE; + } + } + ph.data.size += sample.header.size; + return SUCCESS; + } + + int WriteBuildId(const string &modName) + { + int err = SUCCESS; + char *buildId = nullptr; + int len = SymGetBuildId(modName.c_str(), &buildId); + if (len < 0) { + SetWarn(Perrorno()); + return SUCCESS; + } + if (len > BUILD_ID_LEN) { + // linux says the length of build-id is 20. + SetWarn(LIBSYM_ERR_BUILDID_TOO_LONG); + delete buildId; + return SUCCESS; + } + + // Refer to function in linux tools/perf/util/build-id.c + PerfBuildId bid = {0}; + auto alignedLen = PERF_ALIGN(modName.length(), NAME_ALIGN); + memcpy(bid.data, buildId, len); + bid.size = len; + bid.pid = -1; + bid.header.misc = PERF_RECORD_MISC_BUILD_ID_SIZE | PERF_RECORD_MISC_USER; + bid.header.size = sizeof(bid) + alignedLen; + // Write fields in PerfBuildId except filename. + err = write(fd, &bid, sizeof(bid)); + if (err < 0) { + delete buildId; + return COMMON_ERR_WRITE; + } + + // Write filename. + err = write(fd, modName.c_str(), modName.length() + 1); + if (err < 0) { + delete buildId; + return COMMON_ERR_WRITE; + } + // Write padding buffer for alignment. + if (alignedLen > modName.length() + 1) { + static const char zeroBuf[NAME_ALIGN] = {0}; + err = write(fd, zeroBuf, alignedLen - (modName.length() + 1)); + if (err < 0) { + delete buildId; + return COMMON_ERR_WRITE; + } + } + + return SUCCESS; + } + + int SynthesizeEvents(const PmuAttr *pattr) + { + int err = SUCCESS; + for (int i = 0;i < pattr->numPid;++i) { + int numChild = 0; + auto childTid = GetChildTid(pattr->pidList[i], &numChild); + for (int j = 0;j < numChild; ++j) { + err = SynthesizeCommEvents(fd, childTid[j], ph.data.size); + if (err != SUCCESS) { + return err; + } + } + err = SynthesizeMmapEvents(fd, pattr->pidList[i], ph.data.size); + if (err != SUCCESS) { + return err; + } + } + return SUCCESS; + } + + int SynthesizeMmapEvents(const int fd, const int pid, uint64_t &dataSize) + { + // Read /proc//maps to get modules info. + // Refer to perf_event__synthesize_mmap_events in linux. + + static const char annon[] = "//anon"; + + string mapFile = "/proc/" + to_string(pid) + "/maps"; + ifstream mapIf(mapFile.c_str()); + constexpr __u64 MAX_LINUX_SYMBOL_LEN = 8192; + constexpr __u64 MAX_LINUX_MODULE_LEN = 1024; + char line[MAX_LINUX_SYMBOL_LEN]; + while (mapIf.getline(line, MAX_LINUX_SYMBOL_LEN)) { + PerfRecordMmap2 *event = (PerfRecordMmap2*)malloc(sizeof(PerfRecordMmap2) + idHdrSize); + if (event == NULL) { + return COMMON_ERR_NOMEM; + } + event->header.type = PERF_RECORD_MMAP2; + event->filename[0] = '\0'; + + __u64 end = 0; + string mode; + string modName; + // Read some lines like that: + // 00001234-00001264 r-xp 00000000 fd:01 41234 /lib/libc.so + auto err = ReadMaps(line, event->addr, end, mode, + event->pgoff, event->maj, event->min, event->ino, modName); + if (err != SUCCESS) { + free(event); + return err; + } + event->len = end - event->addr; + + // Parse r-xp to event->prot and event->flags. + err = ParseMapMode(mode, event->prot, event->flags); + if (err != SUCCESS) { + free(event); + return err; + } + event->ino_generation = 0; + + // TODO: only support host yet. + event->header.misc = PERF_RECORD_MISC_USER; + if ((event->prot & PROT_EXEC) == 0) { + event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; + } + + if (modName.empty()) { + // anonymous memory segment + strcpy(event->filename, annon); + } else { + auto copySize = modName.size() > PATH_MAX - 1 ? PATH_MAX - 1 : modName.size(); + memcpy(event->filename, modName.c_str(), copySize); + event->filename[copySize] = '\0'; + } + + size_t size = strlen(event->filename) + 1; + auto alignSize = PERF_ALIGN(size, sizeof(__u64)); + // PerfRecordMmap2 + (real filename size) + (id header size) (maybe we don't need idHdrSize?) + event->header.size = sizeof(*event) - (sizeof(event->filename) - alignSize) + idHdrSize; + memset(event->filename + size, 0, idHdrSize + (alignSize - size)); + // pid is actually tid in user space and we need to get his pid. + event->pid = GetTgid(pid); + event->tid = pid; + + err = write(fd, event, event->header.size); + if (err < 0) { + free(event); + return COMMON_ERR_WRITE; + } + dataSize += event->header.size; + free(event); + } + + return SUCCESS; + } + + int SynthesizeCommEvents(const int fd, const int pid, uint64_t &dataSize) + { + PerfRecordComm *event = (PerfRecordComm *)malloc(sizeof(PerfRecordComm) + idHdrSize); + if (event == NULL) { + return COMMON_ERR_NOMEM; + } + auto comm = GetComm(pid); + auto size = strlen(comm) > sizeof(event->comm) - 1 ? sizeof(event->comm) - 1 : strlen(comm); + memcpy(event->comm, comm, size); + event->comm[size] = '\0'; + event->pid = GetTgid(pid); + event->header.type = PERF_RECORD_COMM; + size = PERF_ALIGN(size + 1, sizeof(__u64)); + memset(event->comm + size, 0, idHdrSize); + // PerfRecordComm + (real comm size) + (id header size) (maybe we don't need idHdrSize?) + event->header.size = sizeof(*event) - (sizeof(event->comm) - size) + idHdrSize; + event->tid = pid; + + if (write(fd, event, event->header.size) <0 ) { + free(event); + return COMMON_ERR_WRITE; + } + dataSize += event->header.size; + free(event); + return SUCCESS; + } + + __u64 GetSampleType() + { + return PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID | + PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_BRANCH_STACK; + } + + PerfFileAttr GetFileAttr(const char *evt, const map &evt2offset) + { + // Now we don't have real perf_event_attr of collection task, + // then we synthesize a similar one, only for sampling. + auto pfm = PfmGetPmuEvent(evt, SAMPLING); + perf_event_attr attr = {0}; + attr.type = pfm->type; + attr.config = pfm->config; + attr.sample_period = pfm->period; + attr.freq = pfm->useFreq; + attr.exclude_kernel = pfm->excludeKernel; + attr.exclude_user = pfm->excludeUser; + // Use a constant sample type, for now, we only support fixed field data, including brbe. + attr.sample_type = GetSampleType(); + // use attr in 5.10 + attr.size = sizeof(perf_event_attr); + attr.sample_id_all = 1; + PerfFileAttr fattr = {0}; + fattr.attr = attr; + // This is the offset of event id. + fattr.ids.offset = evt2offset.at(evt); + fattr.ids.size = sizeof(long); + + return fattr; + } + + void PrepareEvt2Id(const PmuAttr *pattr) + { + long id = 0; + for (int i = 0; i < pattr->numEvt; ++i) { + evt2id[pattr->evtList[i]] = id++; + } + } + + int WriteEvtIds(const map &evt2id, const int fd, map &evt2offset) + { + for (auto ei : evt2id) { + evt2offset[ei.first] = lseek(fd, 0, SEEK_CUR); + if (write(fd, &ei.second, sizeof(ei.second)) < 0) { + return COMMON_ERR_WRITE; + } + } + return SUCCESS; + } + + int WriteFileAttrs(const int fd, const PmuAttr *pattr, const map &evt2offset) + { + for (int i = 0;i < pattr->numEvt; ++i) { + auto fattr = GetFileAttr(pattr->evtList[i], evt2offset); + if (write(fd, &fattr, sizeof(fattr)) < 0) { + return COMMON_ERR_WRITE; + } + } + + return SUCCESS; + } + + int WriteInfoSamples(const int fd, PmuData *data, uint64_t &dataSize) + { + const auto &metaData = PmuList::GetInstance()->GetMetaData(data); + for (auto &sample : metaData) { + if (write(fd, &sample, sample.header.size) < 0) { + return COMMON_ERR_WRITE; + } + dataSize += sample.header.size; + } + return SUCCESS; + } + + int ReadMaps(const char *line, __u64 &start, __u64 &end, string &mode, + __u64 &offset, __u32 &maj, __u32 &min, __u64 &inode, string &modName) + { + constexpr __u64 MAX_LINUX_MODULE_LEN = 1024; + constexpr int MODE_LEN = 5; + char modeBf[MODE_LEN] = ""; + char modNameChar[MAX_LINUX_MODULE_LEN] = ""; + if (sscanf(line, "%lx-%lx %s %lx %x:%x %ld %s", + &start, &end, modeBf, &offset, &maj, &min, + &inode, modNameChar) == EOF) { + return -1; + } + + mode = modeBf; + modName = modNameChar; + return SUCCESS; + } + + int ParseMapMode(const string &mode, __u32 &prot, __u32 &flags) + { + if (mode.size() != 4) { + return -1; + } + + if (mode[0] == 'r') { + prot |= PROT_READ; + } + if (mode[1] == 'w') { + prot |= PROT_WRITE; + } + if (mode[2] == 'x') { + prot |= PROT_EXEC; + } + if (mode[3] == 's') { + flags |= MAP_SHARED; + } else if (mode[3] == 'p') { + flags |= MAP_PRIVATE; + } + + return SUCCESS; + } + + unsigned GetIdHeaderSize() + { + // Refer to perf_evlist__id_hdr_size in linux. + unsigned size = 0; + auto sampleType = GetSampleType(); + if (sampleType & PERF_SAMPLE_TID) { + size += sizeof(__u32) * 2; + } + if (sampleType & PERF_SAMPLE_TIME) { + size += sizeof(__u64); + } + if (sampleType & PERF_SAMPLE_ID) { + size += sizeof(__u64); + } + if (sampleType & PERF_SAMPLE_CPU) { + size += sizeof(__u32) * 2; + } + if (sampleType & PERF_SAMPLE_IDENTIFIER) { + size += sizeof(__u64); + } + + return size; + } + + unsigned idHdrSize = 0; + const char *path = nullptr; + PerfFileHeader ph = {0}; + int fd = 0; + map evt2id; + set modules; + + const uint16_t PERF_RECORD_MISC_BUILD_ID_SIZE = (1 << 15); + const static size_t NAME_ALIGN = 64; +}; + +map> dumpers; + +PmuFile PmuBeginWrite(const char *path, const PmuAttr *pattr) +{ + try { + unique_ptr dumper(new PerfDataDumper(path)); + int err = dumper->Start(pattr); + if (err != SUCCESS) { + New(err); + return NULL; + } + + PmuFile fileHandle = new char; + dumpers[fileHandle] = move(dumper); + New(SUCCESS); + return fileHandle; + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + return NULL; + } +} + +int PmuWriteData(PmuFile file, PmuData *data, int len) +{ + try { + auto findDumper = dumpers.find(file); + if (findDumper != dumpers.end()) { + int err = findDumper->second->Dump(data, len); + if (err != SUCCESS) { + New(err); + } + return err; + } + New(LIBPERF_ERR_INVALID_PMU_FILE); + return LIBPERF_ERR_INVALID_PMU_FILE; + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + return UNKNOWN_ERROR; + } +} + +void PmuEndWrite(PmuFile file) +{ + try { + auto findDumper = dumpers.find(file); + if (findDumper != dumpers.end()) { + findDumper->second->End(); + delete (char*)(file); + } + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + } +} \ No newline at end of file diff --git a/pmu/evt.h b/pmu/evt.h index 9edcfd02855d861b16c0837fb85774614d9026db..8a87cd4e19b6d06006ba5e482a008b818115b3cd 100644 --- a/pmu/evt.h +++ b/pmu/evt.h @@ -43,8 +43,7 @@ public: virtual int Init(const bool groupEnable, const int groupFd, const int resetOutputFd) = 0; - virtual int Read(std::vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &swtichData) = 0; + virtual int Read(EventData &eventData) = 0; virtual int MapPerfAttr(const bool groupEnable, const int groupFd) = 0; diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index 46ac3a6ab2352c72f8a43fc629f40b68a193eaa8..63a8be126d0d124ff1c76ed658938589e4997ce4 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -191,8 +191,7 @@ void KUNPENG_PMU::EvtList::FillFields( } } -int KUNPENG_PMU::EvtList::Read(vector& data, std::vector& sampleIps, - std::vector& extPool, std::vector& switchData) +int KUNPENG_PMU::EvtList::Read(EventData &eventData) { std::unique_lock lg(mutex); @@ -210,17 +209,17 @@ int KUNPENG_PMU::EvtList::Read(vector& data, std::vector for (unsigned int row = 0; row < numCpu; row++) { auto cpuTopo = this->cpuList[row].get(); for (unsigned int col = 0; col < numPid; col++) { - auto cnt = data.size(); - int err = this->xyCounterArray[row][col]->Read(data, sampleIps, extPool, switchData); + auto cnt = eventData.data.size(); + int err = this->xyCounterArray[row][col]->Read(eventData); if (err != SUCCESS) { return err; } - if (data.size() - cnt) { + if (eventData.data.size() - cnt) { DBG_PRINT("evt: %s pid: %d cpu: %d samples num: %d\n", pmuEvt->name.c_str(), pidList[col]->pid, - cpuTopo->coreId, data.size() - cnt); + cpuTopo->coreId, eventData.data.size() - cnt); } // Fill event name and cpu topology. - FillFields(cnt, data.size(), cpuTopo, pidList[col].get(), data); + FillFields(cnt, eventData.data.size(), cpuTopo, pidList[col].get(), eventData.data); } } diff --git a/pmu/evt_list.h b/pmu/evt_list.h index 01f2291e474d665a5a5708766e42e62d06858f1e..5aabc0337af78a48b68a62b4c570da65739dfb49 100644 --- a/pmu/evt_list.h +++ b/pmu/evt_list.h @@ -70,8 +70,7 @@ public: int Enable(); int Stop(); int Reset(); - int Read(std::vector& pmuData, std::vector& sampleIps, std::vector& extPool, - std::vector& switchData); + int Read(EventData &eventData); void SetGroupInfo(const EventGroupInfo &grpInfo); diff --git a/pmu/perf_counter.cpp b/pmu/perf_counter.cpp index de25a70815da5ab637a5f0581b1ec47ff64f812c..4f19c0eb207547949d5505f12c36b083418f67a5 100644 --- a/pmu/perf_counter.cpp +++ b/pmu/perf_counter.cpp @@ -49,8 +49,7 @@ struct GroupReadFormat { * Right now we do not implement grouping logic, thus we ignore the * PERF_FORMAT_ID section for now */ -int KUNPENG_PMU::PerfCounter::Read(vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &swtichData) +int KUNPENG_PMU::PerfCounter::Read(EventData &eventData) { if (__glibc_unlikely(this->fd < 0)) { this->accumCount.clear(); @@ -58,9 +57,9 @@ int KUNPENG_PMU::PerfCounter::Read(vector &data, std::vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &swtichData) override; + int Read(EventData &eventData) override; int MapPerfAttr(const bool groupEnable, const int groupFd) override; int Enable() override; int Disable() override; diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h index 6bffab302ed25c534e4348827d87047bd61a8ca3..557b7a8749070c44fbff3d59dec677f0907da4b0 100644 --- a/pmu/pmu_event.h +++ b/pmu/pmu_event.h @@ -123,7 +123,6 @@ struct PerfRecordMmap { __u64 len; __u64 pgoff; char filename[PATH_MAX]; - struct sampleId sampleId; }; struct PerfRecordMmap2 { @@ -137,13 +136,13 @@ struct PerfRecordMmap2 { __u64 ino; __u64 ino_generation; __u32 prot, flags; - char filename[]; + char filename[PATH_MAX]; }; struct PerfRecordComm { struct perf_event_header header; __u32 pid, tid; - char comm[]; + char comm[16]; }; struct PerfRecordSample { @@ -192,6 +191,16 @@ union PerfEvent { struct ContextSwitchEvent context_switch; }; +struct EventData { + unsigned pd; + PmuTaskType collectType; + std::vector data; + std::vector sampleIps; + std::vector extPool; + std::vector switchData; + std::vector metaData; +}; + int MapErrno(int sysErr); } // namespace KUNPENG_PMU #endif diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index 9e8feb0feb8fa9716f1f8ebef92274091d90bbc3..f7b88a080df4204b7f5aea5a002661681bd44a7a 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -405,7 +405,7 @@ namespace KUNPENG_PMU { auto eventList = GetEvtList(pd); for (auto item: eventList) { item->SetTimeStamp(ts); - auto err = item->Read(evtData.data, evtData.sampleIps, evtData.extPool, evtData.switchData); + auto err = item->Read(evtData); if (err != SUCCESS) { return err; } @@ -630,7 +630,7 @@ namespace KUNPENG_PMU { } } - PmuList::EventData& PmuList::GetDataList(const unsigned pd) + EventData& PmuList::GetDataList(const unsigned pd) { lock_guard lg(dataListMtx); return dataList[pd]; @@ -712,6 +712,15 @@ namespace KUNPENG_PMU { return SUCCESS; } + std::vector PmuList::GetMetaData(PmuData* pmuData) const + { + auto findData = userDataList.find(pmuData); + if (findData == userDataList.end()) { + return vector(); + } + return findData->second.metaData; + } + void PmuList::AggregateData(const std::vector& evData, std::vector& newEvData) { // Acccumulate stat data in previous PmuCollect for convenient use. @@ -1099,9 +1108,11 @@ namespace KUNPENG_PMU { } SymResolverInit(); - int ret = SymResolverRecordKernel(); - if (ret != SUCCESS) { - return ret; + if (taskParam->pmuEvt->excludeKernel != 1) { + int ret = SymResolverRecordKernel(); + if (ret != SUCCESS) { + return ret; + } } std::vector pidList = this->ppidList[pd]; @@ -1113,7 +1124,7 @@ namespace KUNPENG_PMU { for (const auto& pid: pidList) { int rt = SymResolverRecordModuleNoDwarf(pid); if (rt != SUCCESS) { - return ret; + return rt; } } } @@ -1122,7 +1133,7 @@ namespace KUNPENG_PMU { for (const auto& pid: pidList) { int rt = SymResolverRecordModule(pid); if (rt != SUCCESS) { - return ret; + return rt; } } } diff --git a/pmu/pmu_list.h b/pmu/pmu_list.h index 99539d297dd002069f14b4cf9f2cb8841a70406a..8f88ff524b4b62acc08a79b835e5df660183a1a0 100644 --- a/pmu/pmu_list.h +++ b/pmu/pmu_list.h @@ -78,6 +78,8 @@ public: bool IsAllPidExit(const unsigned pd); int ResolvePmuDataSymbol(struct PmuData* iPmuData); + std::vector GetMetaData(PmuData* pmuData) const; + private: using ProcPtr = std::shared_ptr; using CpuPtr = std::shared_ptr; @@ -87,15 +89,6 @@ private: PmuList& operator=(const PmuList&) = delete; ~PmuList() = default; - struct EventData { - unsigned pd; - PmuTaskType collectType; - std::vector data; - std::vector sampleIps; - std::vector extPool; - std::vector switchData; - }; - void InsertEvtList(const unsigned pd, std::shared_ptr evtList); std::vector>& GetEvtList(const unsigned pd); void EraseEvtList(const unsigned pd); diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index 46788dc65ae493129f9488f3f7595802d17120c0..1f44a443dc0c87fa4a33423075914e56072251a8 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -259,8 +259,7 @@ void KUNPENG_PMU::PerfSampler::RawSampleProcess( } } -void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vector &sampleIps, - std::vector &extPool, std::vector &switchData) +void KUNPENG_PMU::PerfSampler::ReadRingBuffer(EventData &eventData) { union KUNPENG_PMU::PerfEvent *event; while (true) { @@ -271,14 +270,15 @@ void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vectorheader.type; switch (sampleType) { case PERF_RECORD_SAMPLE: { - data.emplace_back(PmuData{0}); - auto& current = data.back(); - sampleIps.emplace_back(PerfSampleIps()); - auto& ips = sampleIps.back(); - this->RawSampleProcess(¤t, &ips, event, extPool); + eventData.data.emplace_back(PmuData{0}); + auto& current = eventData.data.back(); + eventData.sampleIps.emplace_back(PerfSampleIps()); + auto& ips = eventData.sampleIps.back(); + this->RawSampleProcess(¤t, &ips, event, eventData.extPool); break; } case PERF_RECORD_MMAP: { + eventData.metaData.push_back(*event); if (symMode == RESOLVE_ELF_DWARF || symMode == NO_SYMBOL_RESOLVE) { SymResolverUpdateModule(event->mmap.tid, event->mmap.filename, event->mmap.addr); } else if (symMode == RESOLVE_ELF) { @@ -287,6 +287,7 @@ void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vectormmap2.tid, event->mmap2.filename, event->mmap2.addr); } else if (symMode == RESOLVE_ELF) { @@ -296,16 +297,18 @@ void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vectorfork.pid, event->fork.tid); + eventData.metaData.push_back(*event); UpdatePidInfo(event->fork.tid); break; } case PERF_RECORD_COMM: { + eventData.metaData.push_back(*event); UpdateCommInfo(event); break; } case PERF_RECORD_SWITCH: { - switchData.emplace_back(PmuSwitchData{0}); - auto& switchCurData = switchData.back(); + eventData.switchData.emplace_back(PmuSwitchData{0}); + auto& switchCurData = eventData.switchData.back(); ParseSwitch(event, &switchCurData); break; } @@ -335,8 +338,7 @@ void KUNPENG_PMU::PerfSampler::FillComm(const size_t &start, const size_t &end, } } -int KUNPENG_PMU::PerfSampler::Read(vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &switchData) +int KUNPENG_PMU::PerfSampler::Read(EventData &eventData) { // This may be a lack of space. if(!this->sampleMmap || !this->sampleMmap->base) { @@ -346,10 +348,10 @@ int KUNPENG_PMU::PerfSampler::Read(vector &data, std::vectorReadRingBuffer(data, sampleIps, extPool, switchData); + auto cnt = eventData.data.size(); + this->ReadRingBuffer(eventData); if (this->pid == -1) { - FillComm(cnt, data.size(), data); + FillComm(cnt, eventData.data.size(), eventData.data); } return SUCCESS; diff --git a/pmu/sampler.h b/pmu/sampler.h index 6c49a74d6e594aec3cb18bd3087743c087e9b088..e4dc0d7e05d7d5d9293f318a3762f9e7e6866a3d 100644 --- a/pmu/sampler.h +++ b/pmu/sampler.h @@ -43,8 +43,7 @@ namespace KUNPENG_PMU { {} int Init(const bool groupEnable, const int groupFd, const int resetOutputFd) override; - int Read(std::vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &switchData) override; + int Read(EventData &eventData) override; int MapPerfAttr(const bool groupEnable, const int groupFd) override; @@ -56,8 +55,7 @@ namespace KUNPENG_PMU { int Mmap(); union PerfEvent *SampleReadEvent(); void RawSampleProcess(struct PmuData *sampleHead, PerfSampleIps *ips, union KUNPENG_PMU::PerfEvent *event, std::vector &extPool); - void ReadRingBuffer(std::vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &switchData); + void ReadRingBuffer(EventData &eventData); void FillComm(const size_t &start, const size_t &end, std::vector &data); void UpdatePidInfo(const int &tid); void UpdateCommInfo(KUNPENG_PMU::PerfEvent *event); diff --git a/pmu/spe_sampler.cpp b/pmu/spe_sampler.cpp index 388272a97cacb3a617c182af53efd7c23056544b..b316baf96f24531bb462a658d6e58688146c8f1e 100644 --- a/pmu/spe_sampler.cpp +++ b/pmu/spe_sampler.cpp @@ -65,8 +65,7 @@ namespace KUNPENG_PMU { return SUCCESS; } - int PerfSpe::Read(vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &switchData) + int PerfSpe::Read(EventData &eventData) { auto findSpe = speSet.find(this->cpu); if (findSpe == speSet.end()) { @@ -82,7 +81,7 @@ namespace KUNPENG_PMU { } // Fill pmu data from SPE collector. - auto cnt = data.size(); + auto cnt = eventData.data.size(); if (pid == -1) { // Loop over all tids in records and resolve module symbol for all pids. UpdatePidList(findSpe->second); @@ -91,14 +90,14 @@ namespace KUNPENG_PMU { continue; } // Insert each spe record for each tid. - InsertSpeRecords(records.first, records.second, data, sampleIps, extPool); + InsertSpeRecords(records.first, records.second, eventData.data, eventData.sampleIps, eventData.extPool); } } else { // Loop over all tids. for (auto &proc : procMap) { // Get all spe records for tid. const auto &records = findSpe->second.GetPidRecords(proc.first); - InsertSpeRecords(proc.second->tid, records, data, sampleIps, extPool); + InsertSpeRecords(proc.second->tid, records, eventData.data, eventData.sampleIps, eventData.extPool); } } diff --git a/pmu/spe_sampler.h b/pmu/spe_sampler.h index 617a04195bd8311b8549ee2ea57234993fc2cacd..3222983dade6b6529d133571055af4df4d45db6f 100644 --- a/pmu/spe_sampler.h +++ b/pmu/spe_sampler.h @@ -37,8 +37,7 @@ namespace KUNPENG_PMU { {} int Init(const bool groupEnable, const int groupFd, const int resetOutputFd) override; - int Read(std::vector &data, std::vector &sampleIps, - std::vector &extPool, std::vector &switchData) override; + int Read(EventData &eventData) override; int MapPerfAttr(const bool groupEnable, const int groupFd) override; bool Mmap(); diff --git a/symbol/symbol.cpp b/symbol/symbol.cpp index ddc74366c4d47c6b3f3e8b85404e4c2e8853cbc7..938ee6ff6ec987c956b6ed90e3ca8c7409fbec13 100644 --- a/symbol/symbol.cpp +++ b/symbol/symbol.cpp @@ -159,6 +159,16 @@ struct Symbol* SymResolverMapCodeAddr(const char* moduleName, unsigned long star } } +int SymGetBuildId(const char *moduleName, char **buildId) +{ + try { + return SymbolResolve::GetInstance()->GetBuildId(moduleName, buildId); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return -1; + } +} + void FreeModuleData(int pid) { return SymbolResolve::GetInstance()->FreeModule(pid); diff --git a/symbol/symbol.h b/symbol/symbol.h index d88151ed27e7b771f60672c154710a2bf44d68bf..bab9951f5806256b066e8c859beb21f1db45185d 100644 --- a/symbol/symbol.h +++ b/symbol/symbol.h @@ -127,6 +127,8 @@ void FreeModuleData(int pid); */ void FreeAsmStack(struct StackAsm* stackAsm); +int SymGetBuildId(const char *moduleName, char **buildId); + struct ProcTopology { int pid; int tid; diff --git a/symbol/symbol_resolve.cpp b/symbol/symbol_resolve.cpp index b9f264dd7fc1f361e61cc1bb94da1603726fbbbe..bcbaa833e1983540f3c79916cdac25b59375ca8d 100644 --- a/symbol/symbol_resolve.cpp +++ b/symbol/symbol_resolve.cpp @@ -1077,6 +1077,60 @@ struct Symbol* SymbolResolve::MapCodeAddr(const char* moduleName, unsigned long return symbol; } +int SymbolResolve::GetBuildId(const char *moduleName, char **buildId) +{ + // Refer to elf_read_build_id in linux. + + struct ElfNoteHeader { + int nameSize; + int descSize; + int type; + }; + + static int BUILD_ID_TYPE = 3; + int fd = open(moduleName, O_RDONLY); + if (fd < 0) { + return LIBSYM_ERR_OPEN_FILE_FAILED; + } + + try { + std::shared_ptr efLoader = elf::create_mmap_loader(fd); + elf::elf ef(efLoader); + MyElf myElf(ef); + const elf::section *sec = &ef.get_section(".note.gnu.build-id"); + if (!sec->valid()) { + sec = &ef.get_section(".notes"); + } + if (!sec->valid()) { + sec = &ef.get_section(".note"); + } + if (sec && sec->valid()) { + // Note section is something like that: + // | name size | desc size(0x14) | type(3) | name(GNU) | 912312fabef135672390... | + // | 4 bytes | 4 bytes | 4 bytes | name size | 20 bytes | + auto header = (ElfNoteHeader*)sec->data(); + auto ptr = (char*)header; + ptr += sizeof(*header); + char *name = ptr; + ptr += header->nameSize; + if (header->type == BUILD_ID_TYPE && + header->nameSize == sizeof("GNU") && + memcmp(name, "GNU", header->nameSize) == 0) { + *buildId = new char[header->descSize]; + memset(*buildId, 0, header->descSize); + memcpy(*buildId, ptr, header->descSize); + return header->descSize; + } + } + } catch (std::exception& error) { + pcerr::New(LIBSYM_ERR_ELFIN_FOMAT_FAILED, "libsym record elf format error: " + std::string{error.what()}); + return LIBSYM_ERR_ELFIN_FOMAT_FAILED; + } + + pcerr::New(LIBSYM_ERR_READ_BUILDID); + return LIBSYM_ERR_READ_BUILDID; +} + struct StackAsm* ReadAsmCodeFromPipe(FILE* pipe) { struct StackAsm* head = nullptr; diff --git a/symbol/symbol_resolve.h b/symbol/symbol_resolve.h index 208b09fa79d999f6313970f47932426323d8c207..ae0c0d6435977ec91fef5decb048a1bb023bb8fb 100644 --- a/symbol/symbol_resolve.h +++ b/symbol/symbol_resolve.h @@ -172,6 +172,7 @@ namespace KUNPENG_SYM { struct Symbol* MapAddr(int pid, unsigned long addr); struct StackAsm* MapAsmCode(const char* moduleName, unsigned long startAddr, unsigned long endAddr); struct Symbol* MapCodeAddr(const char* moduleName, unsigned long startAddr); + int GetBuildId(const char *moduleName, char **buildId); private: void SearchElfInfo(MyElf &myElf, unsigned long addr, struct Symbol *symbol, unsigned long *offset); diff --git a/util/process_map.cpp b/util/process_map.cpp index 0745b51112fa00970095904f25d35fc5c8bf95fa..d419884db416d676e206115f0e5b48474e21d4fa 100644 --- a/util/process_map.cpp +++ b/util/process_map.cpp @@ -46,7 +46,7 @@ void FreeProcTopo(struct ProcTopology *procTopo) delete procTopo; } -static int GetTgid(pid_t pid) +int GetTgid(pid_t pid) { if (pid == -1) { // for system sampling. @@ -85,7 +85,7 @@ static int GetTgid(pid_t pid) return -1; } -static char *GetComm(pid_t pid) +char *GetComm(pid_t pid) { std::string commName; if (pid == -1) { diff --git a/util/process_map.h b/util/process_map.h index e401fbe03fd7b81d855e74c13beace49975b071c..f771d4c7dde8bc9dc3bf4e685145fac2cf39f8fd 100644 --- a/util/process_map.h +++ b/util/process_map.h @@ -23,6 +23,9 @@ extern "C" { struct ProcTopology* GetProcTopology(pid_t pid); void FreeProcTopo(struct ProcTopology *procTopo); int* GetChildTid(int pid, int* numChild); +int GetTgid(pid_t pid); +char *GetComm(pid_t pid); + #ifdef __cplusplus } #endif