Diagnostic Log and Trace——开发人员如何使用 DLT

Posted andylauren

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Diagnostic Log and Trace——开发人员如何使用 DLT相关的知识,希望对你有一定的参考价值。

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) 用于打印指针的架构独立宏

以上是关于Diagnostic Log and Trace——开发人员如何使用 DLT的主要内容,如果未能解决你的问题,请参考以下文章

Diagnostic Log and Trace——dlt的编译和安装

Diagnostic Log and Trace——开发人员如何使用 DLT

Diagnostic Log and Trace——为应用程序和上下文设置日志级别的方法

使用 Trace 和 TraceSource 的区别

Diagnostic and programming Interface

Diagnostic and programming Interface