我正在尝试从存储在git存储库中的多年历史中构建一个“热图”,其中粒度单位是单独的函数。函数变得越来越热,因为它们更频繁地更换次数,并且更改了非空白行。
首先,我检查了
的输出git log --patch -M --find-renames --find-copies-harder --function-context -- *.c
我查看了使用Hackage中的Language.C,但它似乎想要一个完整的翻译单元扩展标题,并且所有 - 而不是能够处理源片段。
--function-context
选项是自1.7.8版以来的新选项。实施的基础in v1.7.9.4 is a regex:
PATTERNS("cpp",
/* Jump targets or access declarations */
"!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
/* C/++ functions/methods at top level */
"^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
/* compound type at top level */
"^((struct|class|enum)[^;]*)$",
/* -- */
"[a-zA-Z_][a-zA-Z0-9_]*"
"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
"|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
这似乎很好地识别了边界,但并不总是将函数作为差异块的第一行,例如,顶部带有#include
指令或带有大块包含多个函数定义。告诉diff为每个更改的函数发出单独的数据的选项非常有用。
这不是安全关键,所以我可以忍受一些失误。这是否意味着我可能有Zawinski的“two problems”?
答案 0 :(得分:1)
我意识到这个建议有点切合,但它可能有助于澄清和排列要求。这适用于C或C ++ ......
不要试图找到作为函数的文本块并比较它们,而是使用编译器来生成二进制块。具体而言,对于更改集中的每个C / C ++源文件,将其编译为对象。然后使用目标代码作为比较的基础。
这对你来说可能不太可行,但是IIRC在gcc上有一个选项可以编译,这样每个函数都可以编译成生成的目标代码文件中的“独立块”。链接器可以将每个“块”拉入程序。 (现在已经很晚了,所以如果你对这个想法感兴趣,我会在早上看一下。)
因此,假设我们可以这样做,你将拥有许多由二进制代码块定义的函数,因此一个简单的“热量”比较是“任何函数的版本之间的代码有多长或多短?” / p>
我也认为使用objdump为函数重构汇编程序可能是切实可行的。我可能会在此阶段使用一些正则表达式来修剪寄存器名称,因此寄存器分配的更改不会导致过多的误报(更改)。
我甚至可能尝试在函数体中对汇编程序指令进行排序,并对它们进行区分以获得两个函数实现之间“已删除”与“已添加”的模式。这将给出一种变化的度量,它几乎与布局无关,甚至在某种程度上与某些源的顺序无关。
因此,看看同一个函数的两个替代实现(即来自不同的变更集)是否是相同的指令可能会很有趣: - )
这种方法也适用于C ++,因为所有名称都已被适当修改,这应该保证比较相同的函数。
因此,正则表达式可能非常简单: - )
假设所有这一切都很简单,这种方法可能会给你什么?
备注:此基本策略适用于任何针对机器代码的语言,以及Java指令集,如Java VM字节码,.NET CLR代码等。
答案 1 :(得分:0)
使用常用工具之一构建简单的解析器可能是值得考虑的,而不仅仅是使用正则表达式。显然,最好选择您熟悉的或您的组织已经使用过的东西。
对于这个问题,解析器实际上并不需要验证代码(我认为它在签入时是有效的),并且它不需要理解代码,所以它可能非常愚蠢。 / p>
它可能会丢弃注释(保留新行),忽略文本字符串的内容,并以非常简单的方式处理程序文本。它主要需要跟踪平衡的'{''}',平衡'('')',所有其他有效的程序文本只是单个标记,可以直接通过。
它的输出可能是一个单独的文件/功能,使跟踪更容易。
如果语言是C或C ++,并且开发人员有相当的纪律,他们可能永远不会使用“非语法宏”。如果是这种情况,则不需要对文件进行预处理。
然后解析器主要只是在文件范围内查找函数名称(标识符),然后是(parameter-list){... code ...}
我是SWAG,使用yacc&amp; amp; lex / flex&amp;野牛,它可能很简单,它们不需要解析器生成器。
如果代码是Java,那么ANTLR是可能的,我认为有一个简单的Java解析器示例。
如果Haskell是你的焦点,那么他们可能是已发布的学生项目,这些项目对解析器进行了合理的尝试。