高性能程序追踪库使用示例
简要示范全流程使用高性能程序追踪库,整体流程如图1所示。
文件目录结构:
/home/tracelog/ ├── user.ini ├── user_demo.c ├── logger_device │ ├── lib │ ├──libkperf.so #性能采集库,提供性能采集能力 │ ├──libloggerdevice.a #功能库(静态库),提供日志序列化,反序列化能力 │ ├──libloggerdevice.so #功能库(动态库),提供日志序列化,反序列化能力 │ ├──libsym.so #符号解析库,提供符号解析能力 │ ├── include │ ├──dev_trace_log.h #提供可直接调用的高性能程序追踪库的API │ ├──dev_trace_log.metadata #提供与dev_trace_log.h相对应的metadata解析文件 │ ├──logger_device.h #对外接口头文件,提供序列化、反序列化相关接口 │ ├──user.h │ ├── bin │ ├──tracelog #tracelog命令行,提供根据.ini文件生成对应头文件及解析日志文件的功能 │ ├──example.ini #生成代码功能输入.ini文件模板,用于配置日志
生成代码使用示例
- 工具包解压后,需配置环境变量。
cd /home/tracelog/ #在该路径下解压工具包 export LD_LIBRARY_PATH=/home/tracelog/logger_device/lib:$LD_LIBRARY_PATH
- 以“/home/tracelog/user.ini”配置文件为例进行生成代码操作,在-o参数指定的路径下生成对应的user.h头文件。
1
tracelog generator -i /home/tracelog/user.ini -o /home/tracelog/logger_device/include
返回信息如下:
1
[INFO] The basic code has been generated to /home/tracelog/logger_device/include/user.h.
user.ini文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
memoryKBs=5120 tracelog="/var/log/trace.log" [normal_example] name=char* id=int charactor=char [callstack_example] key=int callstack=3 [pmu_example] key=int enablepmu=true
metadata文件和头文件会同时生成到同一目录下。
- 查看生成的头文件。
1
vim /home/tracelog/logger_device/include/user.h
生成的对应头文件user.h内容:
#ifndef USER_H #define USER_H #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdbool.h> #include "logger_device.h" #define dev_tracepoint(module,event,...) dev_tracepoint_##module##_##event(__VA_ARGS__) #define MEMORY_KILOBYTES 5120 #define TRACELOG_FILENAME "/var/log/trace.log" static inline __attribute__((always_inline)) int ConvertToCTFStr(DataStream *pDataStream, const char *str, size_t strLen) { char *buffer = (char *)pDataStream->log + pDataStream->consumeSize; memcpy(buffer, str, strLen); pDataStream->consumeSize += strLen; return 0; } static inline __attribute__((always_inline)) int ConvertToCTFChar(DataStream *pDataStream, char c) { char *buffer = (char *)pDataStream->log + pDataStream->consumeSize; *buffer = c; pDataStream->consumeSize += 1; return 0; } static inline __attribute__((always_inline)) int ConvertToCTFInt(DataStream *pDataStream, int i) { int *buffer = (int *)((char *)pDataStream->log + pDataStream->consumeSize); *buffer = i; pDataStream->consumeSize += sizeof(int); return 0; } static inline __attribute__((always_inline)) int GetStrLenLineNum(int lineNum) { int len = 1; int i = lineNum; while (i >= 10) { ++len; i = i / 10; } return len; } static int dev_tracepoint_user_normal_example(const char *name, int id, char charactor) { int ret = InitLoggerDevice(TRACELOG_FILENAME, MEMORY_KILOBYTES, 1, NULL); if (ret != 0) { return ret; } size_t strLen_1 = strlen(name) + 1; size_t bodyLength = strLen_1 + 4 + 1; if (bodyLength > LOG_LENGTH) { return ERROR_LOG_TOO_LONG_FAIL; } size_t totalLength = (bodyLength + 40) * 8; DataStream dataStream = {0xC1FC1FC1, 0, totalLength, totalLength, 0, 0, {0}, 0}; ConvertToCTFStr(&dataStream, name, strLen_1); ConvertToCTFInt(&dataStream, id); ConvertToCTFChar(&dataStream, charactor); return LoggerDeviceWriter(1, &dataStream); } static int dev_tracepoint_user_callstack_example_callstack(int key) { int ret = InitLoggerDevice(TRACELOG_FILENAME, MEMORY_KILOBYTES, 1, NULL); if (ret != 0) { return ret; } CallStackResult cr = Backtrace(3); size_t bodyLength = 4; bodyLength += sizeof(cr); if (bodyLength > LOG_LENGTH) { return ERROR_LOG_TOO_LONG_FAIL; } size_t totalLength = (bodyLength + 40) * 8; DataStream dataStream = {0xC1FC1FC1, 1, totalLength, totalLength, 0, 0, {0}, 0}; memcpy(dataStream.log + bodyLength - sizeof(cr), &cr, sizeof(cr)); ConvertToCTFInt(&dataStream, key); return LoggerDeviceWriter(1, &dataStream); } static int dev_tracepoint_user_callstack_example(int key) { int ret = InitLoggerDevice(TRACELOG_FILENAME, MEMORY_KILOBYTES, 1, NULL); if (ret != 0) { return ret; } size_t bodyLength = 4; if (bodyLength > LOG_LENGTH) { return ERROR_LOG_TOO_LONG_FAIL; } size_t totalLength = (bodyLength + 40) * 8; DataStream dataStream = {0xC1FC1FC1, 2, totalLength, totalLength, 0, 0, {0}, 0}; ConvertToCTFInt(&dataStream, key); return LoggerDeviceWriter(1, &dataStream); } static int dev_tracepoint_user_pmu_example_enablepmu(int key) { int ret = InitLoggerDevice(TRACELOG_FILENAME, MEMORY_KILOBYTES, 1, NULL); if (ret != 0) { return ret; } PmuResult pmuData = GetPmuData(); size_t bodyLength = 4; bodyLength += sizeof(pmuData); if (bodyLength > LOG_LENGTH) { return ERROR_LOG_TOO_LONG_FAIL; } size_t totalLength = (bodyLength + 40) * 8; DataStream dataStream = {0xC1FC1FC1, 3, totalLength, totalLength, 0, 0, {0}, 0}; memcpy(dataStream.log + bodyLength - sizeof(pmuData), &pmuData, sizeof(pmuData)); ConvertToCTFInt(&dataStream, key); return LoggerDeviceWriter(1, &dataStream); } static int dev_tracepoint_user_pmu_example(int key) { int ret = InitLoggerDevice(TRACELOG_FILENAME, MEMORY_KILOBYTES, 1, NULL); if (ret != 0) { return ret; } size_t bodyLength = 4; if (bodyLength > LOG_LENGTH) { return ERROR_LOG_TOO_LONG_FAIL; } size_t totalLength = (bodyLength + 40) * 8; DataStream dataStream = {0xC1FC1FC1, 4, totalLength, totalLength, 0, 0, {0}, 0}; ConvertToCTFInt(&dataStream, key); return LoggerDeviceWriter(1, &dataStream); } #endif
- 重新指定追踪库使用缓存大小,可以在user.h头文件中修改宏定义MEMORY_KILOBYTES的值。
1
#define MEMORY_KILOBYTES 6120
- 重新指定trace.log文件和ProcMaps文件的路径,可以在user.h头文件中修改宏定义TRACELOG_FILENAME的值。
1
#define TRACELOG_FILENAME "/home/tracelog/custom.log"
功能库使用示例
- 请提前准备demo文件,demo文件中已引用生成的user.h文件。查看“/home/tracelog/user_demo.c”文件。
1
vim /home/tracelog/user_demo.c
文件内容如下:
1 2 3 4 5 6 7 8
#include <stdio.h> #include "user.h" int main(){ dev_tracepoint(user, normal_example, "hello", 18, 'a'); dev_tracepoint(user, callstack_example_callstack, 4); //调用了会记录调用栈数据的接口 dev_tracepoint(user, pmu_example_enablepmu, 5); //调用了会记录PMU数据的接口 DestroyLoggerDevice(); }
- dev_tracepoint(user, normal_example, "hello", 18, 'a'):按照.ini配置文件中定义的参数类型顺序来调用dev_tracepoint接口,即日志接口。
- dev_tracepoint(user, callstack_example, 4):此处调用callstack类型的日志接口。
- dev_tracepoint(user, pmu_example, 5):此处调用PMU类型的日志接口。
- DestroyLoggerDevice():在业务应用运行结束前,结束使用功能库。
- 编译user_demo.c。
1
gcc /home/tracelog/user_demo.c -I /home/tracelog/logger_device/include/ -l loggerdevice -L /home/tracelog/logger_device/lib -o /home/tracelog/user_demo -g -O0
- -I(大写i):头文件所在路径。
- -l(小写L):功能库名称。
- -L:功能库所在路径。
- -o:生成的二进制文件。
- 将loggerdevice库的所在路径加到LD_LIBRARY_PATH环境变量。
1
export LD_LIBRARY_PATH=/home/tracelog/logger_device/lib:$LD_LIBRARY_PATH
- 运行user_demo。
/home/tracelog/user_demo
运行完成后会在user.h中指定的路径“/home/tracelog/”下生成custom.log1740206079754882文件和ProcMaps文件。在生成的头文件中,用户可通过修改宏定义内容指定日志文件生成路径,日志文件名带有时间戳,不会覆盖系统文件。日志文件中打印的内容均为用户的业务日志,不打印高性能程序追踪库本身的日志,文件权限为600。每个日志文件大小在100MB以内,且无日志注入风险。
- “/home/tracelog/”:生成的日志文件路径。
- custom.log740206079754882:日志文件名称。
- ProcMaps:内存映射信息文件,ProcMaps文件与日志文件会生成到同一目录下。
日志解析使用示例
- 用户在user_demo.c中调用dev_tracepoint接口为例,在编译并执行相应的业务应用后会在“/home/tracelog/”下生成对应custom.log1740467391374218文件和ProcMaps文件。
dev_tracepoint接口信息如下:
1 2 3
dev_tracepoint(user, normal_example, "hello", 18, 'a'); dev_tracepoint(user, callstack_example, 4); dev_tracepoint(user, pmu_example, 5);
- 执行日志解析命令。
1
tracelog parser -md /home/tracelog/logger_device/include/metadata -tl /home/tracelog/custom.log1740467391374218 -mf /home/tracelog/ProcMaps
返回信息如下:
[2025-05-15 16:06:29] str1 = "hello", i1 = 18, c1 = a [2025-05-15 16:06:29] i1 = 4, callstack = "Thread ID: 2533516 Frame #00: [0x400bd0] dev_tracepoint_user_callstack_example at /home/tracelog/logger_device/include/user.h:69 Frame #01: [0x400e18] main at /home/tracelog/user_demo.c:5 Frame #02: [0xffff8059763c] UNKNOWN at (null):0 " [2025-05-15 16:06:29] i1 = 5, Pmudata: tid = 2533320 cycles = 0
父主题: 高性能程序追踪库