Linux死机排查方法——内存日志

news2024/11/15 23:28:45

一般情况下,Linux系统在死机时会产生一些dump信息,例如oops,通过分析oops信息就可以基本定位问题所在,但有些特殊情况下死机时,没有任何的打印的信息。如果直接使用printk等打印排查问题,有可能会因为printk输出缓慢改变了系统运行的时序,导致问题无法复现,而且在中断里使用printk将大大降低系统性能。如果有DS-5等硬件调试工具,那是最好的,如果没有,那么这时候可以借助一种特殊手段来排查问题,也就是内存日志。

本文所描述的内存日志,并不是将内核的printk重定向到内存中,因为printk的打印太多了,如果将日志写入到内存中,那就比正常的串口printk快的多,对系统的影响最小。简而言之,这种方法就是将关键模块的日志保存在内存中,等到下一次启动时,再将这些日志全部dump出来。这里有两个需要注意的地方:

1、为了尽可能地减小日志大小,写入的日志为16进制格式,自己定义好协议即可,最后看日志的时候,再将16进制日志翻译成自己能看懂的格式。

2、这种方法对DDR有一定的要求,要求死机后复位重启(非断电重启)后DDR里的数据能保持。笔者的板子由于有PMIC给SOC供电,DDR是额外供电的,复位时只复位PMIC,因此DDR数据能保持。另外笔者试过,有些平台看门狗复位后DDR的数据能继续保持,有些则不行,可以做个小实验:在kernel中使用devmem命令在高地址中写入一个特殊数据,然后利用看门狗溢出进行复位,复位后在uboot里将这个地址的数据打印出来,看是否一致,如果一致就说明看门狗复位后DDR数据不会丢失,可以使用这种方法。

下面是我实现的一个mem_log模块,可以根据自己的需求适当修改,例如在每条日志里增加系统的jffies等。笔者板子内存为128M,物理地址空间为0x80000000 ~ 0x87FFFFFF,将最高1M地址空间给mem_log使用,但笔者实际只使用了其中的28KB,因为mem_log的核心是记录cpu的遗言,不需要太大的空间,这可以自行调整。下面是mem_record_t的核心成员的定义:

  1. index:日志的序号,每记录一条会自增1,最后排查时就是根据index的连续性找到最后一条日志。
  2. module:用户自定义的模块,例如中断、线程调度、各种外设驱动等。
  3. flag:标志位,可以用来记录函数进入和退出,是在哪个cpu核上运行等。
  4. args:参数,当记录的模块为中断时,args可以保存中断号;同理,当记录的模块为线程时,可以保存切入和切出的线程名;当记录的模块为外设驱动时,可以保存驱动名称。
#include "linux/mem_log.h"
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <asm/io.h>

#define MEM_LOG_START_ADDR  (0x87F00000)    /* mem_log的起始物理地址 */
#define MEM_LOG_SIZE        (28*1024L)      /* mem_log的大小 */

typedef struct
{
    unsigned int        index;
    unsigned char       module;
    unsigned char       flag;
    unsigned char		args[10];
}mem_record_t;

static volatile unsigned int *log_mem_addr = NULL;
static unsigned int mem_log_index = 0;
static mem_record_t *wrecord = NULL;

#ifdef CONFIG_SMP
static DEFINE_SPINLOCK(mem_log_spinlock);
#endif

void mem_log_init(void)
{
    log_mem_addr = ioremap(MEM_LOG_START_ADDR, MEM_LOG_SIZE);
    if(!log_mem_addr)
    {
        printk(KERN_EMERG"mem_log_init failed.");
        return;
    }
    wrecord = (mem_record_t*)log_mem_addr;
	
}
EXPORT_SYMBOL(mem_log_init);

void mem_log_clear(void)
{
   if(log_mem_addr)
   {
        memset((void*)log_mem_addr , 0xFF, MEM_LOG_SIZE);
   }
}
EXPORT_SYMBOL(mem_log_clear);

void mem_log_record(uint8_t module, uint8_t flag, uint8_t *args, uint8_t args_len)
{
#ifdef CONFIG_SMP
	unsigned long flags;
#endif
	static int print = 0;
	
	if(!wrecord)
	{
		if(!print)
		{
			print = 1;
			printk(KERN_EMERG"please use mem_log_init first.\n");			
		}

		return;
	}
#ifdef CONFIG_SMP
	spin_lock_irqsave(&mem_log_spinlock, flags);
#endif	
    wrecord->index = mem_log_index++;
    wrecord->module = module;
#ifdef CONFIG_SMP
    wrecord->flag = (flag << 4) | smp_processor_id();
#else
    wrecord->flag = (flag << 4);
#endif

	memcpy(wrecord->args, args, args_len);
	
    wrecord = wrecord + 1;
    /* 日志写满后从头覆盖写 */
    if((unsigned int)wrecord >= ((unsigned int)log_mem_addr + MEM_LOG_SIZE))
    {
        wrecord = (mem_record_t*)log_mem_addr;
    }
#ifdef CONFIG_SMP
	spin_unlock_irqrestore(&mem_log_spinlock, flags);
#endif	
}
EXPORT_SYMBOL(mem_log_record);

void mem_log_dump(void)
{
    mem_record_t *record = (mem_record_t*)log_mem_addr;
	uint32_t index_back = record->index;
	uint8_t found = 0;
	
    printk("mem log dump:\n");
	printk("record:%X, end:%X\n", (unsigned int)record, ((unsigned int)log_mem_addr + MEM_LOG_SIZE));
    for(; (unsigned int)record < ((unsigned int)log_mem_addr + MEM_LOG_SIZE) ; )
    {
        printk("%08X %02X %02X %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", 
            record->index, record->module, record->flag,
            record->args[0],record->args[1],record->args[2],record->args[3],record->args[4],
            record->args[5],record->args[6],record->args[7],record->args[8],record->args[9]);

		record++;
		
		if(!found)
		{
			if((index_back+1) != record->index)
			{
				found = 1;
				continue;
			}
			index_back = record->index;				
		}
    }
    /* 找到最后一条index不连续的日志, 即死机前的最后一条日志 */
	printk("find last log index:%08X!\n", index_back);
	
}
EXPORT_SYMBOL(mem_log_dump);

以下是对应的头文件,我定义了三个模块:中断、线程退出、线程切入,当然还可以定义其他一些模块,例如我怀疑SD驱动有问题,可以定义SD模块。Flag只定义了函数进入和函数退出,如果最后的日志只有MEM_LOG_FLAG_FUNC_IN而没有MEM_LOG_FLAG_FUNC_OUT,那么恭喜,就是卡死在这个函数里了。

#ifndef _LINUX_MEM_LOG_H
#define _LINUX_MEM_LOG_H

#include "linux/string.h"

#define MEM_LOG_MODULE_IRQ			(0x11)
#define MEM_LOG_MODULE_THREAD_PRE	(0x22)
#define MEM_LOG_MODULE_THREAD_NEXT	(0x33)

#define MEM_LOG_FLAG_FUNC_IN		(0x01)
#define MEM_LOG_FLAG_FUNC_OUT		(0x02)

void mem_log_init(void);
void mem_log_clear(void);
void mem_log_record(uint8_t module, uint8_t flag, uint8_t *args, uint8_t args_len);
void mem_log_dump(void);

#endif

由于我的板子是直接从TF卡引导kernel启动,没有uboot阶段,因此重启的日志直接从kernel里打印,我将打印加在了内核启动时的start_kernel函数里:

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
	char *command_line;
	char *after_dashes;

	set_task_stack_end_magic(&init_task);
	smp_setup_processor_id();
	debug_objects_early_init();
    
    ……

    console_init();
	if (panic_later)
		panic("Too many boot %s vars at `%s'", panic_later,
		      panic_param);

	lockdep_init();

    mem_log_init();    
    mem_log_dump();    
    mem_log_clear();

	/*
	 * Need to run this when irqs are enabled, because it wants
	 * to self-test [hard/soft]-irqs on/off lock inversion bugs
	 * too:
	 */
	locking_selftest();
    
    ……
}

首次上电时,由于先前没有记录任何日志,所以mem_log_dump会打印一堆脏数据,无需关心。此时mem_log已经初始化完成,在DDR高地址区域开辟了一块空间专门给mem_log使用,需要注意内核不能再使用这段内存,因此需要修改bootargs中的mem参数。此时已经可以在可疑的地方进行打桩,我们知道,程序的执行无外乎两个地方:线程和中断,因此我在这两个地方用mem_log_record函数进行打桩,下面是伪代码示意。

/* 在线程调度的地方打桩 */
static void __sched notrace __schedule(bool preempt)
{
    ……
 	mem_log_record(MEM_LOG_MODULE_THREAD_PRE, MEM_LOG_FLAG_FUNC_IN, (uint8_t*)prev->comm, 10);
	mem_log_record(MEM_LOG_MODULE_THREAD_NEXT, MEM_LOG_FLAG_FUNC_IN, (uint8_t*)next->comm, 10);
	rq = context_switch(rq, prev, next, &rf);
	mem_log_record(MEM_LOG_MODULE_THREAD_PRE, MEM_LOG_FLAG_FUNC_OUT, (uint8_t*)prev->comm, 10);
	mem_log_record(MEM_LOG_MODULE_THREAD_NEXT, MEM_LOG_FLAG_FUNC_OUT,(uint8_t*)next->comm, 10);   
    ……
}

/* 在中断入口打桩 */
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
			bool lookup, struct pt_regs *regs)
{
	……
	irq_enter();
	mem_log_record(MEM_LOG_MODULE_IRQ, MEM_LOG_FLAG_FUNC_IN, (uint8_t*)&hwirq, 4);
    ……
	generic_handle_irq(irq);
    ……
	mem_log_record(MEM_LOG_MODULE_IRQ, MEM_LOG_FLAG_FUNC_OUT, (uint8_t*)&hwirq, 4);
    irq_exit();
	……
}

下面是我的板子死机的实际样例,下面是死机复位后dump的日志,mem_log会找到最后一个不连续的index日志:

可以看到最后截断的日志序号是6F7278BD,将上述日志翻译一下,如下:

 可以看到,最后是切换到arecord进程后卡死了,但具体是里面操作哪个模块卡死的,还需要进一步打桩进行定位。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1438711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm+vue的校园一卡通密钥管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的校园一卡通密钥管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

240207-3步设置VSCode插件Inline-Bookmarks自定义颜色及名称

Step 1: 插件安装 Step 2: 配置文件 "inline-bookmarks.expert.custom.styles": {"default": {"gutterIconColor": "#157EFB","overviewRulerColor": "rgba(21, 126, 251, 0.7)","light": {"fontW…

使用HCPpipelines分割皮层

前段时间阅读了一篇文献,文章的做法我比较感兴趣,所以打算学习一下文献的做法。文章的最开始一部分是使用HCPpipelines对T1和T2像进行皮层分割,调用的是freesurfer6。https://github.com/Washington-University/HCPpipelines 一、工作环境准备 1.安装好FSL,版本在6.0.2以上…

H2和流行关系型数据库对比

1.H2和SQLite数据库对比 1.1.独特的特点和用途 H2 和 SQLite 是两个流行的轻量级数据库&#xff0c;它们各自有一些独特的特点和用途&#xff1a; H2 数据库: 主要用于 Java 应用&#xff0c;因为它是用 Java 编写的。支持内存模式和磁盘持久化。提供了一个基于浏览器的控制台…

Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi

文章目录 简介Asp .Net Core 集成 Panda.DynamicWebApi配置原理什么是POCO Controller&#xff1f;POCO控制器原理ControllerFeatureProvider实现自定义判断规则IApplicationModelConventionPanda.DynamicWebApi中的实现ConfigureApiExplorer()ConfigureSelector()ConfigurePar…

板块零 IDEA编译器基础:第三节 下载和在IDEA中集成 Tomcat服务器 来自【汤米尼克的JAVAEE全套教程专栏】

板块零 IDEA编译器基础&#xff1a;第三节 下载和在IDEA中集成 Tomcat服务器 一、为什么选择Tomcat&#xff08;1&#xff09;常见的JAVA WEB服务器&#xff08;2&#xff09;选择Tomcat的理由 二、Tomcat 8.5下载解压三、Tomcat 结构目录四、在IDEA中集成Tomcat 假设我们已经…

基于STM32平台的嵌入式AI音频开发

加我微信hezkz17&#xff0c;可申请加入 嵌入式人工智能开发交流答疑群。 1 stm32芯片AI开发流程 其中模型也可以选择tensorflow &#xff0c;pytorch 2 FP-AI-SENSING1 SDK开发包介绍 3 声音场景分类项目数据集选择 (1)自己采集数据打标签 (2) 使用专用数据集 4 完整参考

如何使用phpStudy搭建网站并结合内网穿透远程访问本地站点

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

Nacos(1)

Nacos注册中心 主要解决问题 假如微服务被调用较多&#xff0c;为了应对更高的并发&#xff0c;进行了多实例部署 此时&#xff0c;每个微服务的实例其IP或端口不同&#xff0c;问题来了&#xff1a; 这么多实例&#xff0c;如何知道每一个实例的地址&#xff1f;http请求要…

零基础学编程从哪里入手,编程实例分享,配件进出库管理系统软件

零基础学编程从哪里入手&#xff0c;编程实例分享&#xff0c;配件进出库管理系统软件 一、前言 对于刚学编程的人来说&#xff0c;多看看现有的软件实例对自己学开发软件是很有帮助的。 下面分享的实例以配件进出库管理系统软件为例说明。 软件文件下载可以点击最下方官网…

031-安全开发-JS应用WebPack打包器第三方库JQuery安装使用安全检测

031-安全开发-JS应用&WebPack打包器&第三方库JQuery&安装使用&安全检测 #知识点&#xff1a; 1、三方库-JQuery-使用&安全 2、打包器-WebPack-使用&安全 演示案例&#xff1a; ➢打包器-WebPack-使用&安全 ➢第三方库-JQuery-使用&安全 #为什么…

应用案例 | Softing dataFEED OPC Suite助力挤出机械自动化系统OPC UA升级

某知名挤出机械整体方案供应商在其最新自动化系统中采用了Softing dataFEED OPC Suite作为标准的OPC UA通信方案&#xff0c;不仅可采集多个西门子S7-1200控制器数据&#xff0c;而且为终客户提供了可靠、高性能的挤出机械自动化解决方案。 一 背景 多年前&#xff0c;该挤出机…

寒假漫游记之CSS

一&#xff0c;CSS 1.CSS语法规范 CSS规则由两个主要的部分构成&#xff1a;选择器及一条或多条声明。 &#xff08;选择器是用于指定CSS样式的HTML标签&#xff09; 注&#xff1a;CSS是写在<style></style>里 (style在<head></head>),具体可以书写…

进程状态 | 僵尸进程 | 孤儿进程 | 前台后台进程 | 守护进程

文章目录 1.进程的三种基本状态2.Linux中进程状态查看2.1.进程检测脚本2.2.各种状态查看 3.孤儿进程4.前台、后台、守护进程 1.进程的三种基本状态 进程的在系统当中是走走停停的&#xff0c;「运行 - 暂停 - 运行」的活动规律&#xff1b;进程在活动期间的三种状态&#xff1…

《计算机网络简易速速上手小册》第7章:云计算与网络服务(2024 最新版)

文章目录 7.1 云服务模型&#xff08;IaaS, PaaS, SaaS&#xff09;- 你的技术魔法盒7.1.1 基础知识7.1.2 重点案例&#xff1a;构建和部署 Python Web 应用实现步骤具体操作步骤1&#xff1a;创建 Flask Web 应用步骤2&#xff1a;准备应用部署 7.1.3 拓展案例1&#xff1a;使…

transformer剪枝论文汇总

文章目录 NN Pruning摘要实验 大模型剪枝LLM-PrunerSparseGPT LTPVTPWidth & Depth PruningPatch SlimmingDynamicViTSPViTDynamicBERTViT SlimmingFastFormersNViTUVCPost-training pruning NN Pruning 《Block Pruning For Faster Transformers》 《为更快的transformer…

MPC |模型预测控制的一些基本概念

模型预测控制就是在每个采样点处&#xff0c;根据被控对象的状态和预测模型&#xff0c;预测系统在未来一段时间内的状态&#xff0c;依据某一性能指标&#xff08;成本函数&#xff09;来求解最优的一组控制序列&#xff0c;并将这组控制序列的第一个控制作用作为输出给执行机…

【数据库】Unlogged 表使用

【数据库】Unlogged 表使用 前言普通表和Unlogged 表的写性能比较普通表创建和数据插入Unlogged 表创建和数据插入比较结果 Unlogged 表崩溃和正常关闭测试Unlogged 表特点总结 前言 大神偶像在开会上提及了Unlogged 表&#xff0c;它的特点很不错&#xff0c;很适合实时数据保…

顺序表、链表相关OJ题(2)

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; 一、旋转数组&#xff08;力扣&#xff09; 经典算法OJ题&#xff1a;旋转数组 思路1&#xff1a;每次挪动1位&#xff0c;右旋k次 时间复杂度&#xff1a;o(N^2) 右旋最好情况&#xff1a;k是n的倍数…

寒假作业5

TCP 1&#xff1a;提供面向连接的&#xff0c;可靠的数据传输服务 2&#xff1a;传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复 3&#xff1a;数据传输效率低&#xff0c;耗费资源多 4&#xff1a;数据收发是不同步的 5&#xff1a;TCP的使用场景&#…