鲲鹏社区首页
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

高性能程序追踪库使用示例

简要示范全流程使用高性能程序追踪库,整体流程如图1所示。

图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文件模板,用于配置日志

生成代码使用示例

  1. 工具包解压后,需配置环境变量。
    cd /home/tracelog/     #在该路径下解压工具包
    export LD_LIBRARY_PATH=/home/tracelog/logger_device/lib:$LD_LIBRARY_PATH
  2. “/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文件和头文件会同时生成到同一目录下。

  3. 查看生成的头文件。
    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
  4. 重新指定追踪库使用缓存大小,可以在user.h头文件中修改宏定义MEMORY_KILOBYTES的值。
    1
    #define MEMORY_KILOBYTES 6120
    
  5. 重新指定trace.log文件和ProcMaps文件的路径,可以在user.h头文件中修改宏定义TRACELOG_FILENAME的值。
    1
    #define TRACELOG_FILENAME "/home/tracelog/custom.log"
    

功能库使用示例

  1. 请提前准备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():在业务应用运行结束前,结束使用功能库。
  2. 编译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:生成的二进制文件。
  3. 将loggerdevice库的所在路径加到LD_LIBRARY_PATH环境变量。
    1
    export LD_LIBRARY_PATH=/home/tracelog/logger_device/lib:$LD_LIBRARY_PATH
    
  4. 运行user_demo。
    /home/tracelog/user_demo
    运行完成后会在user.h中指定的路径“/home/tracelog/”下生成custom.log1740206079754882文件和ProcMaps文件。

    在生成的头文件中,用户可通过修改宏定义内容指定日志文件生成路径,日志文件名带有时间戳,不会覆盖系统文件。日志文件中打印的内容均为用户的业务日志,不打印高性能程序追踪库本身的日志,文件权限为600。每个日志文件大小在100MB以内,且无日志注入风险。

    • “/home/tracelog/”:生成的日志文件路径。
    • custom.log740206079754882:日志文件名称。
    • ProcMaps:内存映射信息文件,ProcMaps文件与日志文件会生成到同一目录下。

日志解析使用示例

  1. 用户在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);
    
  2. 执行日志解析命令。
    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