Отладка сжатой статической переменной в C (gdb не работает?) - PullRequest
3 голосов
/ 17 июня 2009

Я много занимался программированием, но немного на C, и мне нужен совет по отладке. У меня есть статическая переменная (область видимости файла), которая блокируется примерно через 10-100 секунд выполнения многопоточной программы (используя pthreads в OS X 10.4). Мой код выглядит примерно так:

static float some_values[SIZE];
static int * addr;

addr указывает на действительный адрес памяти на некоторое время, а затем получает некоторое значение (иногда 0, иногда ненулевое), вызывая, таким образом, segfault при разыменовании. Изучая gdb, я убедился, что addr размещается в памяти сразу после some_values, как и следовало ожидать, поэтому мое первое предположение состояло бы в том, что я использовал индекс «вне границ» для записи в some_values. Однако это крошечный файл, поэтому легко проверить, что это не проблема.

Очевидным методом отладки будет установка точки наблюдения для переменной addr. Но это, похоже, приводит к неустойчивому и необъяснимому поведению в gdb. Точка наблюдения срабатывает при первом назначении addr; затем, после продолжения выполнения, я немедленно получаю бессмысленную ошибку сегмента в другом потоке ... предположительно, ошибку сегмента при обращении к адресу статической переменной в другой части программы! Но тогда gdb позволяет мне читать и писать по этому адресу памяти в интерактивном режиме.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x001d5bd0
0x0000678d in receive (arg=0x0) at mainloop.c:39
39          sample_buf_cleared ++;
(gdb) p &sample_buf_cleared
$17 = (int *) 0x1d5bd0
(gdb) p sample_buf_cleared
$18 = 1
(gdb) set sample_buf_cleared = 2
(gdb) 

gdb явно в замешательстве. Кто-нибудь знает почему? Или у кого-нибудь есть предложения по устранению этой ошибки без использования точек наблюдения?

Ответы [ 6 ]

3 голосов
/ 17 июня 2009
  1. Вы можете поместить массив значений uint между some_values ​​и addr и определить, не превышаете ли вы some_values ​​или не влияет ли повреждение на большее количество адресов, чем вы думали. Я бы инициализировал заполнение для DEADBEEF или другого очевидного паттерна, который легко различить и вряд ли произойдет в программе. Если значение в заполнении изменяется, приведите его к плавающему и посмотрите, имеет ли число значение как плавающее.

static float some_values ​​[SIZE]; статическое заполнение без знака int [1024]; static int * addr;

  1. Запустите программу несколько раз. При каждом запуске отключайте разные потоки и смотрите, когда проблемы исчезнут.

  2. Установите привязку процесса программ к одному ядру, а затем попробуйте точку наблюдения. Возможно, вам повезет больше, если у вас нет двух потоков, одновременно изменяющих значение. ПРИМЕЧАНИЕ. Это решение не препятствует этому. Это может облегчить отлов в отладчике.

2 голосов
/ 17 июня 2009

static переменные и многопоточность обычно не смешиваются.

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

Вам либо нужно:

  • создать отдельные экземпляры addr для каждого потока; или
  • обеспечивает некоторую синхронизацию вокруг addr, чтобы остановить два потока, изменяющих значение одновременно.
1 голос
/ 17 июня 2009

Единственное, что вы можете попробовать, - это создать отдельный поток, единственная цель которого - следить за значением addr и прерывать его при изменении. Например:

static int * volatile addr;  // volatile here is important, and must be after the *
void *addr_thread_proc(void *arg)
{
    while(1)
    {
        int *old_value = addr;
        while(addr == old_value) /* spin */;
        __asm__("int3");  // break the debugger, or raise SIGTRAP if no debugger
    }
}
...
pthread_t spin_thread;
pthread_create(&spin_thread, NULL, &addr_thread_proc, NULL);

Затем, когда изменяется значение addr, запускается инструкция int3, которая прерывает отладчик и останавливает все потоки.

1 голос
/ 17 июня 2009

Попробуйте использовать valgrind; Я не пробовал valgrind на OS X, и я не понимаю вашей проблемы, но «попробуйте valgrind» - первое, о чем я думаю, когда вы говорите «забит».

0 голосов
/ 18 июня 2009

Я не выполнял никакой отладки в OSX, но я видел такое же поведение в GDB в Linux: происходит сбой программы, но GDB может читать и записывать память, которую программа только что пыталась прочитать / записать безуспешно.

Это не обязательно означает, что GDB запутан; Скорее, ядро ​​позволило GDB читать / записывать память через ptrace (), который нижестоящему процессу не разрешено читать или записывать. IOW, это была (недавно исправленная) ошибка в ядре.

Тем не менее, похоже, что точки наблюдения GDB не работают для вас по любой причине.

Один из методов, который вы могли бы использовать, - это mmap пробел для some_values, а не статическое выделение для них пространства, расположение массива до end на границе страницы и расположение следующей страницы для быть недоступным (через mprotect).

Если какой-либо код попытается получить доступ после конца some_values, он получит исключение (по сути, вы устанавливаете «недоступную для записи» «точку наблюдения» сразу после some_values).

0 голосов
/ 17 июня 2009

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

...