Как связать измененные строки с функциями в git-репозитории кода C? - PullRequest
11 голосов
/ 17 марта 2012

Я пытаюсь создать & ldquo; heatmap & rdquo; из многолетней истории, хранящейся в репозитории git, где единицей гранулярности являются отдельные функции. Функции должны нагреваться по мере того, как они будут меняться чаще, чаще и с изменением количества непустых строк.

Для начала я проверил вывод

git log --patch -M --find-renames --find-copies-harder --function-context -- *.c

Я смотрел на использование Language.C от Hackage, но, похоже, ему нужна полная единица перевода - расширенные заголовки и все - скорее, возможность справиться с исходным фрагментом.

Опция --function-context является новой с версии 1.7.8. Основой реализации в v1.7.9.4 является регулярное выражение :

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]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),

Кажется, это достаточно хорошо распознает границы, но не всегда оставляет функцию в качестве первой строки блока diff, например, , с директивами #include вверху или с блоком, который содержит несколько определения функций. Было бы очень полезно иметь возможность указывать diff для генерации отдельных блоков для каждой измененной функции.

Это не критично для безопасности, поэтому я могу допустить некоторые промахи. Означает ли это, что у меня, скорее всего, у Завински две проблемы? Rdquo; ?

Ответы [ 2 ]

1 голос
/ 17 марта 2012

Я понимаю, что это предложение немного касательно, но оно может помочь уточнить и оценить требования.Это будет работать для C или C ++ ...

Вместо того, чтобы пытаться найти текстовые блоки, которые являются функциями, и сравнивать их, используйте компилятор для создания двоичных блоков.В частности, для каждого исходного файла C / C ++ в наборе изменений скомпилируйте его в объект.Затем используйте объектный код в качестве основы для сравнений.

Это может быть неосуществимо для вас, но в IIRC есть опция компиляции на gcc, чтобы каждая функция компилировалась в «независимый фрагмент» внутри сгенерированногофайл объектного кода.Компоновщик может вытянуть каждый «кусок» в программу.(Становится здесь довольно поздно, поэтому я посмотрю это утром, если вам интересна идея.)

Итак, если мы сможем это сделать, у вас будет много функций, определенныхкуски двоичного кода, поэтому простое «тепловое» сравнение состоит в том, «насколько длиннее или короче код между версиями для любой функции?»

Я также думаю, что было бы целесообразно использовать objdump для восстановления ассемблерадля функций.Я мог бы использовать некоторые регулярные выражения на этом этапе, чтобы обрезать имена регистров, чтобы изменения в распределении регистров не вызывали слишком много ложных срабатываний (изменений).

Я мог бы даже попытаться отсортировать инструкции на ассемблере в телах функций и различить их, чтобы получить образец «удалено» против «добавлено» между двумя реализациями функций.Это дало бы меру изменений, которая в значительной степени не зависит от макета и даже несколько не зависит от порядка некоторых источников.

Так что может быть интересно посмотреть, являются ли две альтернативные реализации одной и той же функции (т.е. из разных наборов изменений) одинаковыми инструкциями: -)

Этот подход также должен работать для C ++, потому чтовсе имена были соответствующим образом искажены, что должно гарантировать сравнение одних и тех же функций.

Таким образом, регулярные выражения могут быть очень простыми: -)

Предполагая, что все это просто, чтоможет ли этот подход не дать вам?

Примечание: эта базовая стратегия может работать для любого языка, предназначенного для машинного кода, а также для наборов инструкций VM, таких как байт-код Java VM, код .NET CLR и т. д.

0 голосов
/ 19 марта 2012

Возможно, стоит подумать о создании простого синтаксического анализатора с использованием одного из распространенных инструментов, а не просто с помощью регулярных выражений.Ясно, что лучше выбрать что-то, с чем вы знакомы, или то, что ваша организация уже использует.

Для этой проблемы парсеру на самом деле не нужно проверять код (я предполагаю, что он действителен при проверкев), и ему не нужно понимать код, поэтому он может быть довольно глупым.

Он может отбрасывать комментарии (сохраняя новые строки), игнорировать содержимое текстовых строк и обрабатывать текст программы вочень простой способ.В основном он должен отслеживать сбалансированный '{' '}', сбалансированный '(' ')', а весь другой действительный текст программы - это просто отдельные токены, которые можно передать "напрямую".

Это выводможет быть отдельным файлом / функцией для облегчения отслеживания.

Если язык C или C ++, а разработчики разумно дисциплинированы, они могут никогда не использовать «несинтаксические макросы».Если это так, то файлы не нужно предварительно обрабатывать.

Тогда синтаксический анализатор в основном просто ищет имя функции (идентификатор) в области видимости файла, за которой следует (список параметров) {... code ...}

Я бы сказал, что это будет работа в течение нескольких дней с использованием yacc & lex / flex & bison, и это может быть настолько просто, что в генераторе синтаксического анализатора нет необходимости.

Если код на Java, то возможен ANTLR, и я думаю, что был простой пример синтаксического анализатора Java.

Если ваш фокус на Haskell, это могут быть опубликованные студенческие проекты, которые сделалиразумный удар в парсере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...