Помимо тех вещей, которые вы добавляете к своему коду для времени компиляции, можно также добавить дополнительный код для обеспечения возможностей отладки времени исполнения. Это особенно полезно для приложений, которые устанавливаются в полевых условиях, когда в системе клиента не будет установленного исходного кода (а может быть, даже и компилятора!)
В данном разделе представлены некоторые методики отладки, которые мы использовали в течение ряда лет, от простых до более сложных. Обратите внимание, что наше рассмотрение ни в коем случае не является исчерпывающим. Это область, в которой стоит иметь некоторое воображение и использовать его!
15.4.2.1. Добавляйте отладочные опции и переменные
Простейшей методикой является наличие опции командной строки, делающих возможным отладку. Такая опция может быть условно откомпилированной для отладки. Однако более гибким подходом является оставить опцию в
Если ваша программа большая, отладочная опция может принимать аргумент, указывающий, какую подсистему следует отлаживать. На основе этого аргумента можно установить различные флаговые переменные или, возможно, различные флаговые биты в одной отладочной переменной. Вот схема этой методики:
struct option options[] = {
...
{ "debug", required_argument, NULL, 'D' },
...
};
int main(int argc, char **argv) {
int c;
while ((c = getopt_long(argc, argv, "...D:")) != -1) {
switch (c) {
...
case 'D':
parse_debug(optarg);
break;
...
}
}
...
}
Функция parse_debug()
считывает строку аргументов. Например, это может быть строка разделенных запятыми или пробелами подсистем, вроде "file,memory,ipc
". Для каждого действительного имени подсистемы функция устанавливает бит в отладочной переменной:
extern int debugging;
void parse_debug(const char *subsystems) {
char *sp;
for (sp = subsystems; *sp != '\0';) {
if (strncmp(sp, "file", 4) == 0) {
debugging |= DEBUG_FILE;
sp += 4;
} else if (strncmp(sp, "memory", 6) == 0) {
debugging |= DEBUG_MEM;
sp += 6;
} else if (strncmp(sp, "ipc", 3) == 0) {
debugging |= DEBUG_IPC;
sp += 3;
...
}
while (*sp == ' ' || *sp == ',') sp++;
}
}
В конечном счете код приложения может затем проверить флаги:
if ((debugging & DEBUG_FILE) != 0) ...
/* В части программы для ввода/вывода */
if ((debugging & DEBUG_MEM) != 0) ... /* В менеджере памяти */
Использовать ли одну переменную с флаговыми битами, различные переменные или даже отладочный массив, индексируемый именованными константами (желательно из enum
), зависит от вас.
Ценой оставления отладочного кода в исполняемом файле изделия является увеличение размера программы. В зависимости от размещения отладочного кода он может быть также более медленным, поскольку каждый раз осуществляются проверки, которые все время оказываются ложными, пока не будет включен режим отладки. И, как упоминалось, кто-нибудь может изучить вашу программу, что может быть неприемлемым для вас. Или еще хуже, недоброжелательный пользователь может включить столько отладочных возможностей, что программа замедлится до невозможности работать с ней! (Это называется
Преимуществом, которое может быть большим, является то, что вашу уже установленную программу можно запустить с включенным режимом отладки без необходимости сначала построить, а затем загрузить специальную версию на сайт заказчика. Когда программное обеспечение установлено в удаленных местах, в которых может не быть людей и все, что вы можете сделать, это получить удаленный доступ к системе через Интернет (или, еще хуже, через медленное модемное соединение!), такая возможность может оказаться спасительным средством.