姐姐教你写脚本解析Map文件_第1页
姐姐教你写脚本解析Map文件_第2页
姐姐教你写脚本解析Map文件_第3页
姐姐教你写脚本解析Map文件_第4页
姐姐教你写脚本解析Map文件_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

姐姐教你写脚本解析Map文件什么问题小李,你算一下这个项目每个模块的资源使用情况,我明天早上要向客户汇报。你老板这句话让你慌乱而不知所措?别着急,其实可以从程序编译链接后生成的Map文件中提取相关数据。本文,姐教你一步步解析Map文件,给老板一个漂亮的报告。怎么统计这些资源呢?上次,我讲了嵌入式程序员为什么要学脚本(见《“嵌入式开发学脚本干嘛”之进制/Byte/Hex处理》),我们这次也用Python脚本来解析Map文件并生成图表。本文会用到并简要讲解以下Python相关知识:Python文件操作Python字符串处理Python的正则表达式使用csv文件操作Matplotlib图表生成还没学会也没关系,只要把Python和相关库安装好,操起家伙直接实战吧。什么是Map文件首先要搞清楚什么是Map文件。简单粗暴地理解:Map文件就是MCU程序的Map(地图)。我们编写好的代码,通过编译链接一系列动作后,会生成一个elf格式的文件,IDE同时会从这个elf文件生成一个hex/s19或者bin文件,以供烧录到MCU里面去运行,同时(可配置地)生成一个叫Map的文件。这个elf文件实际上就包含了程序的各种信息,包括函数名、变量名,地址、大小等等非常丰富的信息。而这个map文件就是从elf提出取来的,非常直观地(以文本形式)展现程序中的各文件包含了哪些函数变量,而这些函数变量分配在哪些段,地址是什么,以及其占用的空间大小等。那么,我们是可以从这个map文件(当然也可以从elf文件)中提取很多有用的信息的哦。以下,我以ghs编译器生成的Map文件(注意:每种IDE生成出来的Map文件格式可能会有点不一样)为例,讲解如何提取各文件或者模块占有的RAM/ROM资源。内容提取和解析的大概的过程如下:如何解析Map>>Map里面的基本信息为了高效精准地讲解,以下截取了Map中部分内容,也是我们要解析的重点内容。ImageSummarySection

Base

Size(hex)

Size(dec)SecOvect

0001000000000174

372

00102c0.except_table

0001020000000598

1432

0010600.rozdata

0001079800000000

0

0000000.robase

0001079800000000

0

0000000.rosdata

0001079800000000

0

0000000.rodata

000107980002678c

157580

0010b98.text

00036f300008eb9c

584604

0037330....data

febd000000002890

10384

00c5fe8.bss

febd289000014080

82048

000000....debug_info

0000000000fc511c

16535836

00eb9e4.debug_abbrev

0000000000013789

79753

10b0b00.debug_line

00000000000773b5

488373

10c4289.debug_macinfo

0000000000c86d01

13135105

113b63e.debug_frame

0000000000029780

169856

1dc233fImageSummary下面的内容存放的是数据段的汇总大小,你看第一行SectionBaseSize(hex)Size(dec)SecOffs就是这些内容的标题说明。例如,这个.text后面的数据就是这个.text段(一般来说是程序静态代码的存放段)的起始地址和大小,00036f30是baseaddress,即存放起始位置,而0008eb9c是十六进制形式的size,即段所占大小。而后面的584604是十进制的大小,跟前面的size等价,我们可以不用管它,SecOffs也暂时不管它。实际上,这个ImageSummary包含了三类信息:ROM字段数据;RAM字段数据;Debug数据。以上,只有ROM和RAM的数据才是我们要关心的。Module

SummaryOrigin+Size

Section

Module00036f30+000124

.text

crt0.o000370ba+000032

.text

main.o000107f8+00001c

.rodata

main.o00000034+000018

.ghpepatch

main.o00001818+00454b

.debug_info

main.o0000018b+0001a1

.debug_abbrev

main.o00000145+0002c2

.debug_line

main.o0000157e+0072e5

.debug_macinfo

main.o000000d0+0000b0

.debug_frame

main.o000370ec+0002d4

.text

task.ofebd28c8+000030

.bss

task.o00010814+0001a4

.rodata

task.o0000004c+00004c

.ghpepatch

task.o00005d63+00831d

.debug_info

task.o0000032c+0001d3

.debug_abbrev

task.o00000407+000516

.debug_line

task.o00008863+00903c

.debug_macinfo

task.o00000180+000100

.debug_frame

task.o...ModuleSummary是module里面的段的数据,这个module在本文说明的map文件中是指文件,例如task.o即task.c编译后的object文件名。从上面的片段可以看出,一个文件包含了各个段信息,包括其占用的ROM和RAM的地址和大小。我们只需提取出这个文件中ROM和RAM占用的大小数据即可。>>Map文件操作既然Map文件没有固定的形式,那就直接当做文本操作吧。Python的文件操作实际上跟C/C++的类似,但是语法上会巧妙点。mf=open('mapfile.map',mode='r')#...mf.close()这个open函数有很多个参数,大部分都有默认值,常用的只有两三个:open(file,

mode='r',

buffering=-1,

encoding=None,

errors=None,

newline=None,

closefd=True,

opener=None)file:必需,文件路径(相对或者绝对路径)。mode:可选,文件打开模式,默认为t,即文本方式。例如mode='rb',表示binaryread形式buffering:设置缓冲encoding:一般使用utf8errors:报错级别newline:区分换行符closefd:传入的file参数类型...另外,我们可以用with语句配合这个文件操作使用,这样可以省掉一个close操作,因为with在使用完文件操作后,自动调用close方法。withopen('mapfile.map',mode='r')asmf:#...说到这,也许你会想,要是能一行一行处理多好,试试:with

open('./mapfile.map',

'r')

as

mf:for

l

in

mf.readlines():print(l)一行一行的,就是字符串了,下面讲字符串的解析。>>Map字符串解析Python的字符串操作方法是非常丰富的。但是,我们要搞清楚我们这次实战的目的,有目的性地是使用字符串操作方法:首先,一行字符串中有哪些信息?你想从字符串中得到什么内容?用什么方法提取你想要的内容?通过以下字符串内容举例:“.text00036f300008eb9c5846040037330“我想获取“.text”这个名称,还想获得它占用的空间大小“0008eb9c”丢弃其他内容。好像很复杂的样子,别怕,有姐在,姐教你,嘻嘻……O(∩_∩)O。此时此刻,“正则表达式”就呼之欲出了。什么?正则表达式是什么东东?如果你没接触过,我劝你不要对着这个名称苦思冥想,会走火入魔的,我以前就老是想为什么这些东西叫正则表达式,好像是很高深的学问,想多了。不知道正则表达式,字符的通配符总听说过吧,例如型号*代表一个字符,不管是什么字符,AB*,可以表示ABC,也可以表示ABD等等。而这个正则表达式,有其一套完整的规则来“通配”某一类型的字符,举个例子,\d表示一个数字,\d+表示一串数字。我们不在这里讲正则表达式的原理内容了,有兴趣可以网上搜“正则表达式”。我们这个例子会用到以下内容,会简单讲解其用法:字符描述^匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配^字符本身,请使用\^。$匹配输入字符串的结尾位置。如果设置了RegExp对象的Multiline属性,则$也匹配'\n'或'\r'。要匹配$字符本身,请使用\$。()标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用\(和\)。*匹配前面的子表达式零次或多次。要匹配*字符,请使用\*。+匹配前面的子表达式一次或多次。要匹配+字符,请使用\+。.匹配除换行符\n之外的任何单字符。要匹配.,请使用\.。?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配?字符,请使用\?。\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。注意Unicode正则表达式会匹配全角空格符。[标记一个中括号表达式的开始。要匹配[,请使用\[。那么,直接实战,来个例子:“.text00036f300008eb9c5846040037330“可以用以下正则表达式匹配'^\s(.[.\w]+)\s+\w+\s+(\w+).$'试下importres='.text

00036f300008eb9c

584604

0037330“c=pile('^\s*(\.[\.\w]+)\s+\w+\s+(\w+).*$')m=c.match(s)ifm:print(m.groups())得到('.text',

'0008eb9c')说明成功了。那么,我们改进下,把段的大小字符串转成整型备用,并将段名和大小信息存在数组中:importrewithopen('./mapfile.map','r')asmf:section_info=[]forlinmf.readlines():c=pile('^\s*(\.[\.\w]+)\s+\w+\s+(\w+).*$')m=c.match(l)ifm:section_info.append([m.group(1),int(m.group(2),16)])#print(m.groups())print(section_info)同理,Map文件中的“ModuleSummary”也可以通过这样的方法解析。import

rewith

open('./mapfile.map',

'r')

as

mf:section_info

=

[]module_info

=

[]image_start

=

Falsemodule_start

=

Falsefor

l

in

mf.readlines():if

'Image

Summary'

in

l:image_start

=

Trueelif

'Module

Summary'

in

l:image_start

=

Falsemodule_start

=

Trueelse:if

image_start:#

.text

00036f30

0008eb9c

584604

0037330c

=

pile('^\s*(\.[\.\w]+)\s+\w+\s+(\w+).*$')m

=

c.match(l)if

m:section_info.append([m.group(1),

int(m.group(2),

16)])elif

module_start:#

000370ba+000032

.text

main.oc

=

pile('^\w+.*(\w+)\s+([\.\w]+)\s+([\w\.]+).*$')m

=

c.match(l)if

m:if

not

'.debug'

in

m.group(2):module_info.append([m.group(3),

m.group(2),

int(m.group(1),

16)])print(section_info)print(module_info)注意,在解析“ModuleSummary”时,我们把“.debug”相关信息丢弃了,这个信息对统计MCU内存资源没影响。讲到这里,基本需要的信息都提取完了,当然总不能甩给你老板两个数组,下面还有数据处理操作。以下顺便简单解释下这个re的使用(学过或者不感兴趣的同学可以跳过)。re即RegEx,RegularExpression的缩写,库的名称。re有很多很好用的方法,我这里只用了其中几个。'^\w+.*(\w+)\s+([\.\w]+)\s+([\w\.]+).*$'这一串就是所谓的Pattern,里面的每个符合表达可以查看对照上表的解释说明。compile()函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()等函数使用。match()函数,尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。而这个group的意思是:group(num=0),匹配的整个表达式的字符串,group()可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。groups(),返回一个包含所有小组字符串的元组,从1到所含的小组号。输出解析结果基于上一小节,我们已经提取出段(即RAM或ROM的段总体)的信息了,也获得了每个文件所含的段的相关数据。接下来要做的:RAM和ROM各含有什么段名称;每个.o文件应该归于哪一个Module;对于第一点,这个需要查看具体项目的链接文件配置,如GHS工程的ld文件,另外可以看IC手册RAM和ROM的地址范围来区分。至于第二点,跟实际项目应用有关。例如,key_io.c、key_ad.c等可以归类为keyboard模块等。这些归类的操作跟实际项目关系非常紧密,也不难,这里也不多说了。我们直接来看要输出什么样的结果吧。输出表格,如Excel表格等。我看到过,有好多人直接用Excel表格来统计资源的,大多是将原始Map文件内容复制或导入到Excel中,然后格式化其里面的内容,即将数据段里面的地址、大小等信息分别填到单独的单元格,然后通过公式统计出结果。也有部分人通过Excel的脚本vb来解析Map文件内容。当然,你也可以将我们上一小节提取的结果(两个数组信息)复制到Excel中,然后再统计。其实,都做到这一步的,没必要手动复制来复制去了,直接导出Excel表格内容不就行了。有两种办法,一是用pandas库,二是输出csv文件。这里讲第二种。输出csv非常简单,其实际上也是文件操

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论