1 个不稳定版本
使用旧的 Rust 2015
0.1.0 | 2018年6月19日 |
---|
#51 在 #difference
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(-)
实际上,只有 execve
、stat
和 open
的参数不同。
一个查看“空间”格式差异的最终示例。比较在简单二进制文件中进行的微小任意修改前后的反汇编
$ 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