Chrome的差量升级Courgette

Windows客户端做程序的升级是一件比较复杂的事情。不像移动端,有专门的移动商店用来安装和更新程序,Windows客户端程序升级需要自己制作安装包,放到服务器上供用户下载,后续还需要提供升级服务。由于用户操作不可预知性和用户环境的复杂,安装跟升级都会发生各种异常而失败。迄今为止Windows上也没有一个好的程序安装、升级、卸载框架,这不得不说是Windows开发的悲哀。 而且目前程序的安装包越来越大了,动辄几十兆,用户下载安装包需要很长的时间,这也是导致安装升级成功率低的一个原因。所以,安装升级包越小越好。对于这个问题,有两个解决思路:

对于第一种方案,优点是实现起来比较简单,我之前的老东家360很多产品就是用的这种方案。这个方案的缺点也很多:

第二种方案已经有了bsdiff了。但是对于Widnows客户端程序,依然存在很大的挑战。因为C++这种需要编译的代码,充满了内部地址引用,一些指令直接包含其他指令的地址或者位移,几行代码的修改就会导致许多地址值的改变,结果导致最终生成的二进制文件差异很大。

Google的Chrome开发团队特地为C++这种需要代码编译的程序开发了一种叫做Courgette的差异算法。原理是先把可执行的二进制程序进行反汇编,找到那些内部的地址指针,把它们替换成一个符号,然后再用常规的办法生成差异文件。

下面是常规的bsdiff升级:

    server:
        diff = bsdiff(original, update)
        transmit diff

    client:
        receive diff
        update = bspatch(original, diff)

这个是Courgette升级:

    server:
        asm_old = disassemble(original)
        asm_new = disassemble(update)
        asm_new_adjusted = adjust(asm_new, asm_old)
        asm_diff = bsdiff(asm_old, asm_new_adjusted)
        transmit asm_diff

    client:
        receive asm_diff
        asm_old = disassemble(original)
        asm_new_adjusted = bspatch(asm_old, asm_diff)
        update = assemble(asm_new_adjusted)

不得不佩服,这是个绝妙的思路。没有对C++、编译器、汇编、PE文件格式深厚了解的人,是无法设计出如此精妙的方案。 Chrome团队还有一个数据的对比:

Full update              10,385,920
bsdiff update           704,512
Courgette update    78,848

对PE文件来说,采用Courgette做差异的算法比bsdiff小了一个数量级。

Chrome的升级是采用了Courgette,对应的开源Chromium默认是没有升级功能的。不过我们编译mini_installer工程的时候,在gn/gyp里面加上下面几个命令行就可以了:

      # Optional arguments to generate diff installer.
      '--last_chrome_installer=C:/Temp/base',
      '--setup_exe_format=DIFF',
      '--diff_algorithm=COURGETTE',

我们把旧版本的chrome.7z和setup.exe文件放到c:/temp/base目录里面,就会生产了差量的mini_installer。

当然我们也可以手动生成差量文件,编译好courgette.exe:

    "  courgette -supported <executable_file>\n"
    "  courgette -dis <executable_file> <binary_assembly_file>\n"
    "  courgette -asm <binary_assembly_file> <executable_file>\n"
    "  courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
    "  courgette -gen <v1> <v2> <patch>\n"
    "  courgette -apply <v1> <patch> <v2>\n"

gen是生成差量文件,apply是应用差量文件。

参考: