stripcc
去除C源代码中未使用的条件编译分枝
1,stripcc能干什么: 使用C语言编写的软件为了适应各种不同的平台或要求,大多会使用条件编译,这在提高了软件适应性的同时却降低了代码的可读性,比如说有如下代码片断(摘自stunnel-4.20):
阅读该源码,你认为它的可读性怎样?你是否能很容易的从诸多条件编译分枝中找出对应自己特定平台的部分?下面再附上该代码在linux平台(Fedora Core 3)的实际有效代码(使用stripcc处理得到):
再次阅读,是不是感觉清楚多了? 其他常见的情况还包括针对不同平台同一函数会有不同实现,这样当你使用ctags或者SourceInSight等工具来分析代码时也很难一下找到真正的目标,这在linux kernel等支持多平台的源代码中非常多见,比如kernel中arch目录下的代码。 stripcc正是为了解决上述问题,简单的说, stripcc的用途就是为了提高C源代码的可读性而去除其中未被编译的条件编译分枝,但保留正常的注释以及源码风格。 2,stripcc是怎么工作的: stripcc的工作原理很容易理解: 1,在要处理的代码(*.c/*.h)中合适的位置插入gcc扩展的预编译头#warning。 2,进而编译整个项目获取gcc编译输出并进行分析。 3,根据分析结果确定某段代码是否使用,使用则保留,未使用则去除。 这种工作方式相当于手动用#warning来确定某个条件编译分枝(或者整个文件)是否有效,原理上讲是相当可靠的,不会把你的有效代码删除。但 要求你的代码在使用stripcc处理之前必须能使用某种方式调用gcc成功编译。 3,如何使用stripcc: 首先是下载stripcc源码并编译,stripcc使用GPL方式发布,你可以从http://sourceforge.net/projects/stripcc处下载源码文件,获取源码后解压 、make、make install 完成编译安装。安装目录在/usr/local/bin下,可以选择自己拷贝或者修改Makefile以便放入其他位置,但请确认stripcc所在目录在你的执行目录$PATH里。 编译安装完成后就是利用stripcc处理你的源文件。 特别提醒,请在处理之前对你的源代码做备份。 下面分别以stunnel-4.20、lwip-0.5.3和kernel-2.6.20为例说明使用方法: 1,stunnel-4.20:
$ tar zxf stunnel-4.20.tar.gz
$ cd stunnel-4.20 $ ./configure $ make make通过后即可使用stripcc处理,“-d”表示处理失败时保存相关失败信息(在当前目录下的stripcc.dbg文件中),“.”表示处理执行stripcc时的当前目录下的所有源文件。 $ stripcc -d . Backing up all source file...[ OK] Adding "#warning" to each source file...[ OK] Parsing output of the compilation..(省略若干'.')..[ OK] Striping unnecessary CCs...[ OK] 再次make检验处理后源码是否正常: $ make 2,lwip-0.5.3:
$ tar zxf lwip-0.5.3.tar.gz
$ cd lwip-0.5.3 选用lwip做示例的主要原因是lwip编译时并非在lwip的顶层源码目录下执行“make”,而是在“proj/unixsim/”目录下,这也具有一定的普遍性。 $ cd proj/unixsim/ $ make 编译成功后回到顶层源码目录: $ cd .. $ cd .. 在顶层源码目录调用stripcc进行处理,但需告诉stripcc执行“make”的目录,使用“-m”选项: $ stripcc -m "proj/unixsim/" -d . Backing up all source file...[ OK] Adding "#warning" to each source file...[ OK] Parsing output of the compilation..(省略若干'.')..[ OK] Striping unnecessary CCs...[ OK] 再次make检验处理后源码是否正常: $ cd proj/unixsim/ $ make 3,kernel-2.6.20:
$ tar zxf linux-2.6.20.tar.gz
$ cd linux-2.6.20 使用uml平台默认编译选项: $ make menuconfig ARCH=um 为了说明stripcc的运行时间,这里使用time在编译时进行时间统计: $ time make ARCH=um real 4m6.178s user 3m12.692s sys 0m30.518s 统计使用stripcc处理前的代码量: $ find . -type f -name "*.[ch]" | xargs wc -l | grep total 191018 total 240112 total 178362 total 183622 total 166939 total 455531 total 466098 total 323675 total 418717 total 740114 total 606878 total 626812 total 572085 total 180344 total 66175 total 64838 total 87619 total 107007 total 62556 total 101182 total 119397 total 269427 total 460149 total 425931 total -------------- 7114588(这行是我加起来的,非上面命令的直接输出) 编译耗时4分钟多(P4 2.8GHZ),共有代码711万行,然后使用stripcc处理,stripcc默认的项目编译命令为“make”,但kernel编译时使用的是“make ARCH=um”,所以这里需要使用“-c”选项告诉stripcc编译命令,其他与上面编译一致: $ time stripcc -c "make ARCH=um" -d . Backing up all source file...[ OK] Adding "#warning" to each source file...[ OK] Parsing output of the compilation..(省略若干'.')..[ OK] Striping unnecessary CCs...[ OK] real 8m40.420s user 4m34.353s sys 2m4.149s 统计经stripcc处理后的内核代码量: $ find . -type f -name "*.[ch]" | xargs wc -l | grep total 0 total 218 total 0 total 0 total 19642 total 26707 total 8835 total 0 total 187 total 5337 total 0 total 46 total 110152 total 874 total 0 total 6407 total 0 total 0 total 2026 total 29228 total 44996 total 98737 total -------------- 421992 再次make检验处理后源码是否正常: $ make ARCH=um 一切正常!处理耗时8分钟多(P4 2.8GHZ),大概是正常编译的两倍时间,而处理后的内核代码只剩42万行了,这个结果还算不错吧,至少从代码量上说kernel也就是一个中型软件了,如果你现在用ctags或SourceInSight等工具来浏览代码,相信会容易很多。 4,其他: stripcc的早期版本称为nocc,考虑到sf.net已有同名项目,不便于发布,所以改名为stripcc。 stripcc及其早期版本经过了一定的测试,但难免存在bug,希望试用的朋友能将发现的问题发给我(dugang@188.com)。 这里附上一个验证过的源码列表,有些是我验证的,有些是其他朋友验证的,其中红色代表未通过,绿色代表正常通过: ethereal-0.99.0 fcitx-3.4.2 glibc-2.4 httpd-2.2.4 js-1.60 libevent-1.2 libpcap-0.9.5 lighttpd-1.5.0-r1691 linux-2.6.20 openssl-0.9.8d openvpn-2.0.9 privoxy-3.0.6-stable prozilla-1.3.7.4 screen-4.0.2 SDL-1.2.11 squid-2.6.STABLE10 ssldump-0.9b3 stardict-2.4.5 stunnel-4.20 tcpdump-3.9.5 thttpd-2.25b tor-0.1.2.7-alpha uClibc-0.9.28 valgrind-3.2.0 vim70 除ethereal使用yacc类工具在make过程中生成C代码导致stripcc未能正常通过外,其他均正常。另外需要注意的是如果对kernel-2.6.20以上内核做x86平台处理的话,请将include/asm-i386/percpu.h的第23行和26行: 23 addl $per_cpu__/**/var, cpu; 26 movl $per_cpu__/**/var, cpu; 改为 23 addl $per_cpu__cpu_gdt_descr, cpu; 26 movl $per_cpu__cpu_gdt_descr, cpu; 也就是将"/**/var"改成"cpu_gdt_descr",然后使用就可以了,这个问题可能是因为该.h文件实际上不是C的头文件而是汇编文件(.s)的头文件引起的。
如果您想进一步了解,请至
http://sourceforge.net/projects/stripcc/
|