Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1042854
  • 博文数量: 326
  • 博客积分: 10135
  • 博客等级: 上将
  • 技术积分: 2490
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-22 23:53
文章分类

全部博文(326)

文章存档

2014年(1)

2012年(4)

2011年(1)

2010年(4)

2009年(41)

2008年(44)

2007年(63)

2006年(168)

我的朋友

分类: LINUX

2009-12-07 15:48:22

Du XiaoGang<>


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方式发布,你可以从处下载源码文件,获取源码后解压 、make、make install 完成编译安装。安装目录在/usr/local/bin下,可以选择自己拷贝或者修改Makefile以便放入其他位置,但请确认stripcc所在目录 在你的执行目录$PATH里。

    编译安装完成后就是利用stripcc处理你的源文件。特别提醒,请在处理之前对你的源代码做备份

    执行stripcc -h会列出stripcc的使用方法,主要是各个可选项的含义,如下:

    $ stripcc -h
    Usage: stripcc
    Remove CCs(Conditional Compilation Branchs) which won't be compiled from c/c++ source files under current directory.
     The options are:
      -c Commands for build target program, "make" is default
      -m Directory to run build commands, current directory is default
      -f       Working in fast mode (Experimental, NOT RECOMMENDED!)
      -n       Don't verify after strip
      -v       Display this program's version number
      -h       Display this output

    “-c” 用来指定项目编译使用的命令,默认是“make”,如果您的项目也是“make”编译,则可略过“-c”选项,如果是其他,比如“make vmlinux”则需要指定“ -c "make vmlinux" ”,具体可参考后文所举的Linux的例子。
    “-m” 用来指定执行编译命令(如“make”)的目录,这对一些不是在顶层目录执行编译命令的项目非常有用,可参考后文所举的lwip例子。
    “-f” 让stripcc运行在快速模式,这是stripcc-0.2.0新加入的特性,但可惜的是这个模式对项目的要求比较苛刻,不推荐使用
    “-n” 让stripcc在处理完代码之后直接退出,而不执行验证过程,用户可以手动敲入“make”等项目编译命令自行验证,这也是0.2.0版本的新加入特性,默认为自动执行验证。
    “-v” 显示stripcc的版本信息。
    “-h” 显示上面的帮助信息。

    此外,stripcc从0.2.0版本开始支持配置文件,这很大程度上提高了用户对代码处理过程的控制度,处理代码时stripcc将在被调用的当前目录下寻找名为stripcc.conf的配置文件,该文件包含5个部分:

    [strip_exts] 表示以哪些后缀名结束的文件将被处理,默认为“.c”和“.h”。
    [strip_dirs] 表示哪些目录下的特定后缀名文件将被处理(包含子目录),默认为“.”,即当前目录。
    [dont_strip_dirs] 表示哪些目录下的文件将不被处理,即需要从[strip_dirs]目录中去掉的部分,默认为空。
    [strip_files] 表示还有哪些单独的文件应当被处理,这些文件不受后缀名的限制,默认为空。
    [dont_strip_files] 表示不应当被处理的文件。

    关于配置文件的详细写法,可以参考源码包中的stripcc.conf,需要注意的是所有目录和文件都采用相对路径,即相对于stripcc.conf所在的目录,另外,所有路径都以“.”开始,比如“./dir/file.c”

    当stripcc在当前调用目录下找不到stripcc.conf时将会使用默认值,即递归处理当前目录下的所有“.c”和“.h”文件。

    下面分别以stunnel-4.21、lwip-0.5.3和kernel-2.6.27.7为例说明使用方法:

    1,stunnel-4.21:

    $ tar zxf stunnel-4.21.tar.gz
    $ cd stunnel-4.21
    $ ./configure
    $ make

    make通过后即可使用stripcc处理,stripcc默认处理当前目录下的所有源文件(与0.1.*版不同,stripcc自从0.2.0版开始不需要再指定处理目录)。
    $ stripcc
    o Getting a list of files which will be stripped...
      o Failed to open config file(./stripcc.conf), use the default values.
    o Backing up files...
    o Adding "#warning"...
    o Try to build target, this operation may takes a few minutes
    o Restore files...
    o Stripping files...
    o Start to verify
    o Done!

    看到绿色的“Done!”表示处理成功。

   
    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"
    o Getting a list of files which will be stripped...
      o Failed to open config file(./stripcc.conf), use the default values.
    o Backing up files...
    o Adding "#warning"...
    o Try to build target, this operation may takes a few minutes.
    o Restore files...
    o Stripping files...
    o Start to verify
    o Done!

   
    3,kernel-2.6.27.7:

    $ tar jxf linux-2.6.27.7.tar.bz2
    $ cd linux-2.6.27.7

    使用x86平台默认编译选项并编译:
    $ make defconfig
    $ make vmlinux

    统计使用stripcc处理前的kernel代码总量:
    $ totalcode
    totalline:  8410243

    共有代码841万行,然后使用stripcc处理,stripcc默认的项目编译命令为“make”,但kernel编译时使用的是“make vmlinux”,所以这里需要使用“-c”选项告诉stripcc编译命令,其他与上面编译一致:
    $ stripcc -c "make vmlinux"
    o Getting a list of files which will be stripped...
      o Failed to open config file(./stripcc.conf), use the default values.
    o Backing up files...
    o Adding "#warning"...
      o Invalid source line(./arch/mips/kernel/sync-r4k.c: 159).
    o Try to build target, this operation may takes a few minutes.  
    o Restore files...
    o Stripping files...
    o Start to verify   
    o Done!

    统计经stripcc处理后的内核代码量:
    $ totalcode
    totalline:  1287399

    处理耗时大概是正常编译的5倍时间(包含验证过程),而处理后的内核代码只剩128万行了,大约有85%的内核代码被清除,如果现在用ctags或 SourceInSight等工具来浏览代码,相信会容易很多。事实上由于本次编译采用的是kernel默认选项,代码中仍然包含大量的您可能永远用不到 的内核特性和设备驱动,如果去除这些编译选项,完全可以将kernel的代码控制在40万行以内。


4,其他:
   
    1,stripcc-0.2.0版本做了哪些主要改进?相对于0.1.*版本:
        a,支持配置文件,更加灵活的使用方式。
        b,加强了代码容错功能,stripcc-0.1.*工作时如果遇到有语法错误的C文件将自动退出,0.2.0版本会忽略该文件而继续处理。
        c,增加了快速处理模式,但不幸的是事实效果并不好,本来预想的0.2.0的最大改进只能暂时作为实验特性。

    2,stripcc-0.2.0支持哪些OS或者硬件平台?原理上stripcc-0.2.0可以支持所有的能运行GCC的POSIX平台,目前已经验证过的平台如下:
        solaris/t1000:
            -bash-3.00$ uname -a
            SunOS t1000 5.10 Generic_118833-33 sun4v sparc SUNW,Sun-Fire-T1000 Solaris

            -bash-3.00$ gcc --version
            sparc-sun-solaris2.10-gcc (GCC) 4.0.3 (gccfss)
            Copyright (C) 2006 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        solaris/x4100:
            -bash-3.00$ uname -a
            SunOS x4100 5.10 Generic_118855-33 i86pc i386 i86pc

            -bash-3.00$ gcc --version
            gcc (GCC) 3.4.3 (csl-sol210-3_4-branch+sol_rpath)
            Copyright (C) 2004 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        solaris/i386:
            -bash-3.00$ uname -a
            SunOS solaris 5.10 Generic_118855-33 i86pc i386 i86pc Solaris

            -bash-3.00$ gcc --version
            gcc (GCC) 3.4.3 (csl-sol210-3_4-branch+sol_rpath)
            Copyright (C) 2004 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        linux/x86_64:
            dugang@ubuntu:~/test$ uname -a
            Linux ubuntu 2.6.24-21-generic #1 SMP Tue Oct 21 23:09:30 UTC 2008 x86_64 GNU/Linux

            dugang@ubuntu:~/test$ gcc --version
            gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)
            Copyright (C) 2007 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        freebsd/amd64:
            -bash-2.05b$ uname -a
            FreeBSD freebsd.unix-center.net 6.2-RELEASE FreeBSD 6.2-RELEASE #0: Fri Jan 12 08:43:30 UTC 2007
            root@portnoy.cse.buffalo.edu:/usr/obj/usr/src/sys/SMP  amd64

            -bash-2.05b$ gcc --version
            gcc (GCC) 3.4.6 [FreeBSD] 20060305
            Copyright (C) 2006 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        aix/powerpc:
            -bash-3.00$ uname -a
            AIX aix 3 5 00C97AC04C00 powerpc unknown AIX

            -bash-3.00$ gcc --version
            gcc (GCC) 4.2.0
            Copyright (C) 2007 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        linux/i386:
            [duxg@l151 stripcc]$ uname -a
            Linux l151 2.6.23.1-42.fc8 #1 SMP Tue Oct 30 13:55:12 EDT 2007 i686 i686 i386 GNU/Linux

            [duxg@l151 stripcc]$ gcc --version
            gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33)
            Copyright (C) 2006 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions.  There is NO
            warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    3,stripcc对于C/C++的支持如何?
        stripcc遵循C99标准并且尽可能的支持GCC扩展特性,其中唯一与C99相悖的是对多行注释(/* ... */)的处理,C99要求保留其中的换行符,但GCC默认丢弃换行符,这里stripcc与GCC保持一致。
        C++的支持上stripcc并没有做什么工作,如果C++语言预编译等部分与C99一致,那么stripcc确实不需要做更多工作,否则只能留待将来处理,到目前为止,没有听到有C++处理方面的错误。

5,已知的问题:

    1,由于stripcc的工作原理限制,在遇到打开GCC的“-Werror”编译选项的项目时不能正常工作,所以如果stripcc提示处理失败,请先 确认一下问题是不是由于该选项引起,如果是的话从Makefile中去掉该选项即可,目前见到的打开了该选项的项目有trousers等。

    2,stripcc不能处理在编译过程中自动从项目已有C源码中抽取代码组成新源码文件的项目,比如GCC/Ethereal等。但对于编译时从非C源码文件产生新源码的项目则不会有问题,比如那些使用了lex/yacc等自动编译工具的项目。

6,致谢:
   
    感谢各位朋友使用stripcc并且提出的宝贵意见。

    特别感谢为stripcc提供补丁的朋友,包括:
    fangmz
    harite.k
    xishipan

    google docs()对于需要在linux下写文档的朋友是个不错的选择,本文档即使用google docs编写。

    unix-center()提供了诸多Unix实验环境,stripcc的多平台测试从而得以轻松完成。

    最后,如果您对stripcc有什么建议或意见,请联系我:dugang@188.com,谢谢!
阅读(1951) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~