将模块代码量精简为2%的实践

说明


本文通过目录和代码两个层面分析某产品xDsl驱动模块代码,将其精简为原始代码量的2%。

一  完整代码


某产品xDsl驱动模块目录结构如下所示。其中,二级目录Lxx1通常为芯片厂家代码,Lxx2为自定义适配代码。

├─L010

│  ├─include

│  └─source

├─L020

│  ├─include

│  ├─source

…… ……

├─L200

│  ├─L210

…… ……

│  ├─L260

│  │  ├─L261

│  │  │  ├─include

│  │  │  └─source

│  │  └─L262

│  │      ├─include

│  │      └─source

…… ……

│  └─SELT_DELT

│      ├─include

│      └─source

该产品xDsl模块存在大量已废弃的目录,而在用的目录中也存在大量无用的代码。

二  精简代码


此处“精简”意指直接剔除无用代码,而非借助重构等手段来削减代码量。精简主要分为两个步骤:1.
分析makefile文件,清理不予编译的目录;2. 分析预编译宏,删除当前在用目录中无用的代码。

2.1 清理废弃目录

   
 
lnxmkdep.ini文件中定义各编译目录(待编译代码所在目录),xDSL驱动目前用于编译的代码目录如下:

 1 [DSLADDR]
2 USE=YES
3 PATH=../../../xDSL/L030
4 COPT_EXTRA=
5 DEPEND_MODULES= all
6
7
8 [L261]
9 USE=YES
10 PATH=../../../xDSL//L200/L260/L261
11 COPT_EXTRA=
12 DEPEND_MODULES= all
13
14 [L262]
15 USE=YES
16 PATH=../../../xDSL//L200/L260/L262
17 COPT_EXTRA=
18 DEPEND_MODULES= all
19
20 [L271]
21 USE=YES
22 PATH=../../../xDSL//L200/L270/L271
23 COPT_EXTRA=
24 DEPEND_MODULES= all
25
26 [L272]
27 USE=YES
28 PATH=../../../xDSL//L200/L270/L272
29 COPT_EXTRA=
30 DEPEND_MODULES= all
31
32 [L290]
33 USE=YES
34 PATH=../../../xDSL//L200/L290
35 COPT_EXTRA=
36 DEPEND_MODULES= all
37 COPT_EXTRA=
38 DEPEND_MODULES= all
39
40 [SELTDELT]
41 USE=YES
42 PATH=../../../xDSL/L200/SELT_DELT
43 COPT_EXTRA=-Os
44 DEPEND_MODULES= all

Listed Directories

结合头文件包含情况,可知编译需要L010、L030、L200(L210、L230、L260、L270、L290、SELT_DELT)目录。进一步分析得知,仅用到L210和L230目录的若干头文件,而L260目录存在同名头文件且版本更新,故可替代前两个目录。同时,L270目录用于提供30A功能,而当前产品不需要支持该功能,故可删除。

此时,在用的目录已精简为L010、L030、L200(L260、L290、SELT_DELT)。

2.2 删除无用代码

xDsl驱动代码中包含对于其他各种单板和功能的支持,而多数单板已不再使用,某些功能也并未使用。这些支持在代码中主要通过预处理宏(即#if、#ifdef、#ifndef、#elif等含"#+if"关键字的条件编译语句,统称为#if语句)来控制,例如fsap_prj.h文件定义功能宏(INSTALL_MAP_BONDING等),config.mak文件定义编译宏(_INSTALL_VSLC等),其他宏定义则分散于xDsl目录下各文件中。

删除无用代码,即寻找哪些控制编译的宏未定义,并将其控制的代码删除。但鉴于宏定义的分散性,人工查找和删除条件编译分支显然不现实,必须借助自动化工具。

通过工具剔除未使用的条件编译分支,其原理如下:

1.
在待处理代码(*.c、*.h)中#if语句句首插入gcc扩展的预编译头#warning。

2.
编译待处理代码获取gcc编译输出并进行分析。

3.
编译结果中”#warning”警告所对应的#if语句为TRUE,即所控制的代码段正在使用,应予保留;反之可删除。

开源工具stripcc可较好地完成上述工作。在小工程上试用效果符合期望,但应用到本产品时似乎出现死锁,无法正常工作。该问题在研读和调试其源代码后仍未解决。

以下将基于相同工作原理,借助Python脚本处理,分析预编译宏,标记在用的#if代码段。处理后的源代码示例如下(剔除/*TRUE*/
标记后即为原始代码):

 1 #define BCM_BONDING_ENABLED
2 #define BCM_ENABLED
3
4 /*TRUE*/ #ifdef BCM_BONDING_ENABLED
5 CodeLine1;
6 #endif
7
8 #ifdef BCM_DISABLED
9 CodeLine2;
10 /*TRUE*/ #elif defined BCM_ENABLED
11 CodeLine3;
12 #else
13 #error Defination of BCM_DISABLED or BCM_ENABLED is Required!
14 #endif
15
16 #ifdef BCM_TEST
17 CodeLine4;
18 /*TRUE*/ #else
19 CodeLine5;
20 #endif

Processed Code

其中,若#if句前出现:

  • 一个/*TRUE*/:表示该#if句为逻辑真;

  • 多个/*TRUE*/:多出现于被广泛引用的头文件内,每次引用对为真的#if处增加一个/*TRUE*/;

  • 没有/*TRUE*/:表示该#if句为逻辑假。

注意,若某文件“期望”出现/*TRUE*/ 标记(如#ifndef
<头文件宏>)但未出现,则该文件很可能并未编译——可用于甄别无用的文件。

【脚本示例】文件布局如下:

其中,AddWarnsEx.py对源代码添加预编译头#warning。

 1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 import os, re
5
6
7 CodeDirName = r"E:\ValidMacroExample"
8
9 def AddWarnToFile(strPath):
10 OrigFd = open(strPath)
11 BackFd = open(strPath+"b", ‘w+‘)
12
13 OrigLineNo = 0;
14 while(1):
15 CurCodeLine = OrigFd.readline()
16 OrigLineNo = OrigLineNo + 1
17 if(len(CurCodeLine) == 0):
18 break;
19
20 BackFd.write(CurCodeLine)
21 MacthRes = re.compile(".*#\s*(el|if).*").match(CurCodeLine)
22 if MacthRes != None:
23 InsertCodeLine = "#warning Reach code " + ‘<File:‘+strPath +‘>‘+ ‘<Line:‘+str(OrigLineNo)+‘>‘ + "\n";
24 BackFd.write(InsertCodeLine)
25
26 OrigFd.close()
27 BackFd.close()
28 return
29
30
31 def SwapFileStatus(strPath):
32 os.rename(strPath, strPath+‘t‘)
33 os.rename(strPath+‘b‘, strPath)
34 os.rename(strPath+‘t‘, strPath+‘b‘)
35 return
36
37
38 def DirTravel(DirPath):
39
40 #遍历目录中的文件
41 if os.path.isdir(DirPath) == True:
42 FileList = os.listdir(DirPath)
43 else:
44 FileList = [os.path.basename(DirPath)]
45
46 if FileList != []:
47 for File in FileList:
48 #检查目录名或文件名
49 if os.path.isdir(DirPath) == True:
50 FilePath = DirPath + os.sep + File
51 else:
52 FilePath = DirPath
53
54 #文件类型为目录,递归
55 if os.path.isdir(FilePath) == True:
56 DirTravel(FilePath)
57 continue
58
59 #识别C文件和H文件
60 SplitList = File.split(‘.‘)
61 #忽略无后缀名的文件
62 if len(SplitList) < 2:
63 continue
64 FileType = SplitList[-1]
65 if FileType == ‘c‘ or FileType == ‘h‘:
66 AddWarnToFile(FilePath)
67 SwapFileStatus(FilePath)
68 return
69
70
71 DirTravel(CodeDirName)

AddWarnsEx

ChkMacrosEx.py分析编译结果并标记为真的#if语句。

 1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4
5 import os, re
6
7 CodeDirName = r"E:\ValidMacroExample"
8 WarnFileName = CodeDirName + r"\Warns.txt"
9
10
11 def RestoreFileStatus(strPath):
12 os.remove(strPath)
13 os.rename(strPath+‘b‘, strPath)
14 return
15
16
17 def ValidMacroInFile():
18 Fd = open(WarnFileName, ‘r‘)
19
20 while(1):
21 CurCodeLine = Fd.readline()
22 if(len(CurCodeLine) == 0):
23 break;
24
25 MacthRes = re.compile(".*#warning.*<File:(.*)><Line:(\d+)>").match(CurCodeLine)
26 if MacthRes != None:
27 #根据编译警告信息打开相应的源文件(MacthRes.group(1)),修改相应行(MacthRes.group(2))
28 #全文读入,修改一行,全文写入。同一文件内多行#warning时,效率较低
29 SrcFd = open(MacthRes.group(1), ‘r‘)
30 FileLines = SrcFd.readlines()
31 ModLineNo = int(MacthRes.group(2))-1
32 FileLines[ModLineNo] = "/*TRUE*/ " + FileLines[ModLineNo]
33 SrcFd.close()
34
35 SrcFd = open(MacthRes.group(1), ‘w‘)
36 SrcFd.writelines(FileLines)
37 SrcFd.close()
38
39 Fd.close()
40 return
41
42
43 def RemoveBackFile(DirPath):
44
45 #遍历目录中的文件
46 if os.path.isdir(DirPath) == True:
47 FileList = os.listdir(DirPath)
48 else:
49 FileList = [os.path.basename(DirPath)]
50
51 if FileList != []:
52 for File in FileList:
53 #检查目录名或文件名
54 if os.path.isdir(DirPath) == True:
55 FilePath = DirPath + os.sep + File
56 else:
57 FilePath = DirPath
58
59 #文件类型为目录,递归
60 if os.path.isdir(FilePath) == True:
61 RemoveBackFile(FilePath)
62 continue
63
64 #识别C文件和H文件
65 SplitList = File.split(‘.‘)
66 #忽略无后缀名的文件
67 if len(SplitList) < 2:
68 continue
69 FileType = SplitList[-1]
70 if FileType == ‘c‘ or FileType == ‘h‘:
71 RestoreFileStatus(FilePath)
72 #os.remove(FilePath)
73 return
74
75
76 RemoveBackFile(CodeDirName)
77 ValidMacroInFile()

ChkMacrosEx

Macro.c等为待处理的C源文件。

 1 //Macro.c(dir1)
2 #define BCM_BONDING_ENABLED
3 #define BCM_ENABLED
4
5 #ifdef BCM_BONDING_ENABLED
6 CodeLine1;
7 #endif
8
9 #ifdef BCM_DISABLED
10 CodeLine2;
11 #elif defined BCM_ENABLED
12 CodeLine3;
13 #else
14 #error Defination of BCM_DISABLED or BCM_ENABLED is Required!
15 #endif
16
17 #ifdef BCM_TEST
18 CodeLine4;
19 #else
20 CodeLine5;
21 #endif
22
23
24 //Macro1.c(dir2)
25 #define BCM_BONDING_ENABLED
26 #define BCM_ENABLED
27
28 #ifdef BCM_BONDING_ENABLED
29 CodeLine4;
30 #endif
31
32 #ifdef BCM_ENABLED
33 CodeLine8;
34 #endif
35
36
37 //Macro2.c(dir2)
38 #define BCM_ENABLED
39
40 #ifdef BCM_VECTOR_ENABLED
41 CodeLine4;
42 #endif
43
44 #ifdef BCM_ENABLED
45 CodeLine8;
46 #endif

Macros

Warns.txt为编译结果(暂以模拟内容代替)。

1 #warning Reach code <File:E:\ValidMacroExample\dir1\Macro.c><Line:4>
2 #warning Reach code <File:E:\ValidMacroExample\dir1\Macro.c><Line:10>
3 #warning Reach code <File:E:\ValidMacroExample\dir1\Macro.c><Line:18>
4 #warning Reach code <File:E:\ValidMacroExample\dir2\Macro1.c><Line:4>
5 #warning Reach code <File:E:\ValidMacroExample\dir2\Macro1.c><Line:8>
6 #warning Reach code <File:E:\ValidMacroExample\dir2\Macro2.c><Line:7>

Warns

根据实际情况调整代码路径(当前为E:\ValidMacroExample)后,按如下步骤运行:

1.
执行AddWarnsEx.py,生成添加#warning后的代码文件f.c(h)及其备份f.cb(hb)。

2. 编译处理后的代码文件f.c(h),将编译结果重定向到Warns.txt内。

3. 执行ChkMacrosEx.py,生成添加/*TRUE*/的代码文件,并自动删除备份文件。

将Python脚本内待处理代码路径修改为xDsl模块路径后,即可用于实际工程代码的精简。经过处理的实际代码片段截图如下:

更进一步,可分析处理后的/*TRUE*/标记,自动删除未编译的代码段,但需要严密的语法分析。此外,目前的脚本实现未考虑执行效率。因时间精力有限,暂时不予改进。

三  效果评价


清理目录和代码后,比较完整代码(Full)和精简代码(Lite)的规模如下:














版本

代码量()

系数

Full

9,024,746

1

Lite

221,964

0.0246

可见,Lite代码行数约为Full代码的2%(考虑到BCM芯片SDK后续可能更新,为便于同步相应代码未做精简)。编译后经验证,可正常配置和查询。

将模块代码量精简为2%的实践,布布扣,bubuko.com

时间: 2024-10-13 06:14:44

将模块代码量精简为2%的实践的相关文章

代码量简单统计

代码量简单统计 def count_code(file_path): # 单文件代码统计 count=0 tag=False with open(file_path, 'r', encoding='utf-8') as f: for line in f: if tag and line.startswith('"""') or line.startswith("'''"): tag=False if tag and not line.startswith(

Excel VBA在生成副本的工作表中插入本工作簿中的VBA模块代码

即在工作簿中添加一个工作表,然后移出并存为新的工作簿,在移出前将本工作簿的一个模块的代码拷贝至新的工作簿.下面是关键代码: '====================================================================== '各班名单保存为单个xls文件 ActiveSheet.Move ChDir myPath '忽略对话框,覆盖保存 Application.DisplayAlerts = False '班级名称增加"考生号处理"vba模块

git代码量统计(转载)

git统计某人代码量 指定用户名版 git log --author="your_name_here" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' 结果示例:added lines:

Verilog HDL基础语法讲解之模块代码基本结构

Verilog HDL基础语法讲解之模块代码基本结构 ? 本章主要讲解Verilog基础语法的内容,文章以一个最简单的例子"二选一多路器"来引入一个最简单的Verilog设计文件的基本结构. 以下为本章中例子中的代码: 01????/*======================================= 02????*????file neme : mux2.v 03????*????author????:????小梅哥 04????*????Verison????:????

(八)简单了解下angularJS框架中NB的双向数据绑定机制,大大减少需要重复的开发代码量

之前写的第一篇angularJS入门文章 ,介绍ng-model的时候提到:使用angularJS的双向数据绑定机制,不需要我们编写繁琐的代码来实现同样的功能.现在我们看一个比较震撼的例子,看看angularJS是如何减少我们在前端开发中的繁琐劳动的.越是感受到框架功能的强大,越是能够激发学习的兴趣和动力. 假如我们有一个学生信息列表,包含学生的姓名.地址和年龄信息.假如这个数据源信息保存在data.js文件中. var g_phones = [ <span style="white-sp

MDU某产品OMCI模块代码质量现状分析

说明 本文参考MDU系列某产品OMCI模块现有代码,提取若干实例以说明目前的代码质量. 本文旨在就事论事,而非否定前人(没有前人的努力也难有后人的进步).希望以史为鉴,不破不立,最终产出高质量的代码. 一  质量现状 不考虑业务实现,现有的OMCI模块代码质量不甚理想.无论是理解上手.修改扩展和测试排障,可以用举步维艰形容.尤其是二层通道计算相关代码,堪比令史前动物无法自拔的"焦油坑". 本节将不考虑流程设计,仅就函数粒度列举目前存在的较为突出的代码质量问题. 1.1 巨型函数 通过S

如何查看整个项目工程代码量

对于申请双软认证,iso质量管理认证等,需要统计项目工程的代码量. 估计的话,也能大概估个数,但是不是很准,如何才能比较准确的评估出整个项目工程的代码量呢? 下面推荐一个爆款,可以根据自己设定要统计的项,来统计代码. SourceCounter 废话不多说,直接上图 结果出炉了 爽不爽!

统计代码量的小程序

比较简陋的统计代码的小工具,  根据自己的需求改改吧. import java.awt.EventQueue; import java.awt.Font; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOExcept

通过Pojo对象 field 属性加注解实现格式校验,极大的减少代码量

最近做一个接口,接受外系统的报文,通过XStream转换成java对象以后,需要对其中的字段做格式校验.要求如下 传统的方式是硬编码校验,但是对于field很多的情况,代码量暴增.容易出错. String storeCode = uHeader.getStoreCode(); if (StringUtils.isNotBlank(storeCode)) { ParamsUtil.getInstance().checkStrParam(result, storeCode, "抬头-参考订单门店号[