#diff #line #difference #part #utility #context #intra-line

app subdiff

类似于 diff 的工具,考虑行选择的特定部分

1 个不稳定版本

使用旧的 Rust 2015

0.1.0 2018年6月19日

#51#difference

GPL-3.0 许可证

78KB
2K SLoC

subdiff

subdiff 是一个类似于 diff 的工具,它会对输入行应用一组正则表达式,使用每行的选定部分计算差异,然后以原始输入行的形式输出差异。

动机

当处理执行跟踪或任何类型的结构化序列化输出时,通常只对数据中选定的部分的变化感兴趣。这通常是因为输出中存在某种时间上的变化(例如,时间戳或序列号)或半随机值(例如,id 号或输入数据)。

然而,上下文本身,无论是行内(行中忽略的部分)还是行间(上下文行),对于解释差异可能非常有意义。例如,当一系列操作偶尔无法及时完成时,执行中变化周围的时间戳可能有助于诊断问题(当需要将输出与不同系统上的并发输出相关联时,尤其如此)。

示例输出

假设我们想查看两个不同版本的 Xen 虚拟机启动之间的差异,忽略时间戳进行比较,但在最终输出中仍然可以看到它们。

默认情况下,subdiff 提供关于差异的准确信息。

例如,假设你想比较系统启动的两个日志文件。运行 journalctl -b N 产生如下形式的行

Jun 12 17:59:27 dom0 kernel: x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'

比较两个月份 apart 的启动日志会产生如下输出(只截取了一个块的部分)

$ subdiff  -r "^.*:.*:.*:(.*)" boot.old boot.new
[...]
@@ -49,26 +51,27 @@
 {-Nov}{+Jun} {-09}{+12} {-20}{+17}:{-45}{+59}:{-39}{+27} {-localhost}{+dom0} kernel: x86/PAT: MTRRs disabled, skipping PAT initialization too.
 {-Nov}{+Jun} {-09}{+12} {-20}{+17}:{-45}{+59}:{-39}{+27} {-localhost}{+dom0} kernel: x86/PAT: Configuration [0-7]: WB  WT  UC- UC  WC  WP  UC  UC  
 {-Nov}{+Jun} {-09}{+12} {-20}{+17}:{-45}{+59}:{-39}{+27} {-localhost}{+dom0} kernel: e820: last_pfn = 0x9cf00 max_arch_pfn = 0x400000000
-Nov 09 20:45:39 localhost kernel: Scanning 1 areas for low memory corruption
[...]

好吧。变化很大。如果时间戳不是那么重要呢?subdiff 提供了另一种模式,可以保留“常见”行的格式:按字符类别进行摘要。

$ subdiff --context-format=cc  -r "^.*:.*:.*:(.*)" boot.old boot.new
[...]
@@ -49,26 +51,27 @@
 \a+ \d+ \d+:\d+:\d+ \w+ kernel: x86/PAT: MTRRs disabled, skipping PAT initialization too.
 \a+ \d+ \d+:\d+:\d+ \w+ kernel: x86/PAT: Configuration [0-7]: WB  WT  UC- UC  WC  WP  UC  UC  
 \a+ \d+ \d+:\d+:\d+ \w+ kernel: e820: last_pfn = 0x9cf00 max_arch_pfn = 0x400000000
-Nov 09 20:45:39 localhost kernel: Scanning 1 areas for low memory corruption
[...]

好吧,现在,让我们看看两个 strace -T(即“记录系统调用的耗时”)运行 ls 列出 /opt 和 ls 列出 /media(两者都是空的)

$ diff -u ls.opt ls.media | diffstat
 ls.media |  318 +++++++++++++++++++++++++++++++--------------------------------
 1 file changed, 159 insertions(+), 159 deletions(-)

而只选择行中 strace 时间戳之前的部分,忽略地址、时间结构和非错误返回值

$ subdiff  -r "(.*)<\d+.+>$" -i '(0x[a-f0-9]+)|(tv_[un]?sec=\d+)|(=\s\d+\s)' ls.opt ls.media  | diffstat
 ls.media |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

实际上,只有 execvestatopen 的参数不同。

一个查看“空间”格式差异的最终示例。比较在简单二进制文件中进行的微小任意修改前后的反汇编

$ subdiff --context-format=cc  -r "^\s+[0-9a-f]+:\t.*\t(.*)$" -r "^[0-9a-f]+\s(.*)$" -i '(0x[a-f0-9]+)|(#.*)|([0-9a-f]+\s<)|(^\s+[0-9a-f]+:\t00.*)|(.*file format.*)' old new
[...]
@@ -117,14 +117,16 @@
   40053a:      ba \d+ \d+ \d+ \d+              mov    $\w+,%edx
   40053f:      89 c8                   mov    %ecx,%eax
   400541:      f7 ea                   imul   %edx
-  400543:      d1 fa                   sar    %edx
+  400543:      8d 04 0a                lea    (%rdx,%rcx,1),%eax
+  400546:      c1 f8 02                sar    $0x2,%eax
+  400549:      89 c2                   mov    %eax,%edx
   \w+: 89 c8                   mov    %ecx,%eax
   \w+: c1 f8 1f                sar    $0x1f,%eax
   \w+: 29 c2                   sub    %eax,%edx
   \w+: 89 d0                   mov    %edx,%eax
   \w+: 89 c2                   mov    %eax,%edx
   \d+: c1 e2 \d+               shl    $\w+,%edx
-  400553:      01 c2                   add    %eax,%edx
+  400559:      29 c2                   sub    %eax,%edx
   \w+: 89 c8                   mov    %ecx,%eax
   \w+: 29 d0                   sub    %edx,%eax
   \w+: eb 1b                   jmp    \w+ <foo+\w+>
[...]

注意:除非是最简单的情况,否则无法像这样比较二进制文件。

尽管在适当的条件下,即当输入的每一部分都可以用正则语法描述时,subdiff可以给出实际变化的快速概述。请注意,在上面的例子中,我们需要使用两个正则表达式:一个用于打印函数符号的行,另一个用于反汇编指令。

功能

  • diff兼容输出(例如,可以用于diffstat)
  • 智能处理上下文

更多信息可以在手册页中找到。

限制

  • subdiff不如diff功能丰富。重要的是,它只使用一种diff算法:由lcs-diff crate实现的长度最长的公共子序列算法。
  • subdiff会同时将两个输入文件保留在内存中。虽然可以解决这个问题,但这将需要修改lcs-diff的变更。
  • 后端正则表达式引擎(regex)不支持前瞻,这意味着无法表达诸如“忽略引号之间的字符,但在引号字符串中不要考虑转义引号”的概念。

依赖项

~4–5.5MB
~87K SLoC