1. Си / Говнокод #17494

    +133

    1. 1
    2. 2
    if (dbg)
    	printf("2\n");

    // This is debug mode

    Запостил: codemonkey, 22 Января 2015

    Комментарии (42) RSS

    • Поди искали точку, в которой прога падает?
      Ответить
      • Я когда-то использовал
        assert(fprintf(stderr, "test"))
        с той же целью.
        Ответить
        • Проверка ндебаг макро?
          Ответить
          • Определять в каком месте валится код. А ассерт — чтоб только в дебаге спамило.
            Ответить
        • Я тоже. А потом я узнал, что существуют крешдампы.
          Ответить
          • Я тоже так делал, а потом мне прострелили колено.
            Ответить
      • stdout буферизованый. Так некоторые ошибки можно и не поймать.
        Ответить
    • А я всегда просто printf("2\n"); а потом удаляю. Все равно же то в одном месте эти цифры ставишь, то в другом. В результате если не убирать, а делать ассертом\проверкой - сразу во многих местах одинаковые (или пусть даже разные) цифры будут, куча мусора. Лучше чтоб они всегда сыпались, тогда поневоле не забудешь их после отладки удалить.
      Правда первый пост оправдан если dbg устанавливается в 1 при конкретных условиях, от номера команды или чего-то такого зависит.
      Ответить
      • Макро про успешность, некогда и удаление printf который сам себя не удалит.
        Ответить
        • Зря Вы так про printf. Если отлаживать код трезвого человека, printf часто быстрее и эффективнее.
          * С printf не надо приводить отладчик в какое-то специальное состояние и жать все эти br, c, n. Просто запускаем программу, и она всё сама расскажет о себе.
          * Окно отладчика эквивалентно окну редактирования кода. Для программиста не создаётся лишний контекст. Только код, вывод компилятора и вывод программы.
          * Не надо рыться в стеке. Состояние именуется программистом. Простое и понятное "state 6" из printf("state 6 x=%d y=%d z=%a", x, y, z); вместо "3 уровень стека при входе в функцию trololo на строке 100500 файла foo.gif.cpp".
          * printf универсальный. Неважно, что за отладчик используется (из эклипса/вебкитовских веб-инструментов или гдб) и есть ли он вообще, а консоль скорее всего найдётся.
          Ответить
          • Логирование это хорошо, безусловно. Вот только не в виде pritnf("2\n") и не на stdout.
            Ответить
          • То что вы назвали, называется не отладкой, а трассировкой. Действительно, чаще её более чем достаточно.
            Ответить
          • Потому что printf быстрее написать чем br?
            Второй пункт - не понял о чем это. У меня все все равно в одном окне.
            Мне удобнее "рыться" чем придумывать как что-то там распечатать. Особенно с объектами, которые не особенно распечатаешь (указатели, рекурсивные структуры, просто большие структуры).
            По указаной выше причине printf не универсальный. А если идти по пути отказа от полезных инструментов, то можно отказаться и от консоли. Веб-"програмисты" часто без нее обходятся, и ничего.

            Единственное оправдание не использованию отладчика - это как Дейкстра когда-то сказал, что отладчик снимает ответственность, и что програмист начинает полагаться не на личное понимание своего кода, а на случайную интерпретацию его компьютером. На этом фоне printf ничем не лучше, просто ухудшеная версия отладчика.
            Ответить
            • Если всё в одном окне и включается по одной кнопке, то хорошо, повезло.

              По-моему, сложность инструмента должна соответствовать сложности задачи. Да и ситуации разные бывают.
              Тупые баги и сиюминутный контроль - printf("2\n"); (в туалет лучше идти пешком)
              Чуть посложнее - printf("state15 x=%d\n", x); (в соседний район лучше ехать на метро)
              Ещё сложнее - пишем в лог больше и раскуриваем свитки с результатами (за город - на автомобиле)
              Творится какой-то бред, действия программы понять невозможно - настало время для отладки отладчиком (в соседнюю страну - на самолёте)

              > Веб-"програмисты" часто без нее обходятся, и ничего.
              Жаль. Там же консоль даже полезнее отладчика хотя бы из-за того, что показывает иерархию для давно умершего объекта рядом с объектом живым.
              Ответить
              • > настало время для отладки отладчиком
                Серверный софт очень удобно отлаживать. Особенно если таймауты в районе 5-10 секунд.
                Ответить
            • Еще отладчика может не быть или быть такой глючный что на каждой второй точке останова падает и проще printf сделать.

              А если надо отлаживать сам ком-порт, то и printf не сделаешь, только светодиодами моргать.
              Ответить
              • Ну, если определить свой com-поток который будет моргать морзянкой передаваемые ему символы, то можно использовать fprintf
                Ответить
            • > Веб-"програмисты" часто без нее обходятся, и ничего.
              Вот это новости о_О
              Ответить
          • Я вообще отладчик использую только для анализа корок, либо от отчаяния. У меня проблемы в основном многопоточного плана, там отладчик обычно бесполезен, т.к. сильно сбивает тайминг. Да и тред, в котором стоит брейкпоинт, может перестать отвечать на хартбиты и случиться преждевременный дисконнект.

            Валгринд, санитайзеры, логгирование - наше всё.
            Ответить
            • Валгринд тоже не панацея. По крайней мере, тот баг в libfreeradius, который я недавно кидал, он в упор не видел. Да даже вот так:
              void test1() {
                  char *a = malloc(100);
                  printf("%d\n", a[105]); // видит
              }
              void test2() {
                  char a[100];
                  printf("%d\n", a[105]); // не видит
              }
              Ответить
              • А у него вообще есть информация по стек-фреймам? libfreeradius с отладочной информацией собирался?
                Ответить
                • Скажем так - дефолтный инструмент волгринда (memcheck) вообще не умеет со стеком работать, только кучу мониторит. Там есть экспериментальный (exp-sgcheck), но и он даже вот такой простенький пример не засёк (с отладочной информацией и без оптимизации).
                  Ответить
                  • Ну в общем виде такое вообще нельзя засечь. Если за массивом на стеке будут ещё какие-то переменные, то valgrind никак не определит к чему обращались.
                    Тут скорее статический анализ нужен.
                    Ответить
                    • Address Sanitizer добавляет redzones после каждой перменной на стеке. Так нельзя отследить выходы за границу дальше redzone в валидную память, но типичные ошибки небольшого выхода за границы ловятся. С полями структур сложнее, между ними redzones не внедряются.
                      Ответить
                      • Но ведь эти редзоны только запись ловят, как и встроенный гццшный протектор? Или чтения тоже?
                        Ответить
                        • Можно ещё для каждого бита хранить информацию, инициализирован он или является мусором. Вот эта штуковина так делает:
                          https://code.google.com/p/memory-sanitizer/
                          Ответить
                          • > для каждого бита хранить информацию, инициализирован он или является мусором
                            Упрлс...
                            Ответить
                            • MemorySanitizer is bit-exact: it can track uninitialized bits in a bitfield. It will tolerate copying of uninitialized memory, and also simple logic and arithmetic operations with it. In general, MemorySanitizer silently tracks the spread of uninitialized data in memory, and reports a warning when a code branch is taken (or not taken) depending on an uninitialized value.
                              Ответить
                      • Я одно время хотел обёртку для глупых указателей запилить - safe_ptr<T>, safe_new(), safe_delete() и т.п., чтобы были доступны все операции, которые доступны с обычными указателями, включая адресную арифметику и каст char* в T*. А дефайном переключаться между zero overhead и range check + align check + aliasing check версиями. Но, к сожалению, в библиотечном коде кругом обычные указатели, и полноценно работать это не будет :(

                        Как-то так хотелось:
                        safe_ptr<char> cp = safe_new_array<char>(100);
                        safe_ptr<int> ip2 = (cp + 10).cast<int>(); // align error
                        safe_ptr<int> ip = (cp + 20).cast<int>();
                        ip[20] = 5; // range error
                        Ответить
                        • Про алиасинг мысль довольно глупая и непродуманная, но как-то так:
                          safe_ptr<float> fp = (cp + 10).cast<float>(); // aliasing error with ip
                          safe_ptr<float> fp = cp.cast<float>(); // aliasing error with ip
                          safe_ptr<float> fp = cp.cast<float>(0, 5); // aliasing error, because ip can be decremented
                          
                          safe_ptr<int> ip = cp.cast<int>(5, 25); // ok
                          safe_ptr<int> ip = (cp + 20).cast<int>(0, 20); // ok too
                          safe_ptr<float> fp = cp.cast<float>(0, 5); // ok, no overlap
                          Ответить
                          • Если простыми словами - можно превратить любой safe_ptr<T> в safe_ptr<char> или раздать непересекающиеся части safe_ptr<char> нескольким safe_ptr<T>. Все остальные случаи падают с aliasing error.
                            Ответить
              • Есть еще такая штука: http://www.astree.ens.fr/ но не бесплатная (недавно про нее читал).
                Ответить
                • but without dynamic memory allocation and recursion
                  Ответить
                  • Серьезно? Тю блин... а в примерах как раз столько об этом и говорили... (я про проверку моделей / абстрактную интерпретацию читаю).

                    Designed according to the principles of abstract interpretation very roughly sketched
                    above, the ASTRÉE analyzer (www.astree.ens.fr/) [6,7,24,25,26,30,31,42,43,44,45,
                    46,48,49,53,54,55] can formally verify the absence of runtime errors in C programs with
                    Boolean, integer and floating point computations; structures and arrays; pointers (in-
                    cluding on functions); conditionals, loops and function calls; limited branching (forward
                    goto, break, continue), pointer arithmetics and unions without dynamic memory alloca-
                    tion, recursive function calls, unstructured backward branching, conflicting side effects
                    (the ASTRÉE analyzer checks the absence of ambiguous side effects since otherwise the
                    semantics of the C program would not be well-defined), C libraries and system calls (but
                    for the synchronization on a clock tick or the use of stubs).


                    Ах, блин without.
                    Ответить
    • gdb $EXE
      r
      bt
      Ответить
      • Автор кода - маздайный непингуемый ламер, о гдб не знает.
        Ответить
        • Хотите расскажу историю о том как GDB не помог, потому что в момент креша не мог найти страницы со стеком? Зато спасли принты из патченой libc.
          Ответить
    • На мой взгляд фундаментальная проблема в дизайне самих C и С++.
      Я просто оставлю это здесь, тут описана вся суть С++, и санитайзеры, и UB и стрельба себе в ногу.

      Я рассматривал ружье, принесенное Паулой. Оно было совершенно неизвестной мне испанской марки, но как будто в удовлетворительном состоянии. Я обнаружил в нем только один фатальный недостаток.
      — Смотри, Рафаэль, — сказал я, — у этого ружья нет предохранителя.
      Рафаэль подошел и внимательно осмотрел ружье.
      — Нет, Джерри, все в порядке, вот предохранитель.
      — Как, этот маленький рычажок?
      — Ну да, это и есть предохранитель.
      — Нет, не может быть, я переставлял его в оба положения, а курок все равно действует.
      — Нет, нет, Джерри… щелчок слышен, это правда, но выстрела не произойдет.
      Я с сомнением посмотрел на Рафаэля.
      — Во всяком случае, это кажется мне очень странным. Предохранитель есть предохранитель, и когда он установлен, спусковой крючок не может приводить в движение курок.
      — Нет, Джерри, ты не понимаешь… это испанское ружье… сейчас я покажу, как оно действует.
      Он зарядил ружье, опустил рычажок вниз, выставил дуло в окно и нажал спусковой крючок. Раздался оглушительный грохот, на который отозвались все собаки в поселке, а из двери спальни выскочила Джеки, решив, что куфия вырвалась из ящика. Рафаэль поправил очки и внимательно осмотрел ружье.
      — Хорошо, — сказал он с философским спокойствием, — значит, предохранитель действует в таком положении.
      Он поднял рычажок, перезарядил ружье, направил ствол в окно и снова нажал крючок. И на этот раз ружье с грохотом выстрелило, а собаки залились истерическим лаем.
      — Это так называемое испанское ружье, — объяснил я Джеки, — из него можно застрелиться независимо от того, поставлено оно на предохранитель или нет.
      — Нет, Джерри, это очень хорошее ружье, — возмутился Рафаэль, — просто оно сломано внутри.
      — Да, оно действительно «сломано внутри», — согласился я.

      Ответить

    Добавить комментарий