355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Арнольд Роббинс » Linux программирование в примерах » Текст книги (страница 50)
Linux программирование в примерах
  • Текст добавлен: 6 мая 2017, 11:00

Текст книги "Linux программирование в примерах"


Автор книги: Арнольд Роббинс



сообщить о нарушении

Текущая страница: 50 (всего у книги 55 страниц)

15.5. Отладочные инструменты

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

Имеются коммерческие инструменты, которые делают множество (или все) из тех вещей, что и описываемые нами программы, но не все они доступны для GNU/Linux, а многие довольно дороги. Все пакеты, обсуждающиеся в данном разделе, являются свободно доступными.

15.5.1. Библиотека dbug – усовершенствованный printf()

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

Библиотека dbug, написанная Фредом Фишем (Fred Fish) в начале 1980-х, была с тех пор несколько усовершенствована. Теперь она явным образом является общим достоянием, поэтому ее можно использовать без всяких проблем как в свободном, так и частном программном обеспечении. Она доступна через архив FTP Фреда Фиша[175]175
  ftp://ftp.ninemoons.corn/pub/dbug/Примеч. автора.


[Закрыть]
как в виде сжатого файла tar, так и в виде архива ZIP. Документация хорошо резюмирует dbug:

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

Пакет dbug лишь незначительно снижает скорость выполнения программ, обычно значительно менее 10%, и немного увеличивает их размеры, обычно от 10 до 20%. Определив особый идентификатор препроцессора С, можно снизить оба этих показателя до нуля без необходимости изменений в исходном коде.

Следующий список является кратким изложением возможностей пакета dbug. Каждую возможность можно отдельно включать или отключать во время запуска программы, указав соответствующие аргументы командной строки.

• Трассировка исполнения, отображающая уровень потока управления полуграфическим способом с использованием отступов, обозначающих глубину вложения

• Вывод значений всех или любого набора ключевых внутренних переменных.

• Ограничение действий определенным набором указанных функций.

• Ограничение трассировки функций указанной глубиной вложения.

• Пометку каждой выводимой строки названием исходного файла и номером строки.

• Пометку каждой выводимой строки названием текущего процесса.

• Сохранение в стеке или восстановление состояния отладки для обеспечения исполнения со встроенными значениями по умолчанию для отладки.

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

Пакет dbug требует от вас использования определенного порядка при написании своего кода. В частности, нужно использовать его макросы при возвращении из функции или вызове setjmp() и longjmp(). Нужно добавлять один вызов макроса в качестве первого исполняемого оператора каждой функции и вызвать несколько дополнительных макросов из main(). Наконец, нужно добавить отладочную опцию командной строки, по соглашению, это -#, которая редко используется в качестве действительной опции, если вообще используется. В обмен на дополнительную работу вы получаете все только что очерченные преимущества. Давайте взглянем на пример в руководстве:

1  #include

2  #include "dbug.h"

3

4  int

5  main(argc, argv)

6  int argc;

7  char *argv[];

8  {

9   register int result, ix;

10  extern int factorial(), atoi();

11

12  DBUG_ENTER("main");

13  DBUG_PROCESS(argv[0]);

14  DBUG_PUSH_ENV("DBUG");

15  for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) {

16   switch (argv[ix][1]) {

17   case '#':

18    DBUG_PUSH(&(argv[ix][2]));

19    break;

20   }

21  }

22  for (; ix < argc; ix++) {

23   DBUG_PRINT("args", ("argv[%d] = %s", ix, argv[ix]));

24   result = factorial(atoi(argv(ixj));

25   printf("%dn", result);

26   fflush(stdout);

27  }

28  DBUG_RETURN(0);

29 }

Эта программа иллюстрирует большинство важных моментов. Макрос DBUG_ENTER() (строка 12) должен быть вызван после объявлений переменных и перед любым другим кодом. (Это потому, что он сам объявляет несколько частных переменных.[176]176
  В C99, который допускает смешивание объявлений переменных с исполняемым кодом, это составляет меньшую проблему, но помните, что этот пакет был разработан для K&R С – Примеч. автора.


[Закрыть]
)

Макрос DBUG_PROCESS() (строка 13) устанавливает имя программы, главным образом, для использования в выводимых библиотекой сообщениях. Этот макрос должен вызываться лишь однажды, из main().

Макрос DBUG_PUSH_ENV() (строка 14) заставляет библиотеку проверить указанную переменную окружения (в данном случае DBUG) на предмет управляющей строки (Управляющие строки dbug вскоре будут рассмотрены.) Библиотека может, сохранив свое текущее состояние и использовав новое, создавать стек сохраненных состояний. Таким образом, этот макрос помещает в стек сохраненных состояний полученное от данной переменной окружения состояние. В данном примере использован случай, когда макрос создает первоначальное состояние. Если такой переменной окружения нет, ничего не происходит. (В качестве отступления, DBUG является довольно общей переменной, возможно, GAWK_DBUG было бы лучше [для gawk].)

Макрос DBUG_PUSH (строка 18) передает значение управляющей строки, полученной из опции командной строки -#. (Новый код должен использовать getopt() или getopt_long() вместо ручного анализа аргументов.) Таким образом обычно включается режим отладки, но использование переменной окружения предоставляет также дополнительную гибкость.

Макрос DBUG_PRINT() (строка 23) осуществляет вывод. Второй аргумент использует методику, которую мы описали ранее (см. раздел 15.4.1.1 «Используйте отладочные макросы»), по включению в скобки всего списка аргументов printf(), делая его простым аргументом, насколько это касается препроцессора С. Обратите внимание, что завершающий символ конца строки в форматирующей строке не указывается; библиотека dbug вставляет его за вас.

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

Наконец, макрос DBUG_RETURN() (строка 28) используется вместо обычного оператора return для возврата значения. Для использования с функциями void имеется соответствующий макрос DBUG_VOID_RETURN.

Оставшаяся часть программы заполнена функцией factorial():

1  #include

2  #include "dbug.h"

3

4  int factorial (value)

5  register int value;

6  {

7   DBUG_ENTER("factorial");

8   DBUG_PRINT("find", ("find %d factorial", value));

9   if (value > 1) {

10   value *= factorial(value – 1);

11  }

12  DBUG_PRINT("result", ("result is %d", value));

13  DBUG_RETURN(value);

14 }

Когда программа откомпилирована и скомпонована вместе с библиотекой dbug, ее можно запустить обычным способом. По умолчанию, программа не создает вывод отладки. Но со включенной отладкой доступны различные виды вывода:

$ factorial 1 2 3 /* Обычный запуск, без отладки */

1

2

6

$ factorial -#t 1 2 3/* Вывести трассировку вызовов функций, обратите внимание на вложенность */

| >factorial

|

1 /* Обычный вывод в stdout */

| >factorial

| | >factorial

| |

|

2

| >factorial

| | >factorial

| | | >factorial

| | |

| |

|

6

$ factorial -#d 1 2/* Показать отладочные сообщения DBUG_PRINT() */

?func?: args: argv[2] = 1

factorial: find: find 1 factorial

factorial: result: result is 1

1

?func?: args: argv[3] = 2

factorial: find: find 2 factorial

factorial: find: find 1 factorial

factorial: result: result is 1

factorial: result: result is 2

2

Опция -# управляет библиотекой dbug. Она «особая» в том смысле, что DBUG_PUSH() будет принимать всю строку, игнорируя ведущие символы '-#', хотя вы могли бы использовать при желании другую опцию, передав DBUG_PUSH() лишь строку аргументов опций (если вы используете getopt(), это optarg).

Управляющая строка состоит из набора опций и аргументов. Каждая группа опций и аргументов отделяется от других символом двоеточия. Каждая опция представлена одной буквой, а аргументы этой опции отделяются от нее запятыми. Например:

$ myprog -#d,mem,ipc:f,check_salary,check_start_date -f infile -o outfile

Опция d включает вывод DBUG_PRINT(), но лишь если первая строка аргумента является "mem" или "ipc". (Если аргументов нет, выводятся все сообщения DBUG_PRINT().) Сходным образом опция f ограничивает трассировку вызовов функций лишь указанными функциями, check_salary() и check_start_date().

Следующий список опций и аргументов воспроизведен из руководства библиотеки dbug. Квадратные скобки заключают необязательные аргументы. Мы включаем здесь лишь те, которые находим полезными; полный список см. в документации.

d [,ключевые слова]

Разрешает вывод от макросов с указанными ключевыми словами. Пустой список ключевых слов предполагает, что выбраны все ключевые слова.

F

Помечает каждую строку вывода отладки именем исходного файла, содержащего макрос, осуществляющий вывод.

i

Идентифицирует процесс, выводящий каждую отладочную или трассировочную строку номером ID для этого процесса.

L

Помечает каждую строку вывода отладчика номером строки исходного файла, в котором находится осуществляющий вывод макрос.

о[,файл]

Перенаправляет поток вывода отладчика в указанный файл. Потоком вывода по умолчанию является stderr. Пустой список аргументов перенаправляет вывод в stdout.

t[,N]

Включает трассировку потока управления функций. Максимальная глубина вложения определяется N, по умолчанию используется 200.

Для завершения нашего обсуждения вот остальные макросы, определенные библиотекой dbug.

DBUG_EXECUTE(строка, код)

Этот макрос похож на DBUG_PRINT(): первый аргумент является строкой, выбранной с помощью опции d, а второй – код для исполнения:

DBUG_EXECUTE("abort", abort());

DBUG_FILE

Это значение типа FILE* для использования с процедурами . Оно позволяет осуществлять собственный вывод в поток файла отладки.

DBUG_LONGJMP(jmp_buf env, int val)

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

DBUG_POP()

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

DBUG_SETJMP(jmp_buf env)

Этот макрос заключает в оболочку вызов setjmp(), принимая те же самые аргументы. Он позволяет библиотеке dbug обрабатывать нелокальные переходы.

В другом воплощении, в первой начинающей компании, для которой мы работали[177]177
  Хотя нам следовало бы усвоить свой урок после первой компании, мы перешли ко второй. С тех пор, как мы это выяснили, мы обычно избегаем начинающие компании. Ваша выгода, конечно, может меняться – Примеч. автора.


[Закрыть]
, мы использовали в своем продукте библиотеку dbug. Она была неоценимой при разработке, а опустив -DDBUG в конечной сборке, мы смогли построить готовую версию без других изменений исходного кода.

Чтобы извлечь максимальную выгоду от библиотеки dbug, нужно использовать ее последовательно, по всей программе. Это проще, если вы используете ее с начала проекта, но в качестве эксперимента мы обнаружили, что с помощью простого сценария awk мы смогли включить библиотеку в программу с 30 000 строк кода за несколько часов работы. Если вы можете позволить себе накладные расходы, лучше всего оставить ее в конечной сборке вашей программы, чтобы можно было ее отлаживать без необходимости предварительной перекомпиляции.

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

15.5.2. Отладчики выделения памяти

Игнорируя такие проблемы, как плохой дизайн программы, для любого крупномасштабного практического приложения единственной сложной задачей программиста на С является управление динамической памятью (посредством malloc(), realloc() и free()).

Этот факт подкреплен большим количеством инструментов, доступных для отладки динамической памяти. Имеется значительное перекрывание того, что предлагают данные утилиты. Например:

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

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

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

• Обнаружение использования уже освобожденной памяти: память, которая освобождена, используется через висячий указатель.

• Обнаружение выхода за границы выделенной памяти: получение доступа или сохранение в память за пределами выделенной границы.

• Предупреждение об использовании неинициализированной памяти. (Многие компиляторы могут выдавать такие предупреждения.)

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

• Управление инструментами посредством использования переменных окружения.

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

Некоторые утилиты просто записывают эти события. Другие организуют жуткое завершение программы приложения (посредством SIGSEGV), чтобы на код-нарушитель можно было точно указать из отладчика. Вдобавок, большинство спроектированы для работы вместе с GDB.

Некоторые инструменты требуют изменения исходного кода, такого, как вызов специальных функций или использование особого заголовочного файла, дополнительных #define и статической библиотеки. Другие работают посредством использования специального механизма библиотек общего пользования Linux/Unix для прозрачной установки себя в качестве заместителя стандартных библиотечных версий malloc() и free().

В данном разделе мы рассмотрим три отладчика динамической памяти, а затем предоставим ссылки на несколько других.

15.5.2.1. GNU/Linux mtrace

Системы GNU/Linux, использующие GLIBC, предоставляют две функции для включения и отключения трассировки памяти во время исполнения.

#include /* GLIBC */

void mtrace(void);

void muntrace(void);

Когда вызывается mtrace(), библиотека проверяет переменную окружения MALLOC_TRACE. Ожидается, что она указывает на записываемый файл (существующий или нет). Библиотека открывает файл и начинает записывать сведения о выделениях и освобождениях памяти (Если файл не может быть открыт, запись не производится. Файл урезается каждый раз при запуске программы.) Когда вызывается muntrace(), библиотека закрывает файл и больше не регистрирует выделения и освобождения.

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

Когда приложение завершается, вы используете программу mtrace для анализа файла журнала. (Файл журнала в формате ASCII, но информацию нельзя использовать непосредственно.) Например, gawk включает трассировку, если определена TIDYMEM:

$ export TIDYMEM=1 MALLOC_TRACE=trace.out /* Экспортировать переменные окружения */

$ ./gawk 'BEGIN { print "hello, world" }' /* Запустить программу */

hello, world

$ mtrace ./gawk mtrace.out /* Создать отчет */

Memory not freed:

Address Size Caller

0x08085858 0x20  at /home/arnold/Gnu/gawk/gawk-3.1.3/main.c:1102

0x08085880 0xc80 at /home/arnold/Gnu/gawk/gawk-3.1.3/node.c:398

0x08086508 0x2   at /home/arnold/Gnu/gawk/gawk-3.1.3/node.c:337

0x08086518 0x6   at /home/arnold/Gnu/gawk/gawk-3.1.3/node.c:337

0x08086528 0x10  at /home/arnold/Gnu/gawk/gawk-3.1.3/eval.c:2082

0x08086550 0x3   at /home/arnold/Gnu/gawk/gawk-3.1.3/node.с:337

0x08086560 0x3   at /home/arnold/Gnu/gawk/gawk-3.1.3/node.c:337

0x080865e0 0x4   at /home/arnold/Gnu/gawk/gawk-3.1.3/field.c:76

0x08086670 0x78  at /home/arnold/Gnu/gawk/gawk-3.1.3/awkgram.y:1369

0x08086700 0xe   at /home/arnold/Gnu/gawk/gawk-3.1.3/node.c:337

0x08086718 0x1f  at /home/arnold/Gnu/gawk/gawk-3.1.3/awkgram.y:1259

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

15.5.2.2. Electric Fence

В разделе 3.1 «Адресное пространство Linux/Unix» мы описали, как динамическая память выделяется из кучи, которая может расти и сокращаться (с помощью вызовов brk() или sbrk(), описанных в разделе 3.2.3 «Системные вызовы: brk() и sbrk()»).

Ну, картина, которую мы там представили, является упрощением действительности. Более развитые системные вызовы (не рассматриваемые в данной книге) позволяют добавлять в адресное пространство процесса дополнительные, необязательно смежные сегменты памяти. Многие отладчики malloc() работают с использованием этих системных вызовов для добавления новых областей адресного пространства при каждом выделении. Преимуществом этой схемы является то, что операционная система и аппаратное обеспечение защиты памяти компьютера взаимодействуют для обеспечения недействительности доступа к памяти за пределами этих изолированных сегментов, генерируя сигнал SIGSEGV. Эта схема изображена на рис. 15.1.

Рис. 15.1. Адресное пространство Linux/Unix, включая специальные области

Первым пакетом отладки, реализовавшим эту схему, был Electric Fence. Electric Fence является вставляемым заместителем для malloc() и др. Он работает на многих системах Unix и GNU/Linux; он доступен с FTP архива его авторов.[178]178
  ftp://ftp.perens.com/pub/ElectricFenceПримеч. автора.


[Закрыть]
Он поставляется также со многими дистрибутивами GNU/Linux, хотя, возможно, вам придется выбрать ею явным образом при установке системы.

После компоновки программы с Electric Fence любой доступ за пределами выделенной памяти генерирует SIGSEGV. Electric Fence также перехватывает попытки использования уже освобожденной памяти. Вот простая программа, которая иллюстрирует обе проблемы.

1  /* ch15-badmem1.с – плохо обращается с памятью */

2

3  #include

4  #include

5

6  int main(int argc, char **argv)

7  {

8   char *p;

9   int i;

10

11  p = malloc(30);

12

13  strcpy(p, "not 30 bytes");

14  printf("p = <%s>n", p);

15

16  if (argc ==2) {

17   if (strcmp(argv[1], "-b") == 0)

18    p[42] = 'a'; /* коснуться за пределами границы */

19   else if (strcmp(argv[1], "-f") == 0) {

20    free(p); /* освободить память, затем использовать ее */

21    p[0] = 'b';

22   }

23  }

24

25  /* освобождение (p); */

26

27  return 0;

28 }

Эта программа осуществляет простую проверку опций командной строки, чтобы решить, как вести себя плохо: -b вызывает доступ к памяти за ее выделенными страницами, а -f пытается использовать освобожденную память. (Строки 18 и 21 являются соответственно опасными.) Обратите внимание, что без опций указатель никогда не освобождается (строка 25), Electric Fence не перехватывает этот случай.

Одним из способов использования Electric Fence, способом, который гарантированно работает на различных системах Unix и GNU/Linux, является статическая компоновка с ним вашей программы. Затем программа должна быть запущена из отладчика. (Документация Electric Fence явно указывает, что Electric Fence не следует компоновать с двоичным файлом готового изделия.) Следующий сеанс демонстрирует эту процедуру и показывает, что происходит для обеих опций командной строки:

$ cc -g ch15-badmem1.c -lefence -о ch15-badmem1 /* Откомпилировать; компоновка статическая */

$ gdb ch15-badmem1 /* Запустить из отладчика */

GNU gdb 5.3

...

(gdb) run -b /* Попробовать опцию -b */

Starting program: /home/arnold/progex/code/ch15/ch15-badmem1 -b

[New Thread 8192 (LWP 28021)]

Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens

p =

Program received signal SIGSBGV, Segmentation fault.

SIGSBGV: GDB prints where

[Switching to Thread 8192 (LWP 28021)]

0x080485b6 in main (argc=2, argv=0xbffff8a4) at ch15-badmem1.c:18

18 p[42] = 'a'; /* коснуться за пределами границы */

(gdb) run -f /* Теперь попробовать опцию -f */

The program being debugged has been started already.

Start it from the beginning? (y or n) y /* Да */

Starting program: /home/arnold/progex/code/ch15/ch15-badmem1 -f

[New Thread 8192 (LWP 28024)]

Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens

p =

Program received signal SIGSEGV, Segmentation fault. /* Снова SIGSEGV */

[Switching to Thread 8192 (LWP 28024)]

0x080485e8 in main (argc=2, argv=0xbffff8a4) at ch15-badmem1.c:21

21 p[0] = 'b';

На системах, которые поддерживают разделяемые библиотеки и переменную окружения LD_PRELOAD (в том числе и на GNU/Linux), вам не нужно явным образом компоновать библиотеку efence. Вместо этого сценарий оболочки ef организует запуск программы с соответствующей настройкой.

Хотя мы не описали механизмы подробно, GNU/Linux (и другие системы Unix) поддерживают разделяемые (shared) библиотеки, особые версии библиотечных процедур, которые хранятся в одном файле на диске, вместо того, чтобы копироваться в каждый отдельный двоичный исполняемый файл программы. Разделяемые библиотеки экономят дисковое пространство и могут сохранить системную память, поскольку все программы, использующие разделяемые библиотеки, используют одну и ту же копию библиотеки в памяти. Платой за это является замедление загрузки программы, поскольку программу и разделяемую библиотеку нужно подключить друг к другу прежде, чем программа сможет начать выполнение. (Обычно это прозрачно для вас, пользователя.)

Переменная окружения LD_PRELOAD заставляет системный загрузчик программ (который загружает исполняемые файлы в память) связаться со специальной библиотекой до стандартных библиотек. Сценарий ef использует эту особенность для связывания набора функций malloc() в Electric Fence.[179]179
  GDB также позволяет определить переменную окружения LD_PRELOAD: set environment LD_PRELOAD=PATH_TO_YOUR_LIBRARY – Примеч. науч. ред.


[Закрыть]
Таким образом, повторная компоновка даже не нужна. Этот пример демонстрирует ef:

$ cc -g ch15-badmem1.c -о ch15-badmem1 /* Компилировать как обычно */

$ ef ch15-badmem1 -b /* Запустить с использованием ef, создает дамп ядра */

Electric Fence 2.2.0 Copyright (С) 1987-1999 Bruce Perens

p =

/usr/bin/ef: line 20: 28005 Segmentation fault (core dumped)

( export LD_PRELOAD=libefence.so.0.0; exec $* )

$ ef ch15-badmem1 -f /* Запустить с использованием ef, снова создает дамп ядра */

Electric Fence 2.2.0 Copyright (С) 1987-1999 Bruce Perens

p =

/usr/bin/ef: line 20: 28007 Segmentation fault (core dumped)

( export LD_PRELOAD=libefence.so.0.0; exec $* )

$ ls -l core* /* Linux создает для нас разные файлы core */

-rw– 1 arnold devel 217088 Aug 28 15:40 core.28005

-rw– 1 arnold devel 212992 Aug 28 15:40 core.28007

GNU/Linux создает файлы core, которые включают в свое имя ID процесса. В данном случае такое поведение полезно, поскольку мы можем отдельно отлаживать каждый файл core:

$ gdb ch15-badmem1 core.28005 /* От опции -b */

GNU gdb 5.3

...

Core was generated by 'ch15-badmem1 -b'.

Program terminated with signal 11, Segmentation fault.

...

#0 0x08048466 in main (argc=2, argv=0xbffff8c4) at ch15-badmem1.c:18

18 p[42] = 'a'; /* touch outside the bounds */

(gdb) quit

$ gdb ch15-badmem1 core.28007 /* От опции -f */

GNU gdb 5.3

...

Core was generated by 'ch15-badmem1 -f'.

Program terminated with signal 11, Segmentation fault.

...

#0 0x08048498 in main (argc=2, argv=0xbffff8c4) at ch15-badmem1.с:21

21 p[0] = 'b';

Справочная страница efence(3) описывает несколько переменных окружения, которые должны быть установлены, чтобы настроить поведение Electric Fence. Следующие три наиболее примечательны.

EF_PROTECT_BELOW

Установка этой переменной в 1 заставляет Electric Fence проверять «недоборы» (underruns) вместо «переборов» (overruns) при выходе за пределы отведенной памяти. «Перебор», т.е. доступ к памяти в области за выделенной, был продемонстрирован ранее. «Недобор» является доступом к памяти, расположенной перед выделенной областью памяти.

EF_PROTECT_FREE

Установка этой переменной в 1 предотвращает повторное использование Electric Fence памяти, которая была корректно освобождена. Это полезно, когда вы думаете, что программа может получать доступ к освобожденной памяти; если освобожденная память впоследствии была выделена заново, доступ к ней через предыдущий висячий указатель остался бы в противном случае незамеченным.

EF_ALLOW_MALLOC_0

При наличии ненулевого значения Electric Fence допускает вызовы 'malloc(0)'. Такие вызовы в стандартном С технически действительны, но могут представлять программную ошибку. Соответственно Electric Fence по умолчанию их запрещает.

Вдобавок к переменным окружения Electric Fence предоставляет глобальные переменные с такими же названиями. Вы можете изменить их значения из отладчика, так что можно динамически изменять поведение программы, которая уже начала выполнение. Подробности см. в efence(3).


    Ваша оценка произведения:

Популярные книги за неделю

    wait_for_cache