zoukankan      html  css  js  c++  java
  • 将模块代码量精简为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)+'>' + "
    ";
    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:ValidMacroExampledir1Macro.c><Line:4>
    2 #warning Reach code <File:E:ValidMacroExampledir1Macro.c><Line:10>
    3 #warning Reach code <File:E:ValidMacroExampledir1Macro.c><Line:18>
    4 #warning Reach code <File:E:ValidMacroExampledir2Macro1.c><Line:4>
    5 #warning Reach code <File:E:ValidMacroExampledir2Macro1.c><Line:8>
    6 #warning Reach code <File:E:ValidMacroExampledir2Macro2.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后续可能更新,为便于同步相应代码未做精简)。编译后经验证,可正常配置和查询。

  • 相关阅读:
    CaseStudy(showcase)布局篇列表的排放与遮罩
    CaseStudy(showcase)布局篇全屏效果
    Css 学习
    JavaScript 学习之 修改对象创建新方法
    等额本息java实现
    纠结了一天的JAVA简单客户端服务器Socket编程终于解决了
    rhostudio备忘
    sql server学习
    enumeration学习
    cookie和session
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3757194.html
Copyright © 2011-2022 走看看