0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

pstore原理和使用方法总结

嵌入式与Linux那些事 来源:嵌入式与Linux那些事 2024-03-04 14:23 次阅读

什么是pstore

pstore最初是用于系统发生oops或panic时,自动保存内核log buffer中的日志。不过在当前内核版本中,其已经支持了更多的功能,如保存console日志、ftrace消息和用户空间日志。同时,它还支持将这些消息保存在不同的存储设备中,如内存、块设备或mtd设备。 为了提高灵活性和可扩展性,pstore将以上功能分别抽象为前端和后端,其中像dmesg、console等为pstore提供数据的模块称为前端,而内存设备、块设备等用于存储数据的模块称为后端,pstore core则分别为它们提供相关的注册接口

通过模块化的设计,实现了前端和后端的解耦,因此若某些模块需要利用pstore保存信息,就可以方便地向pstore添加新的前端。而若需要将pstore数据保存到新的存储设备上,也可以通过向其添加后端设备的方式完成。

1ab81a72-d9e0-11ee-a297-92fbcf53809c.png

除此之外,pstore还设计了一套pstore文件系统,用于查询和操作上一次重启时已经保存的pstore数据。当该文件系统被挂载时,保存在backend中的数据将被读取到pstore fs中,并以文件的形式显示。

pstore工作原理

pstore 源文件主要有以下几个:fs/pstore/ram_core.c

fs/pstore/
├──ftrace.c#ftrace前端的实现
├──inode.c#pstore文件系统的注册与操作
├──internal.h
├──Kconfig
├──Makefile
├──platform.c#pstore前后端功能的核心
├──pmsg.c#pmsg前端的实现
├──ram.c#pstore/ram后端的实现,dram空间分配与管理
├──ram_core.c#pstore/ram后端的实现,dram的读写操作

文件创建

pstore文件系统位置在:

#ls/sys/fs/pstore
console-ramoops-0dmesg-ramoops-0

控制台日志位于 pstore 目录下的console-ramoops文件中,因为采用console机制,该文件中的日志信息也受printk level控制,并不一定是全的。

oops/panic日志位于 pstore 目录下的dmesg-ramoops-x文件中,根据缓冲区大小可以有多个文件,x从0开始。函数调用序列日志位于 pstore 目录下的ftrace-ramoops文件中。

相关代码在inode.c pstore_mkfile里:

/*
*Makearegularfileintherootdirectoryofourfilesystem.
*Loaditupwith"size"bytesofdatafrom"buf".
*Setthemtime&ctimetothedatethatthisrecordwasoriginallystored.
*/
intpstore_mkfile(enumpstore_type_idtype,char*psname,u64id,intcount,
char*data,boolcompressed,size_tsize,
structtimespectime,structpstore_info*psi)
{
........................

rc=-ENOMEM;
inode=pstore_get_inode(pstore_sb);
..............................

switch(type){
casePSTORE_TYPE_DMESG:
scnprintf(name,sizeof(name),"dmesg-%s-%lld%s",
psname,id,compressed?".enc.z":"");
break;
casePSTORE_TYPE_CONSOLE:
scnprintf(name,sizeof(name),"console-%s-%lld",psname,id);
break;
casePSTORE_TYPE_FTRACE:
scnprintf(name,sizeof(name),"ftrace-%s-%lld",psname,id);
break;
casePSTORE_TYPE_MCE:
scnprintf(name,sizeof(name),"mce-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_RTAS:
scnprintf(name,sizeof(name),"rtas-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_OF:
scnprintf(name,sizeof(name),"powerpc-ofw-%s-%lld",
psname,id);
break;
casePSTORE_TYPE_PPC_COMMON:
scnprintf(name,sizeof(name),"powerpc-common-%s-%lld",
psname,id);
break;
casePSTORE_TYPE_PMSG:
scnprintf(name,sizeof(name),"pmsg-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_OPAL:
sprintf(name,"powerpc-opal-%s-%lld",psname,id);
break;
casePSTORE_TYPE_UNKNOWN:
scnprintf(name,sizeof(name),"unknown-%s-%lld",psname,id);
break;
default:
scnprintf(name,sizeof(name),"type%d-%s-%lld",
type,psname,id);
break;
}
....................

dentry=d_alloc_name(root,name);
.......................

d_add(dentry,inode);
................
}

pstore_mkfile根据不同的type,使用snprintf函数生成文件名name。生成的文件名格式为--,其中type是enum pstore_type_id类型的一个值,psname是给定的psname参数,id是给定的id参数。

接着使用d_alloc_name函数为根目录创建一个目录项dentry,最后使用d_add函数将目录项dentry与索引节点inode关联起来,将其添加到文件系统中。

pstore_register

ramoops负责把message write到某个ram区域上,platform负责从ram读取存到/sys/fs/pstore,ok,先来看机制代码platform.c。

backend需要用pstore_register来注册:

/*
*platformspecificpersistentstoragedriverregisterswith
*ushere.Ifpstoreisalreadymounted,calltheplatform
*readfunctionrightawaytopopulatethefilesystem.Ifnot
*thenthepstoremountcodewillcalluslatertofillout
*thefilesystem.
*/
intpstore_register(structpstore_info*psi)
{
structmodule*owner=psi->owner;

if(backend&&strcmp(backend,psi->name))
return-EPERM;

spin_lock(&pstore_lock);
if(psinfo){
spin_unlock(&pstore_lock);
return-EBUSY;
}

if(!psi->write)
psi->write=pstore_write_compat;
if(!psi->write_buf_user)
psi->write_buf_user=pstore_write_buf_user_compat;
psinfo=psi;
mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock);
...
/*
*Updatethemoduleparameterbackend,soitisvisible
*through/sys/module/pstore/parameters/backend
*/
backend=psi->name;

module_put(owner);

backend判断确保一次只能有一个并记录了全局psinfo。

看下结构体pstore_info:

structpstore_info{
structmodule*owner;
char*name;
spinlock_tbuf_lock;/*serializeaccessto'buf'*/
char*buf;
size_tbufsize;
structmutexread_mutex;/*serializeopen/read/close*/
intflags;
int(*open)(structpstore_info*psi);
int(*close)(structpstore_info*psi);
ssize_t(*read)(u64*id,enumpstore_type_id*type,
int*count,structtimespec*time,char**buf,
bool*compressed,ssize_t*ecc_notice_size,
structpstore_info*psi);
int(*write)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,intcount,boolcompressed,
size_tsize,structpstore_info*psi);
int(*write_buf)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,constchar*buf,boolcompressed,
size_tsize,structpstore_info*psi);
int(*write_buf_user)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,constchar__user*buf,
boolcompressed,size_tsize,structpstore_info*psi);
int(*erase)(enumpstore_type_idtype,u64id,
intcount,structtimespectime,
structpstore_info*psi);
void*data;
};

name就是backend的name了。

*write和*write_buf_user如果backend没有给出会有个默认compat func,最终都走的*write_buf。

if(!psi->write)
psi->write=pstore_write_compat;
if(!psi->write_buf_user)
psi->write_buf_user=pstore_write_buf_user_compat;
staticintpstore_write_compat(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,
u64*id,unsignedintpart,intcount,
boolcompressed,size_tsize,
structpstore_info*psi)
{
returnpsi->write_buf(type,reason,id,part,psinfo->buf,compressed,
size,psi);
}

staticintpstore_write_buf_user_compat(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,
u64*id,unsignedintpart,
constchar__user*buf,
boolcompressed,size_tsize,
structpstore_info*psi)
{
...
ret=psi->write_buf(type,reason,id,part,psinfo->buf,
...
}

继续pstore注册:

if(pstore_is_mounted())
pstore_get_records(0);

如果pstore已经mounted,那就创建并填充文件by pstore_get_records:

/*
*Readalltherecordsfromthepersistentstore.Create
*filesinourfilesystem.Don'twarnabout-EEXISTerrors
*whenwearere-scanningthebackingstorelookingtoaddnew
*errorrecords.
*/
voidpstore_get_records(intquiet)
{
structpstore_info*psi=psinfo;//tj:globalpsinfo
...
mutex_lock(&psi->read_mutex);
if(psi->open&&psi->open(psi))
gotoout;

while((size=psi->read(&id,&type,&count,&time,&buf,&compressed,
&ecc_notice_size,psi))>0){
if(compressed&&(type==PSTORE_TYPE_DMESG)){
if(big_oops_buf)
unzipped_len=pstore_decompress(buf,
big_oops_buf,size,
big_oops_buf_sz);

if(unzipped_len>0){
if(ecc_notice_size)
memcpy(big_oops_buf+unzipped_len,
buf+size,ecc_notice_size);
kfree(buf);
buf=big_oops_buf;
size=unzipped_len;
compressed=false;
}else{
pr_err("decompressionfailed;returned%d
",
unzipped_len);
compressed=true;
}
}
rc=pstore_mkfile(type,psi->name,id,count,buf,
compressed,size+ecc_notice_size,
time,psi);
if(unzipped_len< 0) {
            /* Free buffer other than big oops */
            kfree(buf);
            buf = NULL;
        } else
            unzipped_len = -1;
        if (rc && (rc != -EEXIST || !quiet))
            failed++;
    }
    if (psi->close)
psi->close(psi);
out:
mutex_unlock(&psi->read_mutex);

if needed,call pstore_decompress解压然后创建pstore文件by vfs接口pstore_mkfile。

pstore注册接下来是按类别分别注册:

if(psi->flags&PSTORE_FLAGS_DMESG)
pstore_register_kmsg();
if(psi->flags&PSTORE_FLAGS_CONSOLE)
pstore_register_console();
if(psi->flags&PSTORE_FLAGS_FTRACE)
pstore_register_ftrace();
if(psi->flags&PSTORE_FLAGS_PMSG)
pstore_register_pmsg();

psi->flags仍是由backend决定,只看pstore_register_kmsg和pstore_register_console。

pstore panic log注册

staticstructkmsg_dumperpstore_dumper={
.dump=pstore_dump,
};

/*
*Registerwithkmsg_dumptosavelastpartofconsolelogonpanic.
*/
staticvoidpstore_register_kmsg(void)
{
kmsg_dump_register(&pstore_dumper);
}

pstore_dump最终会call backend的write,直接用全局psinfo。

/*
*callbackfromkmsg_dump.(s2,l2)hasthemostrecently
*writtenbytes,olderbytesarein(s1,l1).Saveasmuch
*aswecanfromtheendofthebuffer.
*/
staticvoidpstore_dump(structkmsg_dumper*dumper,
enumkmsg_dump_reasonreason)
{
...
ret=psinfo->write(PSTORE_TYPE_DMESG,reason,&id,part,
oopscount,compressed,total_len,psinfo);

kmsg_dump_register是内核一种增加log dumper方法,called when kernel oopses or panic。

/**
*kmsg_dump_register-registerakernellogdumper.
*@dumper:pointertothekmsg_dumperstructure
*
*Addsakernellogdumpertothesystem.Thedumpcallbackinthe
*structurewillbecalledwhenthekerneloopsesorpanicsandmustbe
*set.Returnszeroonsuccessand%-EINVALor%-EBUSYotherwise.
*/
intkmsg_dump_register(structkmsg_dumper*dumper)
{
unsignedlongflags;
interr=-EBUSY;

/*Thedumpcallbackneedstobeset*/
if(!dumper->dump)
return-EINVAL;

spin_lock_irqsave(&dump_list_lock,flags);
/*Don'tallowregisteringmultipletimes*/
if(!dumper->registered){
dumper->registered=1;
list_add_tail_rcu(&dumper->list,&dump_list);
err=0;
}
spin_unlock_irqrestore(&dump_list_lock,flags);

returnerr;
}
/**
*kmsg_dump-dumpkernellogtokernelmessagedumpers.
*@reason:thereason(oops,panicetc)fordumping
*
*Calleachoftheregistereddumper'sdump()callback,whichcan
*retrievethekmsgrecordswithkmsg_dump_get_line()or
*kmsg_dump_get_buffer().
*/
voidkmsg_dump(enumkmsg_dump_reasonreason)
{
structkmsg_dumper*dumper;
unsignedlongflags;

if((reason>KMSG_DUMP_OOPS)&&!always_kmsg_dump)
return;

rcu_read_lock();
list_for_each_entry_rcu(dumper,&dump_list,list){
if(dumper->max_reason&&reason>dumper->max_reason)
continue;

/*initializeiteratorwithdataaboutthestoredrecords*/
dumper->active=true;

raw_spin_lock_irqsave(&logbuf_lock,flags);
dumper->cur_seq=clear_seq;
dumper->cur_idx=clear_idx;
dumper->next_seq=log_next_seq;
dumper->next_idx=log_next_idx;
raw_spin_unlock_irqrestore(&logbuf_lock,flags);

/*invokedumperwhichwilliterateoverrecords*/
dumper->dump(dumper,reason);

/*resetiterator*/
dumper->active=false;
}
rcu_read_unlock();
}

pstore console 注册

staticstructconsolepstore_console={
.name="pstore",
.write=pstore_console_write,
.flags=CON_PRINTBUFFER|CON_ENABLED|CON_ANYTIME,
.index=-1,
};

staticvoidpstore_register_console(void)
{
register_console(&pstore_console);
}

->write最终也会call backend write:

#ifdefCONFIG_PSTORE_CONSOLE
staticvoidpstore_console_write(structconsole*con,constchar*s,unsignedc)
{
constchar*e=s+c;

while(s< e) {
        unsigned long flags;
        u64 id;

        if (c >psinfo->bufsize)
c=psinfo->bufsize;

if(oops_in_progress){
if(!spin_trylock_irqsave(&psinfo->buf_lock,flags))
break;
}else{
spin_lock_irqsave(&psinfo->buf_lock,flags);
}
memcpy(psinfo->buf,s,c);
psinfo->write(PSTORE_TYPE_CONSOLE,0,&id,0,0,0,c,psinfo);//tj:here
spin_unlock_irqrestore(&psinfo->buf_lock,flags);
s+=c;
c=e-s;
}
}

ramoops

下面来看下RAM backend: ramoops,先看probe:

staticintramoops_probe(structplatform_device*pdev)
{
structdevice*dev=&pdev->dev;
structramoops_platform_data*pdata=dev->platform_data;
...

if(!pdata->mem_size||(!pdata->record_size&&!pdata->console_size&&
!pdata->ftrace_size&&!pdata->pmsg_size)){
pr_err("Thememorysizeandtherecord/consolesizemustbe"
"non-zero
");
gotofail_out;
}
...

cxt->size=pdata->mem_size;
cxt->phys_addr=pdata->mem_address;
cxt->memtype=pdata->mem_type;
cxt->record_size=pdata->record_size;
cxt->console_size=pdata->console_size;
cxt->ftrace_size=pdata->ftrace_size;
cxt->pmsg_size=pdata->pmsg_size;
cxt->dump_oops=pdata->dump_oops;
cxt->ecc_info=pdata->ecc_info;

pdata应该来源ramoops_register_dummy:

staticvoidramoops_register_dummy(void)
{
...
pr_info("usingmoduleparameters
");

dummy_data=kzalloc(sizeof(*dummy_data),GFP_KERNEL);
if(!dummy_data){
pr_info("couldnotallocatepdata
");
return;
}

dummy_data->mem_size=mem_size;
dummy_data->mem_address=mem_address;
dummy_data->mem_type=mem_type;
dummy_data->record_size=record_size;
dummy_data->console_size=ramoops_console_size;
dummy_data->ftrace_size=ramoops_ftrace_size;
dummy_data->pmsg_size=ramoops_pmsg_size;
dummy_data->dump_oops=dump_oops;
/*
*Forbackwardscompatibilityramoops.ecc=1means16bytesECC
*(using1byteforECCisn'tmuchofuseanyway).
*/
dummy_data->ecc_info.ecc_size=ramoops_ecc==1?16:ramoops_ecc;

dummy=platform_device_register_data(NULL,"ramoops",-1,
dummy_data,sizeof(structramoops_platform_data));

有几个可配参数:

/*
*Ramoopsplatformdata
*@mem_sizememorysizeforramoops
*@mem_addressphysicalmemoryaddresstocontainramoops
*/

structramoops_platform_data{
unsignedlongmem_size;
phys_addr_tmem_address;
unsignedintmem_type;
unsignedlongrecord_size;
unsignedlongconsole_size;
unsignedlongftrace_size;
unsignedlongpmsg_size;
intdump_oops;
structpersistent_ram_ecc_infoecc_info;
};

mem_size:用于Ramoops的内存大小,表示分配给Ramoops的物理内存的大小。

mem_address:用于Ramoops的物理内存地址,指定用于存储Ramoops的物理内存的起始地址。

mem_type:内存类型,用于进一步描述内存的属性和特征。

record_size:每个记录的大小

console_size:控制台记录的大小

ftrace_size:Ftrace记录的大小

pmsg_size:pmsg消息记录的大小

dump_oops:是否转储oops信息的标志,表示是否将oops信息转储到Ramoops中。

ecc_info:RAM的ECC(纠错码)信息,用于提供关于ECC配置和处理的详细信息。

有个结构表示了ramoops的context:

structramoops_context{
structpersistent_ram_zone**przs;
structpersistent_ram_zone*cprz;
structpersistent_ram_zone*fprz;
structpersistent_ram_zone*mprz;
phys_addr_tphys_addr;
unsignedlongsize;
unsignedintmemtype;
size_trecord_size;
size_tconsole_size;
size_tftrace_size;
size_tpmsg_size;
intdump_oops;
structpersistent_ram_ecc_infoecc_info;
unsignedintmax_dump_cnt;
unsignedintdump_write_cnt;
/*_read_cntneedclearonramoops_pstore_open*/
unsignedintdump_read_cnt;
unsignedintconsole_read_cnt;
unsignedintftrace_read_cnt;
unsignedintpmsg_read_cnt;
structpstore_infopstore;
};

在ramoops_probe时也是把ramoops_platform_data的成员赋给了context对应的。要了解具体含义,继续probe:

paddr=cxt->phys_addr;

dump_mem_sz=cxt->size-cxt->console_size-cxt->ftrace_size
-cxt->pmsg_size;
err=ramoops_init_przs(dev,cxt,&paddr,dump_mem_sz);
if(err)
gotofail_out;

err=ramoops_init_prz(dev,cxt,&cxt->cprz,&paddr,
cxt->console_size,0);
if(err)
gotofail_init_cprz;

err=ramoops_init_prz(dev,cxt,&cxt->fprz,&paddr,cxt->ftrace_size,
LINUX_VERSION_CODE);
if(err)
gotofail_init_fprz;

err=ramoops_init_prz(dev,cxt,&cxt->mprz,&paddr,cxt->pmsg_size,0);
if(err)
gotofail_init_mprz;

cxt->pstore.data=cxt;

可见,是逐个init每个persistant ram zone,size一共有4段:

dump_mem_sz+cxt->console_size+cxt->ftrace_size+cxt->pmsg_size=cxt->size

mem_size就是总大小了,mem_address是ramoops的物理地址,record_size再看下oops/panic ram:

staticintramoops_init_przs(structdevice*dev,structramoops_context*cxt,
phys_addr_t*paddr,size_tdump_mem_sz)
{
interr=-ENOMEM;
inti;

if(!cxt->record_size)
return0;

if(*paddr+dump_mem_sz-cxt->phys_addr>cxt->size){
dev_err(dev,"noroomfordumps
");
return-ENOMEM;
}

cxt->max_dump_cnt=dump_mem_sz/cxt->record_size;
if(!cxt->max_dump_cnt)
return-ENOMEM;

ok dump_mem_size大小的区域分成max_dump_cnt个,每个记录大小是record_size。

接着会call persistent_ram_new来分配内存给这个ram zone。

for(i=0;i< cxt->max_dump_cnt;i++){
cxt->przs[i]=persistent_ram_new(*paddr,cxt->record_size,0,
&cxt->ecc_info,
cxt->memtype,0);

console/ftrace/pmsg ram zone同上分配。

最后处理flags并注册pstore:

cxt->pstore.flags=PSTORE_FLAGS_DMESG;//tj:默认dumpoops/panic
if(cxt->console_size)
cxt->pstore.flags|=PSTORE_FLAGS_CONSOLE;
if(cxt->ftrace_size)
cxt->pstore.flags|=PSTORE_FLAGS_FTRACE;
if(cxt->pmsg_size)
cxt->pstore.flags|=PSTORE_FLAGS_PMSG;

err=pstore_register(&cxt->pstore);
if(err){
pr_err("registeringwithpstorefailed
");
gotofail_buf;
}

来看下ramoops pstore的定义的callback,他们通过全局psinfo而来:

staticstructramoops_contextoops_cxt={
.pstore={
.owner=THIS_MODULE,
.name="ramoops",
.open=ramoops_pstore_open,
.read=ramoops_pstore_read,//psi->read
.write_buf=ramoops_pstore_write_buf,//fornonpmsg
.write_buf_user=ramoops_pstore_write_buf_user,//forpmsg
.erase=ramoops_pstore_erase,
},
};

pstore使用方法

ramoops

配置内核

CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_RAM=y
CONFIG_PANIC_TIMEOUT=-1

由于log数据存放于DDR,不能掉电,只能依靠自动重启机制来查看,故而要配置:CONFIG_PANIC_TIMEOUT,让系统在 panic 后能自动重启。

dts

ramoops_mem:ramoops_mem{
reg=<0x0 0x110000 0x0 0xf0000>;
reg-names="ramoops_mem";
};

ramoops{
compatible="ramoops";
record-size=<0x0 0x20000>;
console-size=<0x0 0x80000>;
ftrace-size=<0x0 0x00000>;
pmsg-size=<0x0 0x50000>;
memory-region=<&ramoops_mem>;
};

mtdoops

内核配置

CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_MTD_OOPS=y
CONFIG_MAGIC_SYSRQ=y

分区配置

cmdline方式:

bootargs="console=ttyS1,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfsmtdoops.mtddev=pstore";

blkparts="mtdparts=spi0.0:64k(spl)ro,256k(uboot)ro,64k(dtb)ro,128k(pstore),3m(kernel)ro,4m(rootfs)ro,-(data)";

part of方式:

bootargs="console=ttyS1,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfsmtdoops.mtddev=pstore";
partition@60000{
label="pstore";
reg=<0x60000 0x20000>;
};

blkoops

配置内核

CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_BLK=y
CONFIG_MTD_PSTORE=y
CONFIG_MAGIC_SYSRQ=y

配置分区

cmdline方式:

bootargs="console=ttyS1,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfspstore_blk.blkdev=pstore";

blkparts="mtdparts=spi0.0:64k(spl)ro,256k(uboot)ro,64k(dtb)ro,128k(pstore),3m(kernel)ro,4m(rootfs)ro,-(data)";

part of方式:

bootargs="console=ttyS1,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfspstore_blk.blkdev=pstore";
partition@60000{
label="pstore";
reg=<0x60000 0x20000>;
};

pstore fs

挂载pstore文件系统

mount-tpstorepstore/sys/fs/pstore

挂载后,通过mount能看到类似这样的信息:

#mount
pstoreon/sys/fs/pstoretypepstore(rw,relatime)

如果需要验证,可以这样主动触发内核崩溃:

#echoc>/proc/sysrq-trigger

不同配置方式日志名称不同

ramoops

#mount-tpstorepstore/sys/fs/pstore/
#cd/sys/fs/pstore/
#ls
console-ramoops-0dmesg-ramoops-0dmesg-ramoops-1

mtdoops

#cat/dev/mtd3>1.txt
#cat1.txt

blkoops

cd/sys/fs/pstore/
ls
dmesg-pstore_blk-0dmesg-pstore_blk-1

总结

pstore setup 流程:

ramoops_init
ramoops_register_dummy
ramoops_probe
ramoops_register

查看 pstore 数据保存流程:

registerapstore_dumper
//whenpanichappens,kmsg_dumpiscalled
calldumper->dump
pstore_dump

查看 pstore 数据读取流程:

ramoops_probe
persistent_ram_post_init
pstore_register
pstore_get_records
ramoops_pstore_read
pstore_decompress(onlyfordmesg)
pstore_mkfile(savetofiles)



审核编辑:刘清
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RAM
    RAM
    +关注

    关注

    7

    文章

    1322

    浏览量

    113708
  • DDR
    DDR
    +关注

    关注

    9

    文章

    677

    浏览量

    64255
  • ECC
    ECC
    +关注

    关注

    0

    文章

    90

    浏览量

    20376
  • vfs
    vfs
    +关注

    关注

    0

    文章

    13

    浏览量

    5206

原文标题:【调试】pstore原理和使用方法总结

文章出处:【微信号:嵌入式与Linux那些事,微信公众号:嵌入式与Linux那些事】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法总结

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法总结
    发表于 05-14 21:48

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法总结

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法总结
    发表于 05-15 15:29

    转:Keil的使用方法 - 常用功能(一)

    单片机或者嵌入式开发人员的不二选择。 从今天开始的接下来一些时间总结关于Keil的使用方法。计划前一阶段总结常见功能,后面综合性的总结Keil的功能。 关注我的朋友应该知道,我前面已经
    发表于 06-13 10:42

    转:Keil的使用方法 - 常用功能(二)

    Ⅰ、概述 上一篇文章是总结关于Keil使用方法-常用功能(一),关于(文件和编译)工具栏每一个按钮的功能描述和快捷键的使用。我将每一篇Keil使用方法的文章都汇总在一起,回顾前面的总结
    发表于 06-13 10:42

    GPIO的常用库函数使用方法总结

    +GPIOx_LCKR作用和配置STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区各类时钟的实现+各类时钟寄存器总结前言一、STM32的时钟源(一)时钟类型(二)时钟框图(极其重要)前言之前
    发表于 08-23 09:19

    总结一下串口的几种使用方法

    使用方法对以后的开发还是很有帮助的。有关串口的知识我在之前的博客中有介绍:点击链接跳转一.仅向上位机打印调试信息单纯利用串口向上位机打印调试信息,程序如下:void USART1_Init( uin...
    发表于 02-10 08:03

    介绍SPI的使用方法

    ,这篇介绍SPI的使用方法,流程与TIM类似。大致总结为以下几个步骤:在RT-thread settings中使能对应的驱动框架在stm32f4xx_hal_conf.h中使能对应的模块(HAL_XX_MODULE_ENABLED)在board.h中启用外设类型对应的宏定
    发表于 02-17 06:32

    示波器的使用方法

    数字万用表使用方法和示波器的使用方法详解。
    发表于 03-14 10:38 31次下载

    对于无线对讲机日常使用的常见故障及正确的使用方法总结

    总结一些日常使用中最常见的故障及正确的使用方法作一些简单介绍,供对讲机用户参考。
    的头像 发表于 12-25 08:41 6331次阅读

    示波器的使用方法(三):示波器的使用方法详解

    示波器的使用方法并非很难,重点在于正确使用示波器的使用方法。往期文章中,小编对模拟示波器的使用方法和数字示波器的使用方法均有所介绍。为增进大家对示波器的
    的头像 发表于 12-24 20:37 2481次阅读

    DWIN屏使用方法总结(上)

    DWIN屏使用方法总结(上)DWIN屏使用方法总结(上)DWIN屏介绍开发工具ICL生成CFG修改工程建立与下载工程建立文件下载总结DWIN
    发表于 12-02 14:21 12次下载
    DWIN屏<b class='flag-5'>使用方法</b><b class='flag-5'>总结</b>(上)

    DWIN屏使用方法总结(下)

    DWIN屏使用方法总结(下)DWIN屏使用方法总结(下)数据帧常用的系统指令常用控件基础触控按键返回数据变量录入图标变量数据变量显示总结DW
    发表于 12-31 18:56 10次下载
    DWIN屏<b class='flag-5'>使用方法</b><b class='flag-5'>总结</b>(下)

    新型存储技术:新型SCM类介质的特性及使用方法总结和介绍

    Storage Class Memory (SCM)是非易失性内存,该类介质的存取速度略比内存慢,但是远快于NAND类介质。本文对该类介质的特性及使用方法做了简单总结和介绍。
    发表于 01-15 15:07 1500次阅读

    使用电容器降低噪声-去耦电容的有效使用方法总结

    前面分三次对“去耦电容的有效使用方法”进行了介绍。利用电容来降低噪声是非常重要的,所以在这里总结一下。
    发表于 02-15 16:12 545次阅读

    内网穿透工具的种类、原理和使用方法

    本文以渗透的视角,总结几种个人常用的内网穿透,内网代理工具,介绍其简单原理和使用方法
    的头像 发表于 08-25 10:35 384次阅读
    内网穿透工具的种类、原理和<b class='flag-5'>使用方法</b>