Почему и как мы перенаправляем операторы отладки? - PullRequest
4 голосов
/ 30 декабря 2011

Я хочу знать, почему предпочтительно перенаправлять операторы отладки в stderr, как это делается здесь:

#ifdef DEBUG_TEST
  #define DEBUG_TEST 1
  #else
  #define DEBUG_TEST 0
  #endif

  #define DEBUG_PRINT(fmt, ...) \
             do { if (DEBUG_TEST) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)

Также: Как мы можем перенаправить эти операторы отладки в отдельный файл журнала с отметкой временив этом файле?Я хочу сделать это, используя макрос в моем коде.

Платформа: Linux, компилятор gcc

Ответы [ 4 ]

3 голосов
/ 30 декабря 2011

Преимущество использования stderr перед stdout заключается в том, что если вы перенаправляете вывод в файл или передаете его (используя канал) другому процессу, сообщения отладки не мешают.

Если выЕсли вы хотите перенаправить stderr в файл в Unix, вы можете запустить свою программу следующим образом:

./program 2>logfile
2 голосов
/ 30 декабря 2011

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

Вы можете использовать версию командной строки для перенаправления stderr, как подсказывает thesamet.Чтобы получить временную метку в имени файла, вы можете сделать что-то вроде этого при запуске вашей программы:

./program 2>"logfile-`date`.txt"

Если вы хотите сделать это в самой программе, один из способов - просто использовать fopen, чтобы открыть другую.подать и написать в это.Вот полностью рабочий пример, с которым вы можете поиграть:

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#define DEBUG_TEST true

FILE *debug_file;

#define DEBUG_PRINT(fmt, ...) \
        do { if (DEBUG_TEST) fprintf(debug_file, fmt, ##__VA_ARGS__); } while (false);



int main()
{
        time_t timestamp = time(NULL);
        char * s = malloc(snprintf(NULL, 0, "debug-%d.txt", timestamp));
        sprintf(s, "debug-%d.txt", timestamp);

        debug_file=fopen(s, "w");

        DEBUG_PRINT("YEAH\n");

        fclose(debug_file);

        return EXIT_SUCCESS;
}
1 голос
/ 30 декабря 2011

Простите, если я ошибаюсь, но похоже, что никто не упомянул буферизацию.

По умолчанию stdout является буферизованной строкой на большинстве платформ, а stderr не буферизирован.В основном это означает, что по умолчанию записи в стандартный вывод записываются в память и записываются в файл или консоль порциями, потому что выполнение вывода символа за символом является замедленным.

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

TL; DR при отладке печати на stderr, а не на stdout(другие люди могут предложить вместо этого войти в файл и / или разрешить параметры времени выполнения, такие как включение / отключение печати файла, стиля, порога и т. д.).

1 голос
/ 30 декабря 2011

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

Если вы хотите перенаправить вывод или добавить метки времени (или PID, или любую другую информацию), то не используйте fprintf() напрямую.Вызовите функцию своего собственного устройства, которая обрабатывает информацию, которую вы хотите, так, как вы хотите.

Таким образом, ваш макрос может быть:

extern void dbg_print(const char *fmt, ...);

#define DEBUG_PRINT(fmt, ...) \
         do { if (DEBUG_TEST) dbg_print(fmt, __VA_ARGS__); } while (0)

Или:

extern void dbg_print(const char *func, const char *file, int line, const char *fmt, ...);

#define DEBUG_PRINT(fmt, ...) \
         do { if (DEBUG_TEST) dbg_print(__func__, __FILE__, __LINE__, fmt, __VA_ARGS__); } while (0)

Это включает в себя имя функции, имя файла и номер строки в информации

Например, у меня есть довольно сложный пакет, который делает это.Одна из основных внутренних подпрограмм:

/* err_stdio - report error via stdio */
static void err_stdio(FILE *fp, int flags, int errnum, const char *format, va_list args)
{
    if ((flags & ERR_NOARG0) == 0)
        fprintf(fp, "%s: ", arg0);
    if (flags & ERR_STAMP)
    {
        char timbuf[32];
        fprintf(fp, "%s - ", err_time(timbuf, sizeof(timbuf)));
    }
    if (flags & ERR_PID)
        fprintf(fp, "pid=%d: ", (int)getpid());
    vfprintf(fp, format, args);
    if (flags & ERR_ERRNO)
        fprintf(fp, "error (%d) %s\n", errnum, strerror(errnum));
}

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

Если вы ограничиваете себя с помощью fprintf() непосредственно в макросе отладки, вы застряли с тем, что может fprintf() сделать, или перекомпилировать все.

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

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