如何将更改的行与C代码的git存储库中的函数相关联?

时间:2012-03-16 20:34:27

标签: c++ c regex git parsing

我正在尝试从存储在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”

2 个答案:

答案 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是你的焦点,那么他们可能是已发布的学生项目,这些项目对解析器进行了合理的尝试。