2009-10-13 2 views
2

У меня есть файл C, в котором мы перемещаем инфраструктуру ведения журнала. ТакУсловно изменение выражений C в исходном файле C

if (logging_level >= LEVEL_FINE) 
    printf("Value at %p is %d\n", p, i); 

становится

do_log2(LEVEL_FINE, "Value at %p is %d\n", _ptr(p), _num(i)); 

do_log2 означает журнал с 2-мя аргументами.

Для этого мне нужна инфраструктура синтаксического анализа и модификации C.

Какой инструмент я могу использовать, чтобы выполнить это наиболее легко?

Примечание: Printf может также появиться в файле как:

if (logging_level >= LEVEL_FINE) 
{ 
    printf("Value at %p is %d\n", 
    p, 
    i); 
} 

(с отступом и в блоке). Так что это будет трудно сделать из простого синтаксического анализа текста в perl.

EDIT: Это мой окончательный код Perl, который делает то, что я хочу

#!/usr/bin/perl -W 
$source=<<'END'; 
include etc 
if (logging_level >= LEVEL_DEBUG) 
{ 
    printf("1:Value at %p is %d\n", 
    p(1), 
    i(2)); 
} 
hello(); 
if (logging_level >= LEVEL_FINE) 
{ 
    printf("2:Value is %d\n", i); 
    printf("3:Value is %d\n", i); 
} 
if (logging_level >= LEVEL_FINE) 
{ 
    printf("2:Value is %d\n" 
    "and other info", i); 
} 

other(); 
if(logging_level>=LEVEL_INFO){printf("4:Value at %p is %d %d\n",p(x),i,j);} 
if(logging_level>=LEVEL_FINE) printf("5:Just sayin\"\n"); 
printf("not logging statement\n"). 
END 

while($source =~ m/\G(.*?\n)\s* if \s* \(\s* logging_level \s* >= \s* ([A-Z_0-9]+) \s* \) \s*(\{?)/sgxc) 
{ 
    my $othercode = $1; 
    my $loglevel=$2; 
    my $inblock = $3; 
    print("$othercode"); 

    while($source =~ m/\G\s*printf \(([^;]*) \) \;/sgxc) 
    { 
    my $insideprint = $1; 
    unless ($insideprint =~ /((\"([^\"\\]|\\.)*\")(\s*(\"([^\"\\]|\\.)*\"))*)/g) #fixing stackoverflow quote problem " 
    { 
     die "First arg not string literal"; 
    } 
    my $formatstr = $1; 
    my $remain = substr($insideprint, pos($insideprint)); 
    $remain =~ tr/\n \t//d; 
    my @args = split(",", $remain); 
    shift @args; 

    my $numargs = @args; 

    print "do_log${numargs}($loglevel, $formatstr"; 
    for (my $i=0; $i < $numargs; $i++) 
    { 
     unless ($formatstr =~ /%([a-z]+)/g) 
     { 
     die "Not enough format for args : $formatstr, args = ", join(",", @args), "\n"; 
     } 
     my $lastchar = substr($1, length($1) -1); 
     my $wrapper = ""; 
     if ($lastchar eq "u" || $lastchar eq "d") 
     { $wrapper = "_numeric";} 
     elsif($lastchar eq "p"){ $wrapper = "_ptr";} 
     elsif($lastchar eq "s"){ $wrapper = "_str";} 
     else { die "Unknown format char %$lastchar in $formatstr"; } 

     print ", ${wrapper}($args[$i])"; 
    } 
    print ");"; 
    last unless ($inblock); 
    } 
# eat trailing } 
    if ($inblock) 
    { 
    if ($source =~ m/\G \s* \} /sgxc) 
    { 
    } 
    else 
    { 
    } 
    } 
} 
#whatever is left 
print substr($source, pos($source)); 

выход:

include etc 
do_log2(LEVEL_DEBUG, "1:Value at %p is %d\n", _ptr(p(1)), _numeric(i(2))); 
hello(); 
do_log1(LEVEL_FINE, "2:Value is %d\n", _numeric(i)); 
do_log1(LEVEL_FINE, "3:Value is %d\n", _numeric(i)); 
do_log1(LEVEL_FINE, "2:Value is %d\n" 
     "and other info", _numeric(i)); 

other(); 
do_log3(LEVEL_INFO, "4:Value at %p is %d %d\n", _ptr(p(x)), _numeric(i), _numeric(j)); 
do_log0(LEVEL_FINE, "5:Just sayin\"\n"); 
printf("not logging statement\n"). 

Woohoo! Теперь применим к фактическому исходному коду.

+0

Я не думаю, что это такая трудная проблема для Perl. Но вам придется заплатить мне деньги, чтобы написать решение для вас. – 2009-10-13 04:21:05

ответ

4

Что вам нужно, это Program Transformation System, который может анализировать C и выполнять преобразования по сути кода (например, в соответствующих структурах данных компилятора), а не текст (поэтому его не путать с макетом текста и т. Д. .). (Преобразование программы - это обобщение рефакторинга).

DMS Software Reengineering Toolkit - такая система преобразования программ, и в ней есть парсер C, который применяется к очень крупным системам C.

С DMS, ваши изменения можно записать в виде:

domain C; -- work with C language syntax 

rule change_logging(exp: p, exp: i, s: literal_string, c:literal_integer): stmt -> stmt 
    "if (logging_level >= \l) 
     printf(\s, \p, \i);" 
    -> 
    "do_log2(\l, \s, _ptr(\p), _num(\i));". 

\ к, являются либо MetaQuotes (! "В C должно быть заключено в кавычки внутри правило кавычек) или метапеременные (\ р \ я \ s) из соответствующего типа синтаксиса.

на практике, один пишет набор взаимодействующий правила преобразования для выполнения более сложных задач (вы, вероятно, LOG1 и lOG3 случаи тоже).

узор переведенный, как и разобранный код C, в эквивалентные структуры данных компилятора, а затем сопоставляются с структурами данных компилятора для кода C, поэтому форматирование текста не имеет значения. Когда совпадение найдено, оно заменяется структурами данных компилятора для правой руки правила (2 ->). После того, как все преобразования были применены, результирующие структуры данных компилятора используются для регенерации модифицированного текста путем применения противоположного синтаксического анализа: prettyprinting. Вуала, твоя смена сделана.

Есть несколько осложнений с макросами и препроцессорными директивами, но это еще хуже, если вы делаете это со строковыми хакерскими методами, которые часто реализуются с помощью Perl.

Существуют также осложнения, связанные с рассуждениями о побочных эффектах, достижения определений, значений указателей и т. Д .; DMS обеспечивает поддержку для решения всех этих проблем.

+0

Есть ли такая система с открытым исходным кодом? –

+0

Ближайшая вещь - TXL и/или Stratego. Хорошей новостью является то, что у них есть одна и та же концепция. Более сложная новость заключается в том, что у них нет надежных C-парсеров, связанных с ними. Это непростые системы для сборки. DMS заняло 15 лет. –

+0

Что такое PERL, Ира? –

1

Преимущества C99 и __VA_ARGS__!

Насколько жестко у вас есть два примера макетов? В частности, у вас когда-либо была другая деятельность (например, петля) внутри условий if (logging_level...)-с-фигурными скобками? Или несколько сообщений printf() под управлением одного if?

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

Обращение с украшением параметров, как в _ptr(p), и _num(i) добавляет еще один уровень сложности. Вам нужно будет разбирать строковый литерал (доверяя, что никто не получил достаточно для использования чего-либо, кроме строкового литерала), чтобы определить, какие типы аргументов должны быть.

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

3

Вам не нужно рассчитывать аргументы:

#include <stdio.h> 
#include <stdarg.h> 

void do_log(int level, char *format, ...){ 
    va_list ap; 
    va_start(ap, format); 
    printf("level: %i ", level); vprintf(format, ap); puts(""); 
    va_end(ap); 
} 

int main(){ 
    do_log(1, "zero"); 
    do_log(2, "one: %i", 1); 
    do_log(3, "one: %i two: %i", 1, 2); 
} 

Я хотел бы сделать перезапись кода с Perl. Я не понимаю, почему это сложно.

EDIT: я написал некоторые PERL код переписать части кода регистрации:

#!/usr/bin/perl -W 

$source=<<'END'; 
if (logging_level >= LEVEL_FINE). 
{ 
    printf("1:Value at %p is %d\n",. 
    p(1), 
    i(2)); 
} 

if(logging_level>=LEVEL_FINE){printf("2:Value at %p is %d\n",p(x),i,j);} 
END 

$res = ''; 
while($source =~ /\G(.*?)if\s*\(\s*logging_level\s*>=\s*([A-Z_]+)\s*\)\s*{\s*printf\s*(\(((?:[^()]+|(?3))+)\))\s*;\s*}/sg){ 
    $lastpos = pos($source); $res .= $1; $l=$2; $p=$4; $p =~ s/[\r\n\s]+//g; 
    $c = $p =~ tr/,/,/; 
    $res .= "do_log$c($l,$p);"; 
} 
print $res, substr($source,$lastpos); 

Результат:

do_log2(LEVEL_FINE,"1:Valueat%pis%d\n",p(1),i(2)); 

do_log3(LEVEL_FINE,"2:Valueat%pis%d\n",p(x),i,j); 

добавить простой аргумент подсчета в коде. Надеюсь помочь.

+0

инфраструктура регистрации (do_log * и т. Д.) Установлена ​​в каменном хехе, я не могу ее реализовать. Мне не разрешено даже писать упрощающую оболочку. Большая старая система, строгие правила. –

+0

Мне все еще приходится обрабатывать многострочные строки, концепцию _ptr, _num и т. Д., Но ваш код является хорошей отправной точкой. Благодаря! Теперь позвольте мне посмотреть, могу ли я расшифровать это RE: P –

0

Сколько раз стоит logging_level? Этот процесс называется рефакторингом. Если изменение тривиально, в вашем любимом редакторе можно использовать хорошее регулярное выражение. Но часто код имеет множество вариаций по одной теме. В этом случае все они могут быть найдены через logging_level. Вы можете их поэтапно скрыть, указав код logging_level (так что вы получите предупреждение о компиляторе, но оно все равно будет работать). Или используйте редактор, похожий на источник, который может показать вам все ссылки за один раз.

Некоторые примеры изменений (которые были бы трудно найти с помощью сценария):

if (logging_level >= LEVEL_FINE) 
    printf("Value at %p is %d\n", p, i); 

if (logging_level >= LEVEL_FINE) { 
    calculated_value = i*2/3; 
    printf("Value at %p is %f\n", p, calculated_value); 
} 

(обратите внимание на скобки и расчетные переменные).

Для каждого файла со старой конструкцией, вы можете сделать поиск замены:

поиск: if \(\s*logging_level\s*>=\s*(LEVEL_[a-zA-Z]+) заменить: do_log2(\1,)

Можно включить Printf, но только если ваш редактор поддерживает многострочные шаблоны.

1

Возможно, вы также захотите проверить coccinelle, который также используется хакерами ядра Linux для широкомасштабного автоматического масштабирования code transformations с использованием семантического исправления.

Надеется, что это помогает

2

Решение Coccinelle будет:

 
@@ 
expression p,i; 
@@ 

-if (logging_level >= LEVEL_FINE) 
- printf("Value at %p is %d\n", p, i); 
+do_log2(LEVEL_FINE, "Value at %p is %d\n", _ptr(p), _num(i)); 

Там нет общего способа решить проблему calculated_value упоминавшейся выше, но можно было бы найти код, который имеет эту проблему следующим образом:

 
@@ 
expression p,i; 
@@ 

*if (logging_level >= LEVEL_FINE) 
    { ... 
* printf("Value at %p is %d\n", p, i); 
    ... } 

результат будет выглядеть дифф, но минусы в колонке 0, предназначены для обозначения предметов, представляющих интерес, а не элементы для удаления ,

+0

Спасибо, я надеюсь, что Coccinelle предлагает некоторые возможности сценариев, так что аргумент arg также может быть разрешен. Я решил проблему с perl, но спасибо anyways :) –

Смежные вопросы