openSUSE:Отчет об ошибке при сбое в программе

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

Но не стоит считать, что сообщить об ошибке означает сказать: «она не работает». Одним из факторов, который задерживает исправление ошибки, является неточное её описание. Это руководство должно помочь улучшить взаимодействие между разработчиками и пользователями в устранении ошибок.

Качество отчета об ошибках

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

  • Воспроизведение: Попробуйте воспроизвести ошибку на своём компьютере. Запишите каждый шаг, который нужно выполнить для воспроизведения ошибки. Иногда может быть полезно отключить фоновые задачи, чтобы избежать взаимодействия между сбойным приложением и другим процессом, работающим в фоновом режиме. Если сбойное приложение взаимодействует с другим процессом, об этом также следует сообщить. Каждая дополнительная деталь поможет инженеру воспроизвести ошибку на своей рабочей станции.
  • Конкретика: Не сообщайте о нескольких ошибках в одном сообщением. Предоставьте как можно более точную информацию. Избегайте упоминания других вещей, которые могут ввести программиста или тестировщика в замешательство.

Например, предположем, что приложением содержащим ошибку является веб-браузер. Он падает каждый раз при посещении сайта example.org. Попробуем составить сообщение об ошибке:

Плохо:
Мой браузер падает.
Я думаю это связанно с www.example.org.
Я играю в гольф с Боссом, так что вам лучше
исправить эту проблему или я сообщу ему о ней.
Кстати, ваша иконка Назад выглядит УЖАСНО !!!
Спс за помощь.
Хорошо:
Мой браузер "точное-имя" зависал каждый раз, 
когда я заходил на www.example.org 
в системе openSUSE - "точная-версия".
Превосходно:
Мой браузер "точное-имя" - "точная-версия"
падал каждый раз, когда я заходил на www.example.org.
Я использую сборку браузера от 23 мая 2012 года
из репозитория openSUSE "Browsers"
на системе openSUSE-"точная-версия".
Он падал каждый раз при появлении
баннера вверху страницы.
Я разбил страницу на части и обнаружил, 
что следующая ссылка на изображение приводит к сбою,
если удалить атрибут "BORDER=0":
<IMG SRC="banner.png" WIDTH="400" HEIGHT="60" BORDER="0" ALT="Banner">

Анализ проблемы

Окружение

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

Установка -debuginfo пакетов

Большинство приложений с графическим интерфейсом имеют возможность вывода обратной трассировки в случае сбоя. Трассировка сообщает разработчику более точно, где в прогамме произошла ошибка. Обычно для этого используется GDB. Тем не менее, GDB может позволяет собрать более подробную трассировку, когда у него есть доступ к отладочной информации. Пожалуйста, установите пакет -debuginfo для вашего приложения, чтобы улучшить вывод gdb. Пакеты -debuginfo не являются частью состава компакт-дисков, но их можно найти в репозитории в сети на нашем HTTP-сервере загрузки или на любом зеркале (в официальном репозитории с отладочными пакетами). Этот репозиторий можно активировать в YaST в качестве источника для установки пакетов (в качестве замены для вашего CD).

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

Отладка

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

Существуют разные способы отладки приложений, поэтому вы должны проверить каждое из этих предложений самостоятельно и выбрать подходящий вариант.

strace

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

В простейшем случае strace запускает и отслеживает работу указанной команды до её завершения. Он перехватывает и выводит системные вызовы, к которым обращается отслеживаемый процесс, и получаемые им сигналы. Имя каждого системного вызова, его аргументы и возвращаемое значение печатаются в стандартный поток вывода ошибок или в файл, заданный с помощью опции -o.

Например для трассировки команды cat /dev/null:

  strace -f -ttt -o strace.log cat /dev/null

Эта команда создаст файл вызова strace.log в текущей директории. Заглянув в него мы обнаружим вывод обращения изучаемой программы к системному вызову, как показан ниже:

  open("/dev/null", O_RDONLY) = 3

Для ошибок (обычно возвращаемое значение равно -1) дополнительно выводится errno символ и его краткое описание.

  open("/foo/bar", O_RDONLY) = -1 ENOENT (No such file or directory)

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

Использование GDB

GDB (отладчик GNU) — это программа, используемая для поиска ошибок времени выполнения, которые обычно связаны с повреждением памяти. Ей также можно использовать для получения, так называемой, обратной трассировки — последовательности вызовов функций приведшей к сбою. Для получения трассировки для /usr/bin/brokenprogram, которая при каждом запуске аварийно завершается ошибкой сегментации, используйте следующую команду:

  $ gdb /usr/bin/brokenprogram
  GNU gdb 6.5
  ...
  (gdb)

Теперь вы в GDB. При нехватке -debuginfo пакетов, GDB выведет подсказку подобную следующей:

   Missing separate debuginfo for /lib64/libm.so.6
   Try: zypper install -C "debuginfo(build-id)=35d35d9ce781be3a140a34242d998498615b021f"
   Missing separate debuginfo for /lib64/libpthread.so.0
   Try: zypper install -C "debuginfo(build-id)=522229c2dde70aaa8e4295ecb7b6643c810f758f"
   Missing separate debuginfo for /lib64/libc.so.6
   Try: zypper install -C "debuginfo(build-id)=c3e668c7a2e7ae513e801d34a968a43510b29b52"

Просто скопируйте и вставьте указанные команды zypper в другой терминал, чтобы установить недостающие пакеты, выйдите из gdb с помощью quit и начните сначала.

В gdb введите команду run и дождитесь краха программы:

  (gdb) run
  Starting program: /usr/bin/brokenprogram
  
  Program received signal SIGSEGV, Segmentation fault.
  0x08048394 in brokenfunc () at brokenprogram.c:4
  4	    *i = 2;
  (gdb)

Кроме того, если программа запускается при входе в систему, запускается сценарием инициализации или чем-то подобным, вы можете присоединиться к запущенному процессу и затем продолжить его выполнение с помощью continue и подождать краха программы:

  $ pidof brokenprogram
  12345
  $ gdb /usr/bin/brokenprogram 12345
  (gdb) continue
  Continuing.
  Program received signal SIGSEGV, Segmentation fault.
  0x08048394 in brokenfunc () at broken.c:4
  4	    *i = 2;
  (gdb)

Итак, вернемся к приглашению GDB включить ведение журнала и получить обратную трассировку с потоком применить полную обратную трассировку.

  (gdb) set logging on
  Copying output to gdb.txt.
  (gdb) thread apply all backtrace full
  [New Thread 0x7ffff6d0b700 (LWP 4520)]
   
   Thread 2 (Thread 0x7ffff750c700 (LWP 4519)):
   #0  0x00007ffff7542849 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
           resultvar = 0
           pid = 4515
           selftid = 4519
   #1  0x00007ffff7543cd8 in __GI_abort () at abort.c:89
           save_stage = 2
           act = {__sigaction_handler = {sa_handler = 0x6466203030303030, sa_sigaction = 0x6466203030303030}, sa_mask = {__val = {3906931166148702266, 2314885530818459703, 2314885530818453536,
                 3395749441387372576, 7596498572764408172, 3330465998920576354, 7378697628689264499, 3256155514972955191, 7233967814408037943, 3255307721929404514, 3472891250476064880, 3616454703663034416,
                 2321666313920262688, 2314885530818453536, 2314885530818453536, 8096}}, sa_flags = 89, sa_restorer = 0x7ffff750bed0}
           sigs = {__val = {32, 0 <repeats 15 times>}}
   #2  0x00007ffff7581114 in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff76770e0 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
           ap = {{gp_offset = 40, fp_offset = 0, overflow_arg_area = 0x7ffff750bee0, reg_save_area = 0x7ffff750be70}}
           fd = 3
           on_2 = <optimized out>
           list = <optimized out>
           nlist = <optimized out>
           cp = <optimized out>
           written = <optimized out>
   #3  0x00007ffff758696e in malloc_printerr (action=3, str=0x7ffff767317b "free(): invalid pointer", ptr=<optimized out>) at malloc.c:4916
           buf = '0' <repeats 11 times>, "12345"
           cp = <optimized out>
   #4  0x0000000000400a88 in work (t=0x0) at brokenprogram.c:18
           i = 10
           tid = 0
           result = -4.943676956758269
   #5  0x00007ffff78c40db in start_thread (arg=0x7ffff750c700) at pthread_create.c:309
           __res = <optimized out>
           pd = 0x7ffff750c700
           now = <optimized out>
           unwind_buf = {cancel_jmp_buf = {{jmp_buf = {140737342654208, 3582659488515194238, 1, 140737354125312, 0, 140737342654208, -3582675678410998402, -3582676406767878786}, mask_was_saved = 0}}, priv = {
               pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
           not_first_call = <optimized out>
           pagesize_m1 = <optimized out>
           pd = 0x7ffff750c700
           now = <optimized out>
           unwind_buf = {cancel_jmp_buf = {{jmp_buf = {140737342654208, 3582659488515194238, 1, 140737354125312, 0, 140737342654208, -3582675678410998402, -3582676406767878786}, mask_was_saved = 0}}, priv = {
               pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
           not_first_call = <optimized out>
           pagesize_m1 = <optimized out>
           sp = <optimized out>
           freesize = <optimized out>
           __PRETTY_FUNCTION__ = "start_thread"
   #6  0x00007ffff75f47cd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
   No locals.
   
   Thread 1 (Thread 0x7ffff7fe1700 (LWP 4515)):
   [...]
   (gdb) 

Выйдите из GDB, набрав quit</ tt>, обратная трассировка будет сохранена в файле <tt>gdb.txt в текущем каталоге.

Если программа дает сбой только при использовании определенного параметра, скажем, - crash, то этот параметр нужно добавить в команду run:

  (gdb) run --crash
  Starting program: /usr/bin/brokenprogram --crash

dmesg

dmesg выводит или контролирует кольцевой буфер ядра. Поэтому, если в случае обнаружения проблем с ядром или приложениями, связанными с ядром (особенно с модулями ядра или с компонентами «горячей» замены), вам следует использовать этот инструмент.