Библиотека отладки printf () с использованием таблицы строк «Кольцо декодера» - PullRequest
8 голосов
/ 02 августа 2011

Я пишу, чтобы увидеть, видел ли кто-нибудь из вас или не слышал о реализации идеи, которую я собираюсь описать.

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

Довольно часто операторы отладки выглядят примерно так:

myDebugLibraryPrintf("Inside loop, processing item %d out of %d.\n", i, numItems);

Конечно, когда это развернуто в текст, напечатанная строка выглядит примерно так: «Внутри цикла, обработка элемента 5 из 10. \ n», всего ~ 42 байта или около того. Более 90% данных, напечатанных этим утверждением, являются статическими, буквальными - известными во время компиляции. Конечно, только «5» и «10» не известны во время компиляции.

Что я хотел бы сделать, так это уметь отправлять обратно только эти два целых числа (8 байтов вместо 42). Как только я получу эти данные, у меня будет какое-то «кольцо декодера», которое позволит мне «восстановить» полученные данные и распечатать полное отладочное сообщение здесь, в моем месте.

Я бы сгенерировал «кольцо декодера», автоматически (как часть процесса сборки), дав каждому выражению myDebugLibraryPrintf () уникальный идентификатор во время компиляции, и создав таблицу, которая отображает эти уникальные идентификаторы в исходные строки формата. Затем, каждый раз, когда myDebugLibraryPrintf () вызывается для цели, он передает уникальный идентификатор и любое из значений "%d", "%f" и т. Д., Которые видны в строке формата, но сама строка формата НЕ передается. (Я, вероятно, сейчас просто запрещу "%s" элементы ...) Вернувшись в мое местоположение, у нас будет программа, которая ищет уникальные идентификаторы в таблице, находит соответствующую строку формата и использует ее для восстановления исходное сообщение отладки.

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

Ограничения:

  • Чтобы уточнить, я имею здесь дело с C / C ++, и меня не интересует реализация заменителя printf (), полностью завершенная на 100% - такие вещи, как строки не буквального формата, %s (строковые) спецификаторы формата или более сложные спецификаторы формата, такие как добавление ширины или точности в список varargs с помощью %*.*d, не должны поддерживаться.

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

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

Спасибо!

Ответы [ 4 ]

3 голосов
/ 02 августа 2011

Я видел только эту идею, реализованную с предопределенным набором строк. Код будет выглядеть как debug_print(INSIDE_LOOP_MSG_ID, i, n). Когда разработчики хотят добавить новые сообщения, им придется поместить новый текст в определенный заголовочный файл и присвоить ему новый идентификатор.

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

Одной из идей может быть макрос / шаблон, который превращает первый строковый аргумент в хеш-значение во время компиляции . Поэтому разработчик пишет debug_print("test %d",i), который компилируется в debug_port_send(0x1d3s, i). Написание сценария постобработки для извлечения строк и хэшей для использования на стороне получения должно быть простым. (Простейшим способом разрешения коллизий хеш-функции было бы дать сообщение об ошибке и заставить пользователя немного изменить формулировку).

редактирование:
Поэтому я попробовал это с хешем времени компиляции по ссылке выше.

#define QQuot_(x) #x
#define QQuote(x) QQuot_(x)
#define Debug_Print(s, v) (Send( CONSTHASH(QQuote(__LINE__)##s), *((long*)&(v))))

void Send(long hash, long value)
{
   printf("Sending %x %x\n", hash, value); //replace with COMMS
}


int main()
{
   int i = 1;
   float f= 3.14f;
   Debug_Print("This is a test %d", i);
   i++;
   Debug_Print("This is a test %d", i);
   Debug_Print("This was test %f", f);
}

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

Sending 94b7555c 1
Sending 62fce13e 2
Sending 506e9a0c 4048f5c3

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

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

1 голос
/ 11 августа 2011

Я видел что-то, что выполняет нечто подобное на платформе ARM. Я считаю, что это называется « Embedded Trace Macrocell ». Серия макросов преобразует операторы типа TRACE_POWER_SYSTEM_VOLTAGE_REGULATOR_TRIGGER(inputX); в две регистровые записи в регистры ETM. Обратите внимание, что это ТОЛЬКО принимает 16-битные, 32-битные и 64-битные целые числа в качестве аргументов.

Мы можем использовать инструменты ARM для извлечения этих буферов с временными метками. Затем мы применяем предварительно скомпилированный фрагмент хитрости для преобразования первой (индексной) записи в регистр в выходной файл, который выглядит следующим образом:

timestamp  | POWER SYSTEM    |    VOLTAGE REGULATOR TRIGGER    | 0x2380FF23

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

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

Обратите внимание, что при анализе трассировки чрезвычайно важно, чтобы вы использовали только файл 'decode', который соответствует конкретной версии кода, выполняемого на устройстве.

0 голосов
/ 28 июля 2013

У меня была та же проблема, плюс я хотел уменьшить размер изображения (из-за крошечной встроенной вспышки). Мое решение заключается в отправке имени файла и строки (которая должна быть 14-20 байт) и наличие анализатора исходного кода на стороне сервера, который будет генерировать карту фактических текстов. Таким образом, фактический код не будет содержать строк «format», а будет иметь одну строку «filename» для каждого файла. Кроме того, имена файлов можно легко заменить на enum (в отличие от замены каждой строки в коде), чтобы уменьшить пропускную способность COMM.

Я надеюсь, что пример псевдо-кода поможет прояснить идею:

/* target code */
#define PRINT(format,...) send(__FILE__,__LINE__,__VA_ARGS__)
...

/* host code (c++) */
void PrintComm(istream& in)
{
    string fileName;
    int    line,nParams;
    int*   params;
    in>>fileName>>line>>nParams;
    if (nParams>0)
    {
        params = new int[nParams];
        for (int i=0; i<nParams; ++i)
            in>>params[i];
    }
    const char* format = FindFormat(fileName,line);
    ...
    delete[] params;
}
0 голосов
/ 11 августа 2011

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

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