在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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_lenclose)
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(spsinfo->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;imax_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
    +關注

    關注

    8

    文章

    1369

    瀏覽量

    115029
  • DDR
    DDR
    +關注

    關注

    11

    文章

    715

    瀏覽量

    65536
  • ECC
    ECC
    +關注

    關注

    0

    文章

    97

    瀏覽量

    20644
  • vfs
    vfs
    +關注

    關注

    0

    文章

    14

    瀏覽量

    5283

原文標題:【調試】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 ?32次下載

    對于無線對講機日常使用的常見故障及正確的使用方法總結

    總結一些日常使用中最常見的故障及正確的使用方法作一些簡單介紹,供對講機用戶參考。
    的頭像 發表于 12-25 08:41 ?7090次閱讀

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

    示波器的使用方法并非很難,重點在于正確使用示波器的使用方法。往期文章中,小編對模擬示波器的使用方法和數字示波器的使用方法均有所介紹。為增進大家對示波器的
    的頭像 發表于 12-24 20:37 ?3393次閱讀

    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 ?2354次閱讀

    使用電容器降低噪聲-去耦電容的有效使用方法總結

    前面分三次對“去耦電容的有效使用方法”進行了介紹。利用電容來降低噪聲是非常重要的,所以在這里總結一下。
    發表于 02-15 16:12 ?959次閱讀

    內網穿透工具的種類、原理和使用方法

    本文以滲透的視角,總結幾種個人常用的內網穿透,內網代理工具,介紹其簡單原理和使用方法。
    的頭像 發表于 08-25 10:35 ?2149次閱讀
    內網穿透工具的種類、原理和<b class='flag-5'>使用方法</b>
    主站蜘蛛池模板: 色五月婷婷成人网 | 伊人久久大香线蕉资源 | 天天爽夜夜爽免费看 | 日本大片成人免费播放 | 爱婷婷网站在线观看 | 视色4se在线视频播放 | 久久精品国产精品亚洲精品 | 全部免费特黄特色大片农村 | 午夜欧美福利视频 | 九九99视频在线观看视频观看 | 91福利国产在线观看网站 | 最新激情网 | 一级aaaaaa片毛片在线播放 | 2021国产精品久久 | 国产三级网 | 午夜在线播放 | 美女天天干 | 男男污肉高h坐便器调教 | 奇米影视亚洲四色8888 | se01亚洲 | 国产成人亚洲综合a∨婷婷 国产成人一区二区三中文 国产成人一区二区在线不卡 | 美女三级网站 | 夜夜春宵翁熄性放纵古代 | 天堂中文资源在线观看 | 久久都是精品 | 99久久精品费精品国产一区二区 | 午夜在线看片 | 热久热 | 在线播放免费视频 | 亚洲黄色三级网站 | 亚洲欧美综合一区二区三区四区 | 最新免费jlzzjlzz在线播放 | 一区二区三区免费视频网站 | 久久久噜久噜久久综合 | 国模私拍视频 | 亚洲午夜顶级嘿嘿嘿影院 | 国产午夜精品福利久久 | 日本美女中出 | 国内一级毛片 | 久久国产精品免费网站 | 在线免费观看毛片网站 |