GDB для отладки double free не обнаружен valgrind (?) - PullRequest
3 голосов
/ 12 сентября 2010

Примерно раз в три раза, когда я запускаю свою программу, malloc сообщает об ошибке двойного освобождения; например,

myprogram(703,0xb06d9000) malloc: *** error for object 0x17dd0240: double free
*** set a breakpoint in malloc_error_break to debug

Я запускал один и тот же код через valgrind более десятка раз, но он никогда не сообщает о двойном освобождении.

Я пропустил код через gdb с точкой останова на malloc_error_break и (при возникновении ошибки) об ошибке всегда сообщается в стандартной библиотечной функции c ++. Я изолировал родительскую функцию и запустил ее в тестовом модуле, но без ошибок.

Я думаю, что родительская функция / стандартная библиотека c ++ не виновата, она просто освобождает то, что выделено, но освобождается какая-то другая функция в родительской программе.

Я пытался посмотреть, какой объект освобожден дважды, но мои навыки работы с GDB не позволяют найти первый объект, который был освобожден. Пожалуйста, помогите мне найти, какой объект вызвал первую бесплатную и, кроме того, любую помощь, почему моя программа генерирует эту ошибку. Спасибо.

Родительская функция сводится к:

int i;
double px, py;
int start, finish;
std::string comment;
std::vector<double> x, y;

std::fstream myfile;
myfile.open("filename.txt", std::ios_base::in);

// Read header

std::getline(myfile, comment);

// Read data

while(!myfile.eof())
{
  myfile >> comment >> start >> comment >> finish;

  for(i = 0; i <= finish-start; i++)
  {
    myfile >> px >> py;  // double free here

    x.push_back(px);
    y.push_back(py);
  }
}

EDIT: Мой файл данных выглядит примерно так:

Comment: My Data
start 33 end 36
10.2 139.0076
9.22616 141.584
8.62802 141.083
8.87098 141.813
start 33 end 35
300.354 405
301.698 404.029
303.369 403.953
start 33 end 35
336.201 148.07
334.616 147.243
334.735 146.09

Обратный след от GDB

(gdb) backtrace
#0  0x93c2d4a9 in malloc_error_break ()
#1  0x93c28497 in szone_error ()
#2  0x93b52503 in szone_free ()
#3  0x93b5236d in free ()
#4  0x93b51f24 in localeconv_l ()
#5  0x93c18163 in strtod_l$UNIX2003 ()
#6  0x93c192e0 in strtod$UNIX2003 ()
#7  0x919b76e8 in std::__convert_to_v<double> ()
#8  0x919983cf in std::num_get<char, std::istreambuf_iterator<char, std::char_traits<char> > >::do_get ()
#9  0x91991671 in std::num_get<char, std::istreambuf_iterator<char, std::char_traits<char> > >::get ()
#10 0x9198d2dc in std::istream::operator>> ()

Просто повторюсь, мне нужна помощь, чтобы найти, какой объект был освобожден в первый раз, я не очень заинтересован в рефакторинге своего кода для этой функции - что, я не думаю, вызывает проблему; если только вы не найдете в нем что-то катастрофическое.

РЕДАКТИРОВАТЬ: Изменен пример кода.

Ответы [ 5 ]

4 голосов
/ 12 сентября 2010

Вы, похоже, используете Mac OSX (вы должны были обнародовать этот факт: -)

Есть несколько переменных среды , которые могут помочь вам отладить повреждение кучи.

В частности, MallocStackLoggingNoCompact выглядит очень многообещающе.

Вот что я вижу:

$ cat t.c
int main()
{
  char *p = strdup("hello");
  free(p);
  free(p);
  return 0;
}

$ gdb ./a.out
GNU gdb 6.3.50-20050815 (Apple version gdb-967) (Tue Jul 14 02:11:58 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for shared libraries ... done

(gdb) set env MallocStackLoggingNoCompact 1
(gdb) b malloc_error_break
Breakpoint 1 at 0x13f44a9
(gdb) r
Starting program: /Users/emp-russian/a.out 
bash(22634) malloc: recording malloc stacks to disk using standard recorder
bash(22634) malloc: stack logging compaction turned off; size of log files on disk can increase rapidly
bash(22634) malloc: process 22536 no longer exists, stack logs deleted from /tmp/stack-logs.22536.a.out.8D3VZO
bash(22634) malloc: stack logs being written into /tmp/stack-logs.22634.bash.kjFTGa
arch(22634) malloc: recording malloc stacks to disk using standard recorder
arch(22634) malloc: stack logging compaction turned off; size of log files on disk can increase rapidly
arch(22634) malloc: stack logs deleted from /tmp/stack-logs.22634.bash.kjFTGa
arch(22634) malloc: stack logs being written into /tmp/stack-logs.22634.arch.8L8iLX
Reading symbols for shared libraries ++. done
Breakpoint 1 at 0x909b54a9
a.out(22634) malloc: recording malloc stacks to disk using standard recorder
a.out(22634) malloc: stack logging compaction turned off; size of log files on disk can increase rapidly
a.out(22634) malloc: stack logs deleted from /tmp/stack-logs.22634.arch.8L8iLX
a.out(22634) malloc: stack logs being written into /tmp/stack-logs.22634.a.out.s1qQRw
a.out(22634) malloc: *** error for object 0x100080: double free
*** set a breakpoint in malloc_error_break to debug

Breakpoint 1, 0x909b54a9 in malloc_error_break ()
(gdb) shell ls -l /tmp/stack-logs.22634.a.out.s1qQRw
total 16
-rw-------  1 emp-russian  wheel   96 Sep 12 09:42 stack-logs.index
-rw-------  1 emp-russian  wheel  208 Sep 12 09:42 stack-logs.stacks

(gdb) shell malloc_history 22634 0x100080

Эта первая часть истории, которая нас на самом деле не волнует:

Call [2] [arg=24]: thread_a0103720 |_dyld_start | dyldbootstrap::start(mach_header const*, int, char const**, long) | dyld::_main(mach_header const*, unsigned long, int, char const**, char const**, char const**) | dyld::initializeMainExecutable() | ImageLoader::runInitializers(ImageLoader::LinkContext const&) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) | libSystem_initializer | __keymgr_initializer | _dyld_register_func_for_add_image | dyld::registerAddCallback(void (*)(mach_header const*, long)) | dwarf2_unwind_dyld_add_image_hook | calloc | _malloc_initialize | malloc_set_zone_name | malloc_zone_malloc | __disk_stack_logging_log_stack | reap_orphaned_log_files | opendir$INODE64$UNIX2003 | __opendir2$INODE64$UNIX2003 | telldir$INODE64$UNIX2003 | malloc | malloc_zone_malloc 
Call [4] [arg=0]: thread_a0103720 |_dyld_start | dyldbootstrap::start(mach_header const*, int, char const**, long) | dyld::_main(mach_header const*, unsigned long, int, char const**, char const**, char const**) | dyld::initializeMainExecutable() | ImageLoader::runInitializers(ImageLoader::LinkContext const&) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int) | ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) | libSystem_initializer | __keymgr_initializer | _dyld_register_func_for_add_image | dyld::registerAddCallback(void (*)(mach_header const*, long)) | dwarf2_unwind_dyld_add_image_hook | calloc | _malloc_initialize | malloc_set_zone_name | malloc_zone_malloc | __disk_stack_logging_log_stack | reap_orphaned_log_files | closedir$UNIX2003 | _reclaim_telldir | free | malloc_zone_free

Но вот интересный материал:

Call [2] [arg=6]: thread_a0103720 |0x1 | start | main | strdup | malloc | malloc_zone_malloc 
Call [4] [arg=0]: thread_a0103720 |0x1 | start | main | free | malloc_zone_free 
Call [4] [arg=0]: thread_a0103720 |0x1 | start | main | free | malloc_zone_free 
2 голосов
/ 28 сентября 2012

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

То, что я опишу ниже, представляет собой удобный способ получить обратную трассировку в том редком случае, когда сработала ошибка, без необходимости взаимодействия с gdb при каждом запуске. Это альтернатива отличным советам EmployedRussian.

Используйте следующий командный файл GDB:

# malloc_error_break.gdb
break malloc_error_break
run         # Add program arguments here
backtrace

Запустите GDB, выполнив командный файл, например:

gdb -x malloc_error_break.gdb --batch my_program

Если программа запускается без решения проблемы освобождения, она выведет «No stack» в ответ на команду backtrace, а затем завершит работу gdb (благодаря опции --batch).

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

1 голос
/ 12 сентября 2010

Эта строка:

myfile >> comment

будет читать только «Комментарий:», а не всю строку «Комментарий: мои данные».Следующей вещью, прочитанной из myfile после этой строки, будет «my», что, вероятно, вызовет проблемы.

В частности, при первом выполнении внешнего цикла он попытается прочитать строку «Data»в start, и не сможет этого сделать (поскольку он не может быть проанализирован как целое число).Таким образом, утверждение:

myfile >> comment >> start >> comment >> finish;

будет прервано, а start и finish останутся неинициализированными.

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

Однако, когда я обхожу эту ошибку, удалив «Мои данные» из первой строки, я могу запустить вашу программу 10000 раз без сбоев.

0 голосов
/ 12 сентября 2010

Вы изучили, каково значение начала и конца и достаточно ли в файле содержимого для заполнения векторов x и y?

Лучшим подходом было бы перефакторинг логики зацикливания - вы должны сломать момент, когда вы нажмете EOF файла.В этот момент вы оставили это вере.

0 голосов
/ 12 сентября 2010

попробуйте запустить gdb, cont до точки сбоя, затем напечатайте обратную трассировку (введите bt); посмотрите, поможет ли это вам определить причину проблемы (обратите внимание, вам нужно скомпилировать вашу программу в режиме отладки, g ++ -g, чтобы распечатать четкую обратную трассировку).

EDIT: На большинстве компьютеров, когда вы освобождаете / удаляете область памяти, освобождаемый указатель не имеет значения NULL. В тот момент, когда вы освобождаете память во второй раз, попробуйте добавить «= NULL», т.е.

delete myPointer;
myPointer = NULL;

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

Кстати, ваш фрагмент кода не содержит динамически выделенной памяти (кроме std :: string, которая внутренне выделяет память динамически).

...