首页 > 其他分享 >嵌入式中,日志调试法的一些规则

嵌入式中,日志调试法的一些规则

时间:2024-02-07 14:11:36浏览次数:25  
标签:LOG void 嵌入式 模块 日志 fmt 调试 define

https://mp.weixin.qq.com/s/yTInDBFbI0oM5bowx990lw

在我们嵌入式开发中,打印日志是最常用的一种调试手段。合理地打印日志,可以帮助我们快速地分析问题。

本篇文章我们来汇总一些嵌入式打log的一些规则。

1.什么操作下加日志?

(1)错误处理

对于不能恢复的严重错误,日志内容应详细到足以帮助定位问题,但同时不应该包含敏感信息。比如申请内存失败时使用错误(Error)级别加上日志信息。

(2)一些关键性的操作

一些很关键地处理,无论是正常情况或者异常情况都要打印日志。比如wifi打开时要有对应的日志信息。

(3)系统的打开、关闭

记录系统启动和关闭过程中的关键步骤有助于分析系统初始化是否正确,或者系统是否正常关闭。

(4)性能监控

日志可以记录系统运行的关键性能指标,比如CPU和内存使用率、IO操作等,以便进行系统性能分析和优化。

(5)关键数据

一些关键数据需要打印,很多功能上的问题大多直接与数据进行挂钩。

(6)通信日志

对于需要与外部设备或网络通信的嵌入式系统,记录通信日志可以帮助分析和调试通信协议或数据交换的问题。

(7)记录用户行为

在需要分析用户如何与嵌入式设备交互的情况下,记录用户行为的日志会非常有帮助。

(8)if、switch

分支判断中,各执行分支需要加上对应的日志信息,可以帮助我们准确地知道程序执行的走向。

(9)程序崩溃时的信息

比如,Linxu下应用进程崩溃时的调用堆栈信息。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>

void func0(void)
{
    printf("This is func0\n");
    int *p = NULL;
    *p = 1234;
}

void func1(void)
{
    printf("This is func1\n");
    func0();
}

void func2(void)
{
    printf("This is func2\n");
    func1();
}

void dump(int signo)
{
    void *array[100];
    size_t size;
    char **strings;

    size = backtrace(array, 100);
    strings = backtrace_symbols(array, size);

    printf("Obtained %zd stacks.\n", size);
    for(int i = 0; i < size; i++)
    {
        printf("%s\n", strings[i]);
    }
        
    free(strings);
    exit(0);
}

int main(int argc, char **argv)
{
    printf("==================segmentation fault test5==================\n");
    signal(SIGSEGV, &dump);
    func2();

    return 0;
}

2.功能模块标签

项目中肯定会划分有多个模块,可以给各个模块标记一个模块标签字符串,包含在日志条目里。这样我们就可以在日志文件里通过模块标签来筛选某个模块的日志,提高我们定位问题的效率。

比如:


#define LOG_TAG    "[wifi_module]"
#define LOG_D(fmt, arg...) LOG_D_TAG(LOG_TAG, fmt, ##arg)
LOG_D("hello wifi module");

输出:
[wifi_module]hello wifi module

3.模块日志开关

设置模块日志开关,可以方便我们调试、分析问题时,缩小分析范围。当我们的函数设计有多个功能函数模块的时候,当某个模块出现问题时,这个时候我们只是关心此模块,那么可以先把其他模块的日志功能关闭掉,只是打开关心模块的日志。

比如:

#include "module1.h"

#if MODULE1_LOG_SWITCH
#define LOG_MODULE1(fmt, args...)   DBG_PRINTF(fmt, ##args)  
#else
#define LOG_MODULE1(fmt, args...) 
#endif

// module2.c
#include "module2.h"

#if MODULE2_LOG_SWITCH
#define LOG_MODULE2(fmt, args...)   DBG_PRINTF(fmt, ##args)  
#else
#define LOG_MODULE2(fmt, args...) 
#endif

// config.h
#define  MODULE1_LOG_SWITCH  0
#define  MODULE2_LOG_SWITCH  1

4.时间戳

日志应包含时间戳,可以方便地查看某段代码的执行时间、确定问题发生的具体时间。时间戳最好能精确到微秒/毫秒。

5.日志级别

使用不同的日志级别可以帮助筛选和控制输出的信息量。

常见的日志级别包括:

错误(Error):程序无法运行或严重问题。
警告(Warning):可能的问题,程序可以继续运行。
信息(Info):程序运行状态信息。
调试(Debug):详细的调试信息,包括变量值和程序流程。
详细(Verbose):非常详细的信息,用于深入调试。

6.格式统一

为了使日志易于阅读,所有日志应保持一致的格式。

日志里常包含的固定信息有:

  • 文件名
  • 行号
  • 时间日期/时间戳
  • 函数名
  • 模块名称
  • 进程ID
  • 线程ID
    可根据需要进行组合。比如:

[2024-01-14 11:12:30.666][wifi_module][func:wifi_init]

7.过滤控制

使用日志动态过滤控制功能可以动态地调整日志地输出,但前提是项目使用地日志组件具备这样的能力。比如EasyLogger(https://github.com/armink/EasyLogger)。

8.Release/Debug开关

由于日志打印可能占用不少系统资源,应当注意其对性能的影响。在Release版本中,可能需要减少日志输出或者去掉一些不必要的日志,需要一个开关来进行切换。

9.封装日志接口

嵌入式中,日志的输出大多数情况下会输出到串口终端。但也有一些场景,需要把日志输出到屏幕、网络设备、定制化的上位机等场景。需要留出对应接口,方便灵活切换。

标签:LOG,void,嵌入式,模块,日志,fmt,调试,define
From: https://www.cnblogs.com/lvzh/p/18010885

相关文章

  • C++实现memcpy和memmove(含调试程序)
    #include<iostream>#include<string>usingstd::cout;usingstd::endl;void*mymencpy(void*dest,void*src,size_tnum){ char*d=(char*)dest; char*s=(char*)src; while(num--){ *(d++)=*(s++); } returnd;}void*mymenmove(vo......
  • Linux下gdb如何调试coredump文件
    目录简介示例简介在Linux下,你可以使用GNU调试器(GDB)来调试coredump文件。Coredump文件是在程序崩溃时由操作系统生成的,它包含了程序崩溃时的内存内容、寄存器状态和其他相关信息。下面是在Linux下使用GDB调试coredump文件的步骤:确保你的系统已经安装了GDB。如果没有安装,你......
  • 软件测试学习笔记丨App端测试——adb日志操作
    一、日志的级别V:明细verbose(最低优先级,会输出所有日志)D:调试debugI:信息infoW:警告warnE:错误errorF:严重错误fatalS:无记载silent(最高优先级,不会输出任何日志)二、adb命令查看日志adblogcat三、查看日志常用的参数adblogcat:打印默认日志数据adblogcat-vtime:打印时间adblogcat-vc......
  • linux调试工具strace,gdb
    strace用于跟踪系统调用和信号。strace是一个集诊断、调试、统计于一体的工具,我们可以使用strace跟踪程序的系统调用和信号传递分析程序,以解决问题或了解程序工作过程。当然strace与专业的调试工具比如说gdb之类的是没法相比的,因为它不是一个专业的调试器。strace最简......
  • 常用GDB调试命令
    1.启动gdb调试gcc-ghello.c-ohello/gdbhello2.退出调试quit3.给程序设置参数/获取设置参数setargs1020showargs4.查看当前文件代码list行号/函数名(不加则从默认位置显示)5.查看非当前文件代码list文件名:行号/函数名6.设置显示的行数setlist行数7.设......
  • openGauss学习笔记-215 openGauss性能调优-确定性能调优范围-性能日志
    openGauss学习笔记-215openGauss性能调优-确定性能调优范围-性能日志215.1性能日志概述性能日志主要关注外部资源的访问性能问题。性能日志指的是数据库系统在运行时检测物理资源的运行状态的日志,在对外部资源进行访问时的性能检测,包括磁盘、OBS等外部资源的访问检测信息。ope......
  • docker中调试java代码
    以shiro550为例子在vulhub/shiro/CVE-2016-4437启动环境docker-composeup-d然后看一下当前容器启动的命令是java-jar/shirodemo-1.0-SNAPSHOT.jar将容器内的jar包复制出来dockercp容器id:/shirodemo-1.0-SNAPSHOT.jar.然后ijidea新建项目,并且解压jar包到项......
  • 浮木云学习日志(7)---可视化大屏搭建
    之前对浮木云的web端的静态页面和APP的页面搭建进行了简单的记录,虽然只是了解些皮毛,但足够支撑一些简单的页面的制作。最近我在浏览他们的公众号【武汉浮木科技有限公司】,意外发现他们对高校科技成果转化平台的模板进行了相关介绍,看了他们对这个平台的介绍,让我觉得他们对这个业务......
  • 理解日志基础:使用Python进行有效的日志记录
    源码分享https://docs.qq.com/sheet/DUHNQdlRUVUp5Vll2?tab=BB08J2日志记录是任何软件开发过程中的一个基本组成部分,尤其是在爬虫开发中。有效的日志记录策略可以帮助开发者监控爬虫的行为,诊断问题,以及追踪爬虫的性能。Python的logging模块提供了一套强大的日志记录工具,它可以帮助......
  • Remix v0.42.0 更新日志
    重要讯息向GPT提问关于CircomZKP编译器的错误或警告问题Solidity默认版本变更为0.8.24,支持坎昆EVM版本工作空间模版‘Uniswapv4Periphery’更名为‘Uniswapv4Template’GPT帮助解决关于Circom的问题您尝试过使用Remix里的Circom编译器吗?如果没有,您......