新闻  |   论坛  |   博客  |   在线研讨会
Diagnostic Log and Trace——开发人员如何使用 DLT
电子禅石 | 2023-11-17 18:41:18    阅读:8586   发布文章

DLT 示例应用

要在应用程序中使用 DLT,它必须链接到 DLT 库。在系统上安装 DLT 守护程序后,将有一个名为 libdlt.so 的共享库,它为应用程序提供接口以获取与 DLT 守护程序的连接。在使用共享 dlt 库构建程序之前,必须在构建环境中设置库路径和包含路径。默认情况下,头文件“dlt.h”位于标准包含目录中名为“dlt/”的目录中。


此示例通过使用最少的代码示例概述了应用程序内部的 DLT 使用情况。

#include <dlt/dlt.h>
 
DLT_DECLARE_CONTEXT(ctx); /* declare context */
 
int main()
{
	DLT_REGISTER_APP("TAPP", "Test Application for Logging");
 
	DLT_REGISTER_CONTEXT(ctx, "TES1", "Test Context for Logging");
 
	/* … */
 
	DLT_LOG(ctx, DLT_LOG_ERROR, DLT_CSTRING("This is an error"));
 
	/* … */
 
	DLT_UNREGISTER_CONTEXT(ctx);
	DLT_UNREGISTER_APP();
	return 0;

DLT 非常易于使用。开发人员必须做的第一件事是包含 dlt 头文件。可以使用下一行中显示的宏静态声明 DLT 上下文。首先,必须在主函数内注册一个 DLT 应用程序。为此,必须指定应用程序标识符 APID 和应用程序描述。之后,可以指定一个或多个 DLT 上下文。要以详细模式记录消息,可以使用 DLT_LOG 宏。作为参数,必须指定日志上下文、日志级别和参数变量列表。 DLT 要求使用 DLT 类型宏对每个参数进行强类型化。在本例中,DLT_CSTRING 用于指定一个常量字符串。在应用程序清理时,所有 DLT 上下文以及 DLT 应用程序都必须注销。

————————————————

cmake 中如何加入 DLT

要将 DLT 加入 CMake,推荐的方法是使用作为安装一部分生成的 CMake 配置文件。

你可以这样:

find_package(automotive-dlt REQUIRED)
...
target_link_libraries(myapp PRIVATE Genivi::DLT)

这让您的项目自动获得 libdlt 所需的所有必要编译和链接标志,包括包含目录。


生成的 CMake 配置文件遵循“Modern CMake”约定,并且只导出一个 IMPORTED CMake 目标;它不设置任何变量,除了可用于将 DLT 视为可选依赖项的automotive-dlt_FOUND 变量。


生成的 CMake 配置文件(在调用 find_package(automotive-dlt) 时隐式使用)默认仅将顶级目录添加到编译器的头文件搜索路径中;这要求用户的 #include 指令以常规形式编写,例如<dlt/dlt.h>。如果您还希望能够使用旧形式 <dlt.h>(出于向后兼容性原因,pkg-config 模块始终允许使用),您可以使用 CMake 选项 -DWITH_LEGACY_INCLUDE_PATH=On 配置 DLT,以便达到目的。

————————————————

DLT使用 pkg-config

除了上面详述的 CMake 集成之外,还可以通过 pkg-config 使用 DLT。这也可以通过 CMake 的 PkgConfig 模块来完成。


PkgConfig 与“Modern CMake”的使用


在这里,您也让 PkgConfig 模块创建目标;然而,目标的名称由 PkgConfig 模块确定:

————————————————

find_package(PkgConfig)
pkg_check_modules(DLT REQUIRED IMPORTED_TARGET automotive-dlt)

根据“Modern CMake”,不需要再添加的变量,而只有要添加到链接库的 CMake 目标:

target_link_libraries(myapp PRIVATE PkgConfig::DLT)

PkgConfig 与“Legacy CMake”(<3.0)的使用

在这里,您让 PkgConfig 模块只创建变量,而不创建目标:

find_package(PkgConfig)
pkg_check_modules(DLT REQUIRED automotive-dlt)

到 INCLUDE_DIRECTORIES(或者,从 CMake 2.8.11 开始,TARGET_INCLUDE_DIRECTORIES),添加

${DLT_INCLUDE_DIRS}

TARGET_LINK_LIBRARIES:


${DLT_LINK_LIBRARIES}  (preferred, for CMake >= 3.12)

${DLT_LIBRARIES}       (otherwise)

${DLT_LIBRARIES} 的内容不包括库的路径(例如 -L/path/to/lib),因此如果库驻留在不在链接器默认搜索路径上的位置,您要么必须添加LINK_DIRECTORIES 的路径:


link_directories(${DLT_LIBRARY_DIRS})

或者,不使用 ${DLT_LIBRARIES},而是使用 ${DLT_LDFLAGS},它结合了 ${DLT_LIBRARIES} 和 ${DLT_LIBRARY_DIRS}:


target_link_libraries(myapp ${DLT_LDFLAGS})

局限性

在 Android 上,应避免在 DLT 应用程序中定义 SIGUSR1,因为 DLT 库会阻止 SIGUSR1 在退出时终止管家线程。

————————————————

测试工程源码

下面是我的工程:


dlt_test/

├── CMakeLists.txt

├── dlt_test.c

└── LICENSE

dlt_test.c


#include <dlt/dlt.h>

 

DLT_DECLARE_CONTEXT(ctx); /* declare context */

 

int main()

{

    DLT_REGISTER_APP("TAPP", "Test Application for Logging");

 

    // DLT_REGISTER_CONTEXT(ctx, "TES1", "Test Context for Logging");

    // 向守护进程注册新的上下文,初始日志级别为DLT_LOG_VERBOSE

    DLT_REGISTER_CONTEXT_LL_TS(ctx, "TES1", " First context ", DLT_LOG_VERBOSE, DLT_TRACE_STATUS_OFF);

    // dlt_register_context_ll_ts(&ctx, "TES1", " First context ", DLT_LOG_VERBOSE, DLT_TRACE_STATUS_OFF);

 

 

    /* … */

    sleep(3);

    DLT_LOG(ctx, DLT_LOG_VERBOSE , DLT_CSTRING("This is an error"));

 

    /* … */

    sleep(3);

    DLT_UNREGISTER_CONTEXT(ctx);

    DLT_UNREGISTER_APP();

    return 0;

}

CMakeLists.txt


# for dlt_test

 

cmake_minimum_required (VERSION 3.0)

# The version number.

set (rtser_VERSION_MAJOR 0)

set (rtser_VERSION_MINOR 1)

set (rtser_VERSION_PATCH 0)

######################### Project settings #####################################

project(dlt_test)

#打印make详细信息

set(CMAKE_VERBOSE_MAKEFILE on)

#设置编译级别

add_definitions (-Wall -g)

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")

#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -pthread")

#include( FindPkgConfig )

 

######## config inc&src&linklib settings and build #############################

find_package(PkgConfig)

pkg_check_modules(DLT REQUIRED IMPORTED_TARGET automotive-dlt)

include_directories(

)

 

link_directories(

)

 

file(GLOB SOURCES

    "*.c"

)

 

add_executable(dlt_test

    ${SOURCES}

)

 

 

target_link_libraries(dlt_test PRIVATE PkgConfig::DLT)

 

#安装位置

set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR})

# build a CPack driven installer package

include (InstallRequiredSystemLibraries)

set (CPACK_RESOURCE_FILE_LICENSE  

     "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")

set (CPACK_PACKAGE_VERSION_MAJOR "${rtser_VERSION_MAJOR}")

set (CPACK_PACKAGE_VERSION_MINOR "${rtser_VERSION_MINOR}")

set (CPACK_PACKAGE_VERSION_PATCH "${rtser_VERSION_PATCH}")

include (CPack)

 

######## Install targets ########

install(TARGETS dlt_test

RUNTIME DESTINATION  /usr/bin/

)

记录的一般规则

需要在关键位置打印log,例如错误处理,不要乱用log,因为打印log需要消耗资源;


避免高频输出;


合并多条消息,请始终考虑每条日志消息都会产生一定的开销。所有必要的信息总是被组合在一起。此类log通常使用正则表达式 - 让工作更轻松!


不要使用 ASCII art;


不要使用 ASCII 创建图表;


避免在循环中跟踪;


日志级别的使用

DLT 中提供以下日志级别:


DLT_LOG_FATAL 致命的系统错误,应该很少见


DLT_LOG_ERROR 影响正确功能的错误


DLT_LOG_WARN 无法确保正确行为时发出警告


DLT_LOG_INFO 信息,提供高层次的理解


DLT_LOG_DEBUG 程序员详细调试信息


DLT_LOG_VERBOSE 程序员的详细调试信息


请注意默认日志级别设置为 INFO;这意味着记录在 INFO、WARN、ERROR 和 FATAL 中的消息将被记录。提示:可以通过设置环境变量来更改默认日志级别(请参阅 DLT 库 - 运行时配置)。


DLT API 使用

注册申请

重要的提示:因为 DLT是非异步线程安全函数,所以在子线程中不能使用。


DLT_REGISTER_APP 是异步的。建立 IPC 通道可能需要几毫秒的时间。因此,如果您在注册后立即登录,可能会丢失消息。在应用程序初始化期间,必须通过调用 DLT_REGISTER_APP() 尽早注册 DLT 应用程序。每个应用程序只允许调用一次 DLT_REGISTER_APP()。必须指定应用程序 ID(最多四个字符)并且在 ECU 中必须是唯一的。在这个例子中使用了“MAPP”。并且还可以指定应用程序的描述,这里是“用于日志记录的测试应用程序”。


int main(int argc, const char* argv[])

{

    DLT_REGISTER_APP("MAPP","Test Application for Logging");

}

获取应用程序 ID

要获取应用程序 ID 值,请求分配一个至少 4 字节长度的字符数组并输入到函数调用中。


应用程序 ID 将存储在此输入字符数组中。


MACRO


DLT_GET_APPID(appid);

Function


dlt_get_appid(appid);

定义和注册所有日志上下文

可以根据需要定义尽可能多的上下文。这些上下文可以在不同的 C 或 CPP 文件中声明为上下文。但是每个上下文只允许声明一次。因此,必须为每个上下文使用唯一的变量名称。


DLT_DECLARE_CONTEXT(myContext1);

DLT_DECLARE_CONTEXT(myContext2);

DLT_DECLARE_CONTEXT(myContext3);

如果应使用来自另一个 C 或 CPP 文件的上下文,则可以通过调用来导入这些上下文:


DLT_IMPORT_CONTEXT(myContext1);

DLT_IMPORT_CONTEXT(myContext2);

DLT_IMPORT_CONTEXT(myContext3);

在注册应用程序并声明上下文后,需要在应用程序初始化期间尽早注册上下文。 DLT_REGISTER_CONTEXT() 不应在 DLT_REGISTER_APP() 之前调用。


在注册每个上下文期间,必须提供一个上下文 ID(最多四个字符)。在这个例子中使用了“TESX”。还可以提供上下文的描述;这里是“用于日志记录的测试上下文 X”。还可以使用宏 DLT_REGISTER_CONTEXT_LL_TS 使用预定义的日志级别和跟踪状态注册上下文。使用此方法注册第三个上下文。


int main(int argc, const char* argv[])

{

  DLT_REGISTER_APP("MAPP","Test Application for Logging");

 

  DLT_REGISTER_CONTEXT(myContext1,"TES1","Test Context 1 for Logging");

  DLT_REGISTER_CONTEXT(myContext2,"TES2","Test Context 2 for Logging");

  DLT_REGISTER_CONTEXT_LL_TS(myContext3, "TES3","Test Context 3 for Logging",

                             DLT_LOG_DEBUG, DLT_TRACE_STATUS_OFF);

}

注意:请注意,在 DLT 守护程序和应用程序之间的日志级别同步完成之前,可能需要一秒钟的时间。


注销上下文和应用程序

在终止应用程序注册的上下文之前,最后需要取消注册应用程序。


int main(int argc, const char* argv[])

{

/* business logic */

 

  DLT_UNREGISTER_CONTEXT(myContext1);

  DLT_UNREGISTER_CONTEXT(myContext2);

  DLT_UNREGISTER_CONTEXT(myContext3);

 

  DLT_UNREGISTER_APP();

 

  return 0;

}

log命令

DLT 提供的函数允许使用任意数量的参数灵活构建消息。支持 Verbose 和 Non-Verbose 消息,具有不同的 API。使用这些函数发送消息需要多个函数调用,以启动消息构造、添加参数和发送消息。


下表显示了使用常量字符串和整数进行日志记录的所有 4 种类型的示例。


Verbose与非Verbose API


以下部分显示了所有 4 种日志类型的示例,例如一个字符串和一个整数。


MACRO


Verbose


DLT_LOG(ctx, DLT_LOG_INFO, DLT_STRING("ID: "), DLT_UINT32(123));

Non-Verbose


DLT_LOG_ID(ctx, DLT_LOG_INFO, 42 /* unique message ID */, DLT_STRING("ID: "),

           DLT_UINT32(123));

Function


Verbose


if (dlt_user_log_write_start(&ctx, &ctxdata, DLT_LOG_INFO) > 0) {

    dlt_user_log_write_string(&myctxdata, "ID: ");

    dlt_user_log_write_uint32(&myctxdata, 123);

    dlt_user_log_write_finish(&myctxdata);

}

Non-Verbose


if (dlt_user_log_write_start_id(&ctx, &ctxdata, DLT_LOG_INFO, 42) > 0) {

    dlt_user_log_write_string(&myctxdata, "ID: ");

    dlt_user_log_write_uint32(&myctxdata, 123);

    dlt_user_log_write_finish(&myctxdata);

}

记录参数

可以使用以下参数类型。可以将多个参数添加到单个日志消息中。所有日志参数的大小加在一起不应超过 1390 字节,包括 DLT 消息头。


类型 说明

DLT_STRING(TEXT) 字符串


DLT_STRING_ATTR(TEXT,NAME) 字符串(带属性)


DLT_SIZED_STRING(TEXT,LENGTH) 已知长度的字符串


DLT_SIZED_STRING_ATTR(TEXT,LENGTH,NAME) 已知长度的字符串(带属性)


DLT_CSTRING(TEXT) 常量字符串(不以非详细模式发送)


DLT_CSTRING_ATTR(TEXT,NAME) 常量字符串(带属性;不以非详细模式发送)


DLT_SIZED_CSTRING(TEXT,LENGTH) 已知长度的常量字符串(不以非详细模式发送)


DLT_SIZED_CSTRING_ATTR(TEXT,LENGTH,NAME) 已知长度的常量字符串(带属性;不以非详细模式发送)


DLT_UTF8(TEXT) utf8 编码的字符串


DLT_UTF8_ATTR(TEXT,NAME) Utf8 编码字符串(带属性)


DLT_SIZED_UTF8(TEXT,LENGTH) 已知长度的 Utf8 编码字符串


DLT_SIZED_UTF8_ATTR(TEXT,LENGTH,NAME) 已知长度的 utf8 编码字符串(带属性)


DLT_RAW(BUF,LENGTH) 原始缓冲区


DLT_RAW_ATTR(BUF,LENGTH,NAME) 原始缓冲区(带属性)


DLT_INT(VAR) 整数变量,取决于平台


DLT_INT_ATTR(VAR,NAME,UNIT) 整数变量,取决于平台(带属性)


DLT_INT8(VAR) 整数 8 位变量


DLT_INT8_ATTR(VAR,NAME,UNIT) 整数 8 位变量(带属性)


DLT_INT16(VAR) 整数 16 位变量


DLT_INT16_ATTR(VAR,NAME,UNIT) 整数 16 位变量(带属性)


DLT_INT32(VAR) 整数 32 位变量


DLT_INT32_ATTR(VAR,NAME,UNIT) 整数 32 位变量(带属性)


DLT_INT64(VAR) 整数 64 位变量


DLT_INT64_ATTR(VAR,NAME,UNIT) 整数 64 位变量(带属性)


DLT_UINT(VAR) 无符号整数变量


DLT_UINT_ATTR(VAR,NAME,UNIT) 无符号整数变量(带属性)


DLT_UINT8(VAR) 无符号 8 位整数变量


DLT_UINT8_ATTR(VAR,NAME,UNIT) 无符号 8 位整数变量(带属性)


DLT_UINT16(VAR) 无符号 16 位整数变量


DLT_UINT16_ATTR(VAR,NAME,UNIT) 无符号 16 位整数变量(带属性)


DLT_UINT32(VAR) 无符号 32 位整数变量


DLT_UINT32_ATTR(VAR,NAME,UNIT) 无符号 32 位整数变量(带属性)


DLT_UINT64(VAR) 无符号 64 位整数变量


DLT_UINT64_ATTR(VAR,NAME,UNIT) 无符号 64 位整数变量(带属性)


DLT_BOOL(VAR) 布尔变量


DLT_BOOL_ATTR(VAR,NAME) 布尔变量(带属性)


DLT_FLOAT32(VAR) 浮点型 32 位变量


DLT_FLOAT32_ATTR(VAR,NAME,UNIT) 浮点型 32 位变量(带属性)


DLT_FLOAT64(VAR) Float 64 位变量


DLT_FLOAT64_ATTR(VAR,NAME,UNIT) Float 64 位变量(带属性)


DLT_HEX8(UINT_VAR) 8 位十六进制值


DLT_HEX16(UINT_VAR) 16 位十六进制值


DLT_HEX32(UINT_VAR) 32 位十六进制值


DLT_HEX64(UINT_VAR) 64 位十六进制值


DLT_BIN8(UINT_VAR) 8 位二进制值


DLT_BIN16(UINT_VAR 16 位二进制值


DLT_PTR(PTR_VAR) 用于打印指针的架构独立宏

————————————————

版权声明:本文为CSDN博主「andylauren」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/andylauren/article/details/121118770




*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
属于自己的技术积累分享,成为嵌入式系统研发高手。
推荐文章
最近访客