knowledge_map/log实训.md
2021-01-22 10:12:08 +08:00

12 KiB
Raw Blame History

log实训

log简介

log的作用

log的定义:日志是将软件运行的状态、过程等信息,输出到不同的介质中(例如:文件、控制台、显示屏等),并进行显示和保存。为软件调试、维护过程中的问题追溯、性能分析、系统监控、故障预警等功能,提供参考依据。可以说,日志的使用,几乎占用的软件生命周期的至少 80% 的时间。

log的重要性:对于操作系统而言,由于其软件的复杂度非常大,单步调试在一些场景下并不适合,所以日志组件在操作系统上几乎都是标配。完善的日志系统也能让操作系统的调试事半功倍

px4所使用的log为ulog.

## ulog的功能

ULog File Format

  • Ulog是一种用来记录系统数据的日志格式。这种格式是自解释的比如他包含了日志的格式和消息类型。
  • 他可以用来记录设备的输入传感器等内部状态CPU负载姿态等以及打印日志信息。
  • 采用小端格式。(注:低字节存储在低地址)

数据类型

  • 下面列举了使用的数据类型他们都与C语言的类型相对应。

请输入图片描述

  • 此外所有类型都可以使用数组,比如float[5]。一般而言所有的字符串(char[length])结尾都不包含 '\0'。字符串大小写敏感。

文件结构

  • 文件包含三个部分:
----------------------
|       Header       |
----------------------
|    Definitions     |
----------------------
|        Data        |
----------------------
  • 头部
  • 头部大小固定,格式如下(16 bytes):
----------------------------------------------------------------------
| 0x55 0x4c 0x6f 0x67 0x01 0x12 0x35 | 0x00         | uint64_t       |
| File magic (7B)                    | Version (1B) | Timestamp (8B) |
----------------------------------------------------------------------
  • Version是文件格式的版本当前是0。时间戳是uint64_t类型用微秒表示记录开始的时间。
  • 定义部分Definitions Section
  • 长度可变,包含版本信息,格式定义以及(初始) 参数值。
  • 定义部分和数据部分由消息流组成,消息流以下面这样的头部开始:
struct message_header_s {
    uint16_t msg_size;
    uint8_t msg_type
};
  • msg_size 消息去掉头部的字节数 (hdr_size= 3 bytes). msg_type定义了内容,是下面可能的情况之一:
    • 'F': format definition for a single (composite) type that can be logged or used in another definition as a nested type.
    • 'F': 单一(混合)类型的格式定义,用于日志记录或者作为嵌套类型用在其他的定义中。
struct message_format_s {
    struct message_header_s header;
    char format[header.msg_size-hdr_size];
};
  • format: 纯文本字符串,格式如下: message_name:field0;field1;可以有任意数量的field (至少 1), 用 ;隔开。 field 的格式: type field_name 或者数组形式 type[array_length] field_name(只支持固定尺寸的数组). type 可以是基本的数据类型,也可以是另一种格式定义的message_name (嵌套用法).
  • type可以在定义前使用。可以任意地嵌套但是不要循环依赖。
  • 有一些特殊的field:
  • timestamp: 每个日志消息 (message_add_logged_s) 必须包含一个。timestamp field (不必是第一个). 他的type可以是:
  `uint64_t` \(当前唯一被用到的\), `uint32_t`, `uint16_t` or  
  `uint8_t`. 除了 `uint8_t` 的单位是毫秒,其他单位都是微秒 。
  日志写入器必须确保记录日志消息足够频繁,能够检测环绕,一个日志读取器必须处理环绕
  \(并且考虑到数据丢失\. 拥有相同`msg_id`的消息序列的timestamp必须单调增加.
  • Padding: 以_padding 开头的field名称不应该被显示并且读取器应该忽略他们的数据should not be displayed and their data must be ignored by a reader. 写入器插入这些 fields 用来确保正确的对齐。

    如果 padding field 是最后一个field, 那么这个field不会被记录,这样就避免了写入不必要的数据 这使message_data_s.data 得以缩短 。然而当消息用于嵌套定义的时候依然需要padding

  • 'I': information message.

struct message_info_s {
    struct message_header_s header;
    uint8_t key_len;
    char key[key_len];
    char value[header.msg_size-hdr_size-1-key_len]
};
  • key 是一个纯文本字符串, 只包含一个field没有;结尾,例如 float[3] myvalues. value 含有用key描述的数据。
  • 预定义的 information messages :

请输入图片描述

  • ver_sw_releasever_os_release的格式是: 0xAABBCCTT, AA 是 major主版本号, BB 是 minor次版本号, CC 是 patch补丁版本 and TT 是类型. 类型 定义如下: >= 0: development, >= 64: alpha version, >= 128: beta version, >= 192: RC version, == 255: release version. 例如 0x010402ff 转换成版本为 v1.4.2.
  • This message can also be used in the Data section (this is however the preferred section).
    • 'P': 参数消息. 和message_info_s格式一样. 如果一个参数在运行时实时改变, 那这个消息也可以用在数据部分(Data section).
数据类型限制为: `int32_t`, `float`.
  • This section ends before the start of the first message_add_logged_s or message_logging_s message, whichever comes first.

数据部分Data Section

  • 下列消息属于这一部分:
  • 'A': 订阅一个message并且赋予它一个用于message_data_s的id. This must come before the first corresponding message_data_s.
struct message_add_logged_s {
    struct message_header_s header;
    uint8_t multi_id;
    uint16_t msg_id;
    char message_name[header.msg_size-hdr_size-3];
};
  • multi_id: 相同的消息格式可以通过multi_id赋予多个实例。默认的第一个实例为0。 msg_id: 唯一的 id 用来匹配 message_data_s 数据.第一次用必须置0然后增加(The first use must set this to 0, then increase it.) 不同的订阅必须使用不同的id,甚至在取消订阅之后也不能使用相同的id message_name: 要订阅的消息名称. 必须与message_format_s 中的一个定义相匹配.
  • 'R': 取消订阅一个message,标记这个消息不再被记录 (当前没有使用).
struct message_remove_logged_s {
    struct message_header_s header;
    uint16_t msg_id;
};
  • 'D': 包含记录的数据.
struct message_data_s {
    struct message_header_s header;
    uint16_t msg_id;
    uint8_t data[header.msg_size-hdr_size];
};
  • msg_id: 被message_add_logged_s定义的 message。 data 包含被 message_format_s定义的 二进制消息. 关于padding特殊的处理机制查看上面.
  • 'L': 记录的字符串消息, i.e. printf output.
struct message_logging_s {
    struct message_header_s header;
    uint8_t log_level;
    uint64_t timestamp;
    char message[header.msg_size-hdr_size-9]
};
  • timestamp:微秒为单位, log_level: 与 Linux kernel 一样:

请输入图片描述

  • synchronization message so that a reader can recover from a corrupt message by search for the next sync message (not used currently). 'S': 同步消息,消息阅读器通过搜索下一个同步消息的方式从一个损坏的消息恢复。(当前未使用)
struct message_sync_s {
  struct message_header_s header;
  uint8_t sync_magic[8];
};
  • sync_magic: 待定义(to be defined).
  • 'O': 标记一个在以ms给定的时间段内的数据丢失 (丢失日志消息)。 比如设备不够快的时候就会发生消息丢失.
struct message_dropout_s {
    struct message_header_s header;
    uint16_t duration;
};
  • 'I': information message. See above.
  • 'P': parameter message. See above.

Requirements for Parsers

  • A valid ULog parser must fulfill the following requirements:
    • Must ignore unknown messages (but it can print a warning).
    • Parse future/unknown file format versions as well (but it can print a warning).
    • Must refuse to parse a log which contains unknown incompatibility bits set (incompat_flags of ulog_message_flag_bits_s message), meaning the log contains breaking changes that the parser cannot handle.
    • A parser must be able to correctly handle logs that end abruptly, in the middle of a message. The unfinished message should just be discarged.
    • For appended data: a parser can assume the Data section exists, i.e. the offset points to a place after the Definitions section.
  • Appended data must be treated as if it was part of the regular Data section.

Known Implementations

File Format Version History

  • Changes in version 2
  • Addition of ulog_message_info_multiple_header_s and ulog_message_flag_bits_s messages and the ability to append data to a log. This is used to add crash data to an existing log. If data is appended to a log that is cut in the middle of a message, it cannot be parsed with version 1 parsers. Other than that forward and backward compatibility is given if parsers ignore unknown messages.

该记录器能够记录任何包含所有字段的ORB主题。所有必要的内容都从.msg文件中生成因此只需要指定主题名称。一个可选的间隔参数指定了某个主题的最大记录速率。一个主题的所有现有实例都会被记录。默认情况ulog会在飞机解锁之后开始记录,飞机锁定之后停止记录..

logger

源代码: modules/logger

说明

系统日志记录一组可配置的uORB主题和系统打印消息PX4_WARNPX4_ERR到ULog文件。这些可用于系统和飞行性能评估调整再现和意外分析。

它支持2个后端

  • 文件将ULog文件写入文件系统SD卡
  • MAVLink通过MAVLink向客户端传输ULog数据客户端必须支持此功能

两个后端可以同时启用和使用。

实现

实现使用两个线程:

  • 主线程以固定速率运行(如果以-p参数启动的话则轮询主题并检查数据更新
  • 写入线程,将数据写入文件

两个线程之间有一个可配置大小的写入缓存区。写入缓存应该足够大,以避免数据丢失。

示例

立即启动日志的典型用法

logger start -e -t

如果已经在运行状态:

logger on

用法

logger <命令> [参数...]
 命令:
   start
     [-m <val>]  后端模式
                 可选: file|mavlink|all, 缺省: all
     [-e]        启动后立即开始记录,直到上锁(否则只在解锁后开始记录)
     [-f]        记录直到关闭为止(包含 -e)
     [-t]        使用日期/时间命名日志目录和文件
     [-r <val>]  记录速率单位Hz值为0则不限速
                 缺省: 280
     [-b <val>]  记录缓存大小单位KiB
                 缺省: 12
     [-q <val>]  mavlink模式下uORB队列大小
                 缺省: 14
     [-p <val>]  轮询主题而不是以固定速率运行(记录速率和主题间隔将被忽略)
                 可选: <topic_name>

   on            立即开始记录,覆盖解锁(日志系统必须在运行中)

   off           立即停止记录,覆盖锁定(日志系统必须在运行中)

   stop

   status        打印状态信息