Отладка программ на C для начинающих

    … или что делать если «Hello world!» упала.

    Всё последующее в основном написано для ОС Linux и консольной отладки, хотя кое-что можно использовать и в других условиях.

    Возможно это прозвучит странно, но начинать писать порграмму стоит с установки системы контроля версий (если ещё не установлена) и создания репозитория. Это нужно для того, чтобы в процессе написания не потерять много времени на попытки вспоминить где, что, как, когда и зачем пишущий исправлял/добавлял. На сегодня наиболее популярные — это svn (subversion), git и mercurial. Последний, лично мне, нравится больше остальных, т.к., субъективно, он проще и удобнее, особенно для личного пользования.

    Далее нужно убедиться в наличии команд gdb (отладчик) и strace (монитор системных вызовов). Если их нет, то установить. А при компиляции своей программы не забыть включить добавление отладочной информации.

    Итак, случилось — она упала. Один из основных моментов, которому меня научили — внимательно читать, что пишет система/программа. Многое можно узнать, воспользовавшись командами:
    • dmesg (информация ядра), lspci (устройства на шине PCI), lsmod (список загруженных драйверов) — при работе с драйверами;
    • tail /var/log/messages — показывает десяток строк в конце системного лога;
    • ps ax — список запущенных процессов (ключи могут быть и другие);


    Допустим, что ничего нужного там не нашлось. Тогда стоит выполнить команду «ulimit -c 50000», ulimit (встроенная команда shell, устанавливающая/показывающая ограничения использования ресурсов shell. Подробное описание можно почитать c помощью man или тут), 50000 — взято отбалды, это размер core-файла, который в большинстве случаев создастся после падения программы, запущенной в этой же консоли, и представляет собой дамп памяти упавшей программы. Далее запускаем программу снова, в консоли, где был ulimit. Она опять падает, но уже с созданием коры (обычно). Как вариант, всё это можно проделать и заранее, т.к. если ошибка плавающая, то второй раз может упасть нескоро, бывало люди месяцами ждали.

    С помощью отладчика нужно попытаться рассмотреть тёпленькую кору:
    • gdb <программа> core.NNNN

    gdb предоставляет интерактивный консольный интерфейс, в котором много чего можно сделать, но тут я описывать всё не стану — в манах и интернете это есть на куче языков. Пока достаточно будет выполнить там команду «bt» (от backtrace), которая покажет стэк вызовов и, если не было прописи памяти (рассмотрим ниже), можно будет увидеть где сломалось. А с помощью команды «frame N», где N — номер вызова (слева), можно увидеть и подробнее. Команда «print <переменная>» поможет увидеть (не всегда) значение переменной. Если ваш «Hello world!» многонитевый, то тут всё сложнее, но можно попытаться воспользоваться командой «thread N» отладчика, чтобы перейти к стэку N-ой нити, правда, как правило, это не сильно помогает.

    Если отладчик рисует только кучу вопросиков в стэке вызовов, то это явная пропись памяти и он (отладчик) вам не поможет. В большинстве таких случаев нужно открыть текст программы и
    обратить особое внимание на функции memcpy, memset, sprintf и прочие, подобного типа, работающие с блоками данных, способных выйти за пределы массива и писать поверх всего, что идёт в памяти дальше. Скорее всего ошибка где-то там. Как минимум стоит их заменить на более безопасные аналоги (если есть), например, snprintf. Если и это не помогло, то в бой идут старые, проверенные временем методы:
    • комментирование всего и раскомментирование маленькими частями с последующей компиляцией и проверкой на отсутствие ошибки;
    • вставка отладочной печати (зачастую чуть ли не после каждой строки подозрительного участка кода (memset! memcpy!)) с дальнейшим анализом — после какой печати сломалось.

    Это помогает как и для однонитевых, так и для многонитевых программ (тут как обычно могут быть танцы с бубном).

    Как быть если программа входит в бесконечный цикл и система впадает в ступор (бывает и такое)? Выгружаем оконный менеджер и в текстовом режиме на одна из консолей запускается под суперпользователем (root). Для полной уверенности можно ей немного повысить приоритет с помощью nice/renice, а программу запустить на другой консоли/терминале под обычным пользователем. Тогда её, при зацикливании, можно будет снять в консоли суперпользователя (команда «kill») и таки увидеть что она пишет.

    Что делать если ничего не помогает? Ответ только один — садиться и внимательно, очень внимательно, изучать код и думать.

    Если в программе ошибок нет (не видно на 1001й взгляд и анализ), то может на разделах закончилось место? (true story)

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

    P.S. В исходниках ядра Linux каталог Documentation есть файлик CodingStyle. В нём есть что взять на заметку начинающему C-программисту.

    P.P.S. Если бы мне всё это было известно cначала, то мне было бы гораздо проще.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 64

      +1
      Ну для начинающего программиста я бы посоветовал не gdb а NetBeans. Отладка нам намного удобнее чем голый gbd
        +1
        Правда очень не хватает просмотра стека
          +5
          насчет удобства я бы поспорил. я, перепробовав почти все, вернулся к выводу, что gdb практически идеален для C/C++. никакой другой отладчик не позволяет с такой скоростью и гибкостью отлаживать программы любой сложности.
            +6
            и да, кстате, нам когда-то в университете преподавали программирование, все было под линукс, первый курс — nasm, второй C/C++. отладка в обоих случаях в gdb, редактировать учили в vim'е. ничего, студенты без проблем и стрессов осваивали это все. сложность для начинающих весьма преувеличена
              0
              А мы редактировали в vi :)
              • UFO just landed and posted this here
                  +2
                  Wolong, на комментарий которого вы отвечаете, писал про Vim. Vim, в отличие от vi (а также pico и nano) умеет подсветку синтаксиса, автодополнение и навигацию по коду. Порог вхождения высокий, но как редактор для программиста он очень хорош (опять же в отличие от pico и nano).
                  • UFO just landed and posted this here
                • UFO just landed and posted this here
              –1
              Чтобы он потратил весь запал на то, чтобы разобраться в IDE? Лучше что-то простое
                +4
                Хотите сказать в gdb не надо разбираться? В IDE я нажал Debug и сразу увидел все на одном экране — переменные, память, ассемблерный код с кодом С++ для удобства отладки
                  0
                  Хочу сказать, что Netbeans слишком сложен и монструозен для новичков и не только.
                    +2
                    Ну это вы знаете, это чисто Ваше мнение. Для меня наоборот, так как я программировал в нетбинсе под джаву было все очень понятно. У каждого свой опыт за плечами и свои вкусы. Я просто решил что так как статья для новичков в С, то не помешает упомянуть альтернативный способ. Пусть люди выбирают, я ничего не навязываю же
                      +4
                      Ну, раз пошла такая пьянка, то я вставлю слово за QtSDK.
                        +1
                        А я пожалуй за Code::Blocks :)
                          0
                          Мне тоже Code::Blocks нравиться, правда и проекты на уровне университетских заданий.
                          С серьезными проектами на нем дела не имел.
                            0
                            Вот только не его. Он коряв как моя жизнь)))
                          0
                          я java начинал юзать под jedit. И компиляция на мейкфайлах. И только через годик уже перешёл куда-нибудь к эклипсу.
                    +1
                    kdevelop/eclipse
                    в первом дебагер по проще, во втором по информативнее, но оба используют gdb.
                      0
                      Не всегда есть выбор, у меня, например, не было.
                      +10
                      в чем смысл статьи? упомянуть названия инструментов? насчет coredump — в той же убунту для его создания необходимо поставить пакет linux-crashdump. Когда «рисуются вопросики», ломается стек, куча, и происходит прочее волшебство, связанное и повреждением памяти, куда лучше поможет valgrind, разберет с указанием строк кода, что не так, как и почему.
                        +3
                        Когда начинаешь, то обычно не знаешь названий инструментов, которые можно использовать и зачем они нужны.
                          0
                          Собственно, ожидал, что вся статья будет про то, что такое valgrind, и как им пользоваться.
                          Ан нет. Странно.
                          +1
                          > strace (монитор системных вызовов)
                          и ltrace(1).
                            +3
                            и blktrace, и etrace, и latrace, и xtrace, и mutextrace, и kmtrace, и mutrace и еще много страшных слов я знаю, опишите хоть как и зачем вы предлагаете использовать этот инструмент.
                              +1
                              Ну, раз это непонятно: strace и ltrace удобно использовать для того, чтобы быстро выяснить до какой точки дошло выполнение и что при этом происходило (ошибки, водвращаемые значения и пр.).
                            +6
                            > … или что делать если «Hello world!» упала
                            Купить расческу для рук. Сменить компьютер. Не запускать «Hello world!». :)
                            • UFO just landed and posted this here
                                –9
                                Нет, друзья, нет нет и еще раз нет системе контроля версий для начинающего программиста!
                                Надо писать много, много и дико, и желательно с одного раза, с одной попытки на упражнение\задание\пример! А контроль версий тут только задержит!
                                  +5
                                  У меня была проблема когда начал изучать Си/Си++/Windows/MSVC6 — неосторожное изменение кода влекло за собой падающий проект. Я начинал комментировать код, пытаясь понять что именно падает и как сделать так чтобы не падало. Позже я научился делать частые бэкапы в другую папку с указанием даты. Если бы мне тогда показали на контроль версий — да я бы был счастлив.
                                  +1
                                  Мне очень отладчик ddd нравится. Это GUI над gbd, внизу можно в консоль писать команды gdb, но так же есть гуи. Выглядит правда жутко (интерфейс какой-то очень нативный и древний), но дело свое успешно выполняет.
                                  • UFO just landed and posted this here
                                      0
                                      Спасибо, круто. Попробую. А шорткаты-то есть? Типа перейти до следующего брейкпоинта, войти в метод. Или все через «консоль»?
                                      • UFO just landed and posted this here
                                          +1
                                          Многие команды gdb можно сокращать до одного символа:

                                          Запустить программу: run или r
                                          Шаг вперёд: next или n
                                          Войти в функцию: step или s
                                          Поставить новый breakpoint: break или b
                                          «Отпустить» выполнение до следующего breakpoint-а: continue или c

                                          Иногда до двух-трёх символов:
                                          Отключить breakpoint 3: disable 3 или dis 3
                                          Включить breakpoint 3 обратно: enable 3 или en 3
                                          Выполнять код до строчки 123: advance 123 или adv 123
                                          Выполнять код до конца функции: finish или fin

                                          Конечно, не совсем шорткаты, но почти.
                                          0
                                          Или из «обычного» gdb — layout src, layout split, или лучше help layout :)
                                            +2
                                            -tui колбасит от utf-8 комментов в коде :-( я юзаю cgdb
                                              0
                                              Также можно запускать переключаться между обычной командной строкой и TUI c с помощью «Ctrl-X A» или «Ctrl-X Ctrl-A», т.е. не важно, когда вы отжали Ctrl после Ctrl-X.
                                            0
                                            Так же можно посоветовать memwatch для отслеживания утечек памяти. Лично мне очень помогает.
                                              +8
                                              А я привык отлаживаться с помощью printf. Кидаю в лог кучу всего (метки прохождения участков кода, значения переменных). Запускаю и смотрю вывод, анализирую. Привычка выработалась в результате отсутствия пошаговых отладчиков под некоторые платформы. В случае реалтайма возникают сложности. В худшем случае приходится выводить сигналы на выводы микроконтроллера и смотреть осциллографом. (например, в начале исполнения участка кода устанавливем вывод в единичку, в конце исполнения выставляем в нолик. запускаем и смотрим осциллографом. Думаем.)
                                              Если же отладчик под текущую платформу присутствует, всё равно не использую. Привычка.
                                                +2
                                                О боже, как это похоже на разработку под PHP. Даже если есть куча нормальных средств, от привычки использовать var_dump отказаться очень сложно :( Да и редко бывает нужно, вообще говоря
                                                  +3
                                                  Да, я тоже не люблю все эти самолеты и позда — от привычки ходить пешком отказаться очень сложно :( Да и редко бывает нужно, вообще говоря.
                                                    0
                                                    Вполне хватает случаев, когда логгирование даёт намного больше пользы, чем отладка.
                                                      0
                                                      Как я вас понимаю. Я тоже от дома в продуктовый магазин хожу пешком. Каждую неделю трачу десять минут, чтобы туда дойти, а ведь давно бы уже пора туда самолётом летать.
                                                        0
                                                        Когда 10 минут (отладочный вывод) это одно. А когда серьезный дебаггинг, напихивать 100500 принтфов, чтобы потом их удалить или закомментить (ибо слишком много мусора в логах после отладки) это двойная работа: сначала сделать из 3-х строчной функции 10 строчную (образно), а потом или отключать это ифа-ми или комментировать/удалять.

                                                        Отладчики как раз для решения это проблемы и придуманы, как и, допустим, исключения — чтобы не засорять чистый код неосновными вещами.
                                                          0
                                                          А вы уверены, что заменив 100500 принтфов отладчиком, это поможет быстрее найти баг?

                                                          Знаете ли вы способ перед поиском бага оценить наиболее выгодный алгоритм его поиска?
                                                        0
                                                        И ведь что характерно так и есть, без сарказма))
                                                    +2
                                                    Я в детстве, еще в Турбо Си, пошагово трассировал с слежением за переменными через watch. Этот метод чтоль уже не рулит?
                                                      0
                                                      а если оно в продакшене упало, что делать? ставить отладчик? уже после того, как один раз упало и в следующий раз упадёт нескоро?
                                                      0
                                                      Забыли упомянуть, чтобы GDB нашел нормальную отладочную информацию, неплохо бы дать флаг -g при сборке и выключить все оптимизации.
                                                        0
                                                        Там написано, что нужно при компиляции включить добавление отладочной информации.
                                                        –3
                                                        Все-таки более традиционным уже считается «многопоточность», а не «многонитевость».
                                                        К примеру, для программистов WinAPI существуют потоки (threads) и нити (fibers) и это устоявшийся и официальный перевод.
                                                          +4
                                                          То для программистов WinAPI. А для программистов С++ существуют потоки (streams) и нити (threads). Трудности перевода в общем. Я в таких случаях вообще стараюсь английскую кальку использовать, хоть это и коряво звучит, чтобы не было неоднозначностей.
                                                            0
                                                            Приведите, пожалуйста, контекст (а лучше фразу/предложение) в котором можно спутать «поток» в значении «поток ввода/вывода == stream» и «поток» в значении «поток исполнения == thread».

                                                            А «fiber» обычно переводят как «волокно» или «пользовательский поток»
                                                              0
                                                              В контексте то как раз в большинстве случаев понятно о чем речь. Просто чтобы одним словом не называть две кардинально разные сущности некоторые называют стримы потоками, а треды нитями. А файберы волокнами, да.
                                                                0
                                                                Ну а если есть устоявшиеся в книгах переводы, и вам из контекста все понятно — зачем этим коверканьем заниматься?
                                                                  0
                                                                  Ну когда один поток пишет в поток, а другой поток из этого потока читает — то всё ж понятно из контекста? Но звучит не очень.

                                                                  Вообще спор из серии «Называть долину силиконовой или кремниевой». Вроде всем понятно, что кремниевой, но устоявшееся название и всё такое… В итоге используются оба.
                                                                    0
                                                                    «На конвйере по производсту пива, в процессоре ошибся предсказатель переходов и проихошел сброс конвйера» — вас это смущает? Давайте тоже переименовывать.
                                                                    +1
                                                                    Найти бы того, кто их устоял в своё время…
                                                                    Как могло придти в голову перевести «thread» как «поток» — не представляю.
                                                                      –1
                                                                      Не важно кому пришло это в голову. Это термин. Он в книгах, переводах. Хороших и рекомендованных. Терминология существует не для того, чтобы ухо радовать (хотя это, бесспорно, ее приятное свойство), а для того, чтобы можно было обмениваться информацией, не переобозначая каждый раз, определения используемых слов
                                                            +1
                                                            ulimit -c unlimited для core-файлов любого размера. Иначе по обрезку мало потом чего скажешь.
                                                            • UFO just landed and posted this here
                                                                0
                                                                Я учил gdb по этой доке www.opennet.ru/docs/RUS/gdb/
                                                                Вообще самая лучшая подборка документации на русском на опеннете.

                                                                Only users with full accounts can post comments. Log in, please.