stripcc去除C源代码中未使用的条件编译分枝(转)

来源:互联网 发布:php开源手机商城系统 编辑:程序博客网 时间:2024/04/27 18:13
stripcc
    去除C源代码中未使用的条件编译分枝
http://hi.baidu.com/harite/blog/item/f41c2aa40aaa3df49052ee87.html

1,stripcc能干什么:

     使用C语言编写的软件为了适应各种不同的平台或要求,大多会使用条件编译,这在提高了软件适应性的同时却降低了代码的可读性,比如说有如下代码片断(摘自stunnel-4.20):

void stunnel_info(int raw) {
    char line[STRLEN];


    sprintf(line, "stunnel " VERSION " on " HOST " with %s",
         SSLeay_version(SSLEAY_VERSION));
    if(raw)
         log_raw("%s", line);
    else
         s_log(LOG_NOTICE, "%s", line);

     safecopy(line, "Threading:");
#ifdef USE_UCONTEXT
     safeconcat(line, "UCONTEXT");
#endif
#ifdef USE_PTHREAD
     safeconcat(line, "PTHREAD");
#endif
#ifdef USE_WIN32
     safeconcat(line, "WIN32");
#endif
#ifdef USE_FORK
     safeconcat(line, "FORK");
#endif

#ifdef HAVE_OSSL_ENGINE_H
     safeconcat(line, " SSL:ENGINE");
#endif

     safeconcat(line, " Sockets:");
#ifdef USE_POLL
     safeconcat(line, "POLL");
#else /* defined(USE_POLL) */
     safeconcat(line, "SELECT");
#endif /* defined(USE_POLL) */
#if defined(USE_WIN32) && !defined(_WIN32_WCE)
    if(s_getaddrinfo)
         safeconcat(line, ",IPv6");
    else
         safeconcat(line, ",IPv4");
#else /* defined(USE_WIN32) */
#if defined(USE_IPv6)
     safeconcat(line, ",IPv6");
#else /* defined(USE_IPv6) */
     safeconcat(line, ",IPv4");
#endif /* defined(USE_IPv6) */
#endif /* defined(USE_WIN32) */

#ifdef USE_LIBWRAP
     safeconcat(line, " Auth:LIBWRAP");
#endif

    if(raw)
         log_raw("%s", line);
    else
         s_log(LOG_NOTICE, "%s", line);
}


     阅读该源码,你认为它的可读性怎样?你是否能很容易的从诸多条件编译分枝中找出对应自己特定平台的部分?下面再附上该代码在linux平台(Fedora Core 3)的实际有效代码(使用stripcc处理得到):
   

void stunnel_info(int raw) {
    char line[STRLEN];

    sprintf(line, "stunnel " VERSION " on " HOST " with %s",
         SSLeay_version(SSLEAY_VERSION));
    if(raw)
         log_raw("%s", line);
    else
         s_log(LOG_NOTICE, "%s", line);

     safecopy(line, "Threading:");
     safeconcat(line, "PTHREAD");

     safeconcat(line, " SSL:ENGINE");

     safeconcat(line, " Sockets:");
     safeconcat(line, "POLL");
     safeconcat(line, ",IPv4");

     safeconcat(line, " Auth:LIBWRAP");

    if(raw)
         log_raw("%s", line);
    else
         s_log(LOG_NOTICE, "%s", line);
}


     再次阅读,是不是感觉清楚多了?

     其他常见的情况还包括针对不同平台同一函数会有不同实现,这样当你使用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/
原创粉丝点击