Как уменьшить вероятность ошибки на этапе написания кода. Заметка N4

    PVS-Studio vs Firefox
    Это уже четвертая заметка, где я хочу поделиться полезными наблюдениями о паттернах ошибок и том, как можно с ними бороться. В этот раз я затрону такую тему, как обработка редких и аварийных ситуаций в программах. Рассматривая множество программ, я пришел к выводу, что код обработки ошибок в Си/Си++ программах — одно из самых ненадежных мест.
    К чему приводят такие дефекты? Программа, вместо того, чтобы выдать сообщение «файл X не найден», падает и заставляет пользователя гадать, что он не так делает. Программа для работы с базой данных выводит невразумительное сообщение, вместо того, чтобы сообщить, что неверно заполнено одно из полей. Попробуем сразиться с этой разновидностью ошибок, которые досаждают нашим пользователям.


    Введение


    Вначале информация для читателей, которые не знакомы с моими предыдущими заметками. Их можно найти здесь:

    Как всегда я буду не абстрактен, а начну с примеров. В этот раз примеры будут взяты из исходного кода Firefox. Я постараюсь продемонстрировать, что даже в качественном и известном приложении с кодом для обработки ошибок, всё обстоит не самым лучшим образом. Дефекты были найдены мной с помощью анализатора PVS-Studio 4.50.

    Примеры ошибок


    Пример N1. Неполноценная проверка целостности таблицы
    int  AffixMgr::parse_convtable(..., const char * keyword)
    {
      ...
      if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
          HUNSPELL_WARNING(stderr,
                           "error: line %d: table is corrupt\n",
                           af->getlinenum());
          delete *rl;
          *rl = NULL;
          return 1;
      }
      ...
    }

    Диагностика PVS-Studio: V579 The strncmp function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. affixmgr.cpp 3708

    Здесь сделана попытка проверить целостность таблицы. К сожалению, эта проверка может сработать, а может и не сработать. Для вычисления длины ключевого слова используют оператор sizeof(), что естественно некорректно. В результате, работоспособность кода зависит от счастливого стечения обстоятельств (длины ключевого слова, размера указателя 'keyword' в текущей модели данных).

    Пример 2. Неработающая проверка при чтении файла
    int PatchFile::LoadSourceFile(FILE* ofile)
    {
      ...
      size_t c = fread(rb, 1, r, ofile);
      if (c < 0) {
        LOG(("LoadSourceFile: "
             "error reading destination file: " LOG_S "\n",
             mFile));
        return READ_ERROR;
      }
      ...
    }

    Диагностика PVS-Studio: V547 Expression 'c < 0' is always false. Unsigned type value is never < 0. updater.cpp 1179

    Это пример, когда код обработки ошибки пишется по принципу «чтобы было». Программист даже не задумался, что собственно он написал, и как это будет работать. Проверка некорректна. Функция fread() возвращает количество прочитанных байт с помощью беззнакового типа. Прототип функции:
    size_t fread( 
       void *buffer,
       size_t size,
       size_t count,
       FILE *stream 
    );

    Естественно, для хранения результата используется переменная 'c', имеющая тип size_t. Как следствие, результат проверки (c < 0) всегда ложен.

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

    Аналогичную ошибку можно увидеть и в других местах:

    V547 Expression 'c < 0' is always false. Unsigned type value is never < 0. updater.cpp 2373

    V547 Expression 'c < 0' is always false. Unsigned type value is never < 0. bspatch.cpp 107

    Пример 3. Проверка указателя на NULL уже после его использования
    nsresult
    nsFrameSelection::MoveCaret(...)
    {
      ...
      mShell->FlushPendingNotifications(Flush_Layout);
      if (!mShell) {
        return NS_OK;
      }
      ...
    }

    Диагностика PVS-Studio: V595 The 'mShell' pointer was utilized before it was verified against nullptr. Check lines: 1107, 1109. nsselection.cpp 1107

    Если указатель равен нулю, то мы должны обработать этот специальный случай и вернуть из функции NS_OK. Смущает то, что указатель mShell до этого момента уже используется.

    Скорее всего, этот код работает, так как указатель mShell всегда неравен NULL. Пример я привожу, чтобы показать, что можно допустить ошибку даже в очень простых проверках. Проверка есть, а смысла от неё нет.

    Пример 4. Проверка указателя на NULL уже после его использования
    CompileStatus
    mjit::Compiler::performCompilation(JITScript **jitp)
    {
      ...
      JaegerSpew(JSpew_Scripts,
        "successfully compiled (code \"%p\") (size \"%u\")\n",
        (*jitp)->code.m_code.executableAddress(),
        unsigned((*jitp)->code.m_size));
    
      if (!*jitp)
          return Compile_Abort;
      ...
    }

    Диагностика PVS-Studio:V595 The '* jitp' pointer was utilized before it was verified against nullptr. Check lines: 547, 549. compiler.cpp 547

    Кстати, использование указателя до проверки — распространенная ошибка. Это ещё один пример на эту тему.

    Пример 5. Неполная проверка входных значений
    PRBool
    nsStyleAnimation::AddWeighted(...)
    {
      ...
      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
          unit[0] == eCSSUnit_Null || unit[0] == eCSSUnit_URL) {
        return PR_FALSE;
      }
      ...
    }

    Диагностика PVS-Studio: V501 There are identical sub-expressions 'unit [0] == eCSSUnit_Null' to the left and to the right of the '||' operator. nsstyleanimation.cpp 1767

    Кажется, здесь сразу 2 опечатки. Затрудняюсь сказать, как именно должен выглядеть здесь код, но, наверное, хотели написать так:
    if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
        unit[0] == eCSSUnit_URL  || unit[1] == eCSSUnit_URL) {

    Из-за опечаток функция может начать обрабатывать некорректные входные значения.

    Пример 6. Неполная проверка входных значений
    nsresult PresShell::SetResolution(float aXResolution, float aYResolution)
    {
      if (!(aXResolution > 0.0 && aXResolution > 0.0)) {
        return NS_ERROR_ILLEGAL_VALUE;
      }
      ...
    }

    Диагностика PVS-Studio: V501 There are identical sub-expressions to the left and to the right of the '&&' operator: aXResolution > 0.0 && aXResolution > 0.0 nspresshell.cpp 5114

    А вот ещё один пример неудачной проверки входных параметров. В этот раз из-за опечатки не проверяется значение аргумента aYResolution.

    Пример 7. Неразыменованный указатель
    nsresult
    SVGNumberList::SetValueFromString(const nsAString& aValue)
    {
      ...
      const char *token = str.get();
      if (token == '\0') {
        return NS_ERROR_DOM_SYNTAX_ERR; // nothing between commas
      }
      ...
    }

    Диагностика PVS-Studio: V528 It is odd that pointer to 'char' type is compared with the '\0' value. Probably meant: *token == '\0'. svgnumberlist.cpp 96

    Проверка, что между запятыми ничего нет, не работает. Чтобы узнать, пустая строка или нет, можно сравнить первый символ с '\0'. Но здесь с нулем сравнивается не первый символ, а указатель. Этот указатель всегда неравен нулю. Корректная проверка должна была выглядеть так: (*token == '\0').

    Пример 8. Неподходящий тип для хранения индекса
    PRBool 
    nsIEProfileMigrator::TestForIE7()
    {
      ...
      PRUint32 index = ieVersion.FindChar('.', 0);
      if (index < 0)
        return PR_FALSE;
      ...
    }

    Диагностика PVS-Studio: V547 Expression 'index < 0' is always false. Unsigned type value is never < 0. nsieprofilemigrator.cpp 622

    Функция не вернёт PR_FALSE, если в строке нет точки и продолжит работать с некорректными данными. Ошибка в том, что для переменной 'index' выбран беззнаковый тип данных. Проверка (index < 0) не имеет смысла.

    Пример 9. Формирование неправильного сообщения об ошибке
    cairo_status_t
    _cairo_win32_print_gdi_error (const char *context)
    {
      ...
      fwprintf(stderr, L"%s: %S", context, (wchar_t *)lpMsgBuf);
      ...
    }

    Диагностика PVS-Studio: V576 Incorrect format. Consider checking the third actual argument of the 'fwprintf' function. The pointer to string of wchar_t type symbols is expected. cairo-win32-surface.c 129

    Даже если ошибка успешно обнаружена, её еще надо суметь правильно обработать. А поскольку обработчики ошибок тоже никто не тестирует, то там можно увидеть много интересного.

    Функция _cairo_win32_print_gdi_error() распечатает абракадабру. В качестве третьего аргумента функция fwprintf() ожидает указатель на unicode-строку, а вместо этого получает строку в формате 'const char *'.

    Пример 10. Ошибка записи дампа
    bool ExceptionHandler::WriteMinidumpForChild(...)
    {
      ...
      DWORD last_suspend_cnt = -1;
      ...
      // this thread may have died already, so not opening
      // the handle is a non-fatal error
      if (NULL != child_thread_handle) {
        if (0 <= (last_suspend_cnt =
                    SuspendThread(child_thread_handle))) {
      ...
    }

    Диагностика PVS-Studio: V547 Expression is always true. Unsigned type value is always >= 0. exception_handler.cc 846

    Это другой пример в обработчике ошибок. Здесь некорректно обрабатывается результат, возвращаемый функцией SuspendThread. Переменная last_suspend_cnt имеет тип DWORD, а значит она всегда будет больше или равна 0.

    О других ошибках в Firefox


    Сделаю небольшое отступление и расскажу о результатах проверки Firefox в целом. Проект качественен, и PVS-Studio выявил мало ошибок. Однако, так как он большой, то в количественном отношении ошибок достаточно много. К сожалению, мне не удалось полноценно изучить отчет, выданный инструментом PVS-Studio. Дело в том, что для Firefox отсутствует файл проекта для Visual Studio. Проект проверялся консольной версией PVS-Studio, вызываемой из make-файла. Открыв отчет в Visual Studio, можно просмотреть все диагностические сообщения. Но раз нет проекта, то Visual Studio не подсказывает, где какие переменные объявлены, не позволяет перейти в место определения макросов и так далее. В результате, анализ неизвестного проекта крайне трудоемок, и я смог изучить только часть сообщений.

    Ошибки встречаются разноплановые. Например, есть выход за границы массива:
    class nsBaseStatis : public nsStatis {
    public:
      ...
      PRUint32 mLWordLen[10]; 
      ...
      nsBaseStatis::nsBaseStatis(...)
      {
        ...
        for(PRUint32 i = 0; i < 20; i++)
           mLWordLen[i] = 0;
        ...
      }
      ...
    };

    Диагностика PVS-Studio: V557 Array overrun is possible. The value of 'i' index could reach 19. detectcharset.cpp 89

    Хотя эта и подобные ошибки интересны, они не связаны с темой данной статьи. Поэтому, если интересно, можно посмотреть на некоторые другие ошибки в этом файле: mozilla-test.txt.

    Вернемся к ошибкам в обработчиках ошибок


    Я решил привести не парочку, а 10 примеров, чтобы убедить вас в актуальности проблем наличия дефектов в обработчиках ошибок. Конечно, обработчики ошибок не самые критичные и важные участки программы. Но ведь программисты их пишут, а значит, надеются с их помощью улучшить поведение программы. К сожалению, как показывают мои наблюдения, очень часто проверки и обработчики ошибок не работают. Смотрите, мне было достаточно одного, пусть и крупного проекта, чтобы показать множество ошибок данного типа.

    Что же с этим делать и какие можно дать рекомендации?

    Первая рекомендация


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

    Конечно, писать тесты на обработчики ошибок очень сложно и часто экономически нецелесообразно. Но если программист хотя бы будет знать об опасности, это уже многое. Осведомлен, значит вооружен. С обработчиками ошибок можно привести и вот такую аналогию.

    По статистике альпинисты чаще всего падают в конце подъема. Это случается не из-за усталости, а из-за того, что человек думает, что ему осталось совсем немного. Он расслабляется, теряет внимательность и, как результат, чаще допускает ошибки. С программистом при написании кода происходит что-то похожее. Он много сил и внимания тратит на алгоритм, а различные проверки пишет не сосредотачиваясь, так как уверен, что в них он не может допустить ошибку.

    Итак, теперь вы предупреждены. И я уверен, это уже очень хорошо и полезно.

    Если вы скажите, что подобные глупые ошибки допускают только студенты и неопытные программисты, то вы не правы. Опечатки легко делают все. Предлагаю на эту тему вот эту небольшую заметку "Миф второй — профессиональные разработчики не допускают глупых ошибок". Я могу подтвердить это множеством примеров из различных проектов. Но думаю, приведенных здесь вполне достаточно, чтобы задуматься.

    Вторая рекомендация


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

    Неработающий механизм сохранения дампа не только бесполезен, он только создает видимость, что в случае беды им можно будет воспользоваться. Если пользователь пришлёт испорченный dump-файл, то он не только не поможет, а может только ввести в заблуждение и на поиски ошибок будет потрачено больше времени, чем если бы dump-файла вообще отсутствовал.

    Рекомендация выглядит простой и очевидной. Но у многих ли, из читающих эту заметку, есть юнит-тесты для проверки класса WriteMyDump ?

    Третья рекомендация


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

    Другими словами, при статическом анализе покрытие кода составляет 100%. Достичь такого покрытия кода с помощью других видов тестирования практически нереально. Покрытие кода при юнит-тестах и регрессионном тестировании обычно составляет менее 80%. Оставшиеся 20% протестировать очень сложно. В эти 20% входят большинство обработчиков ошибок и редких ситуаций.

    Четвертая рекомендация


    Можно попробовать использовать методологию внесения неисправностей. Смысл в том, что ряд функций время от времени начинают возвращать различные коды ошибок, и программа должна корректно их обрабатывать. Например, можно написать свою функцию malloc(), которая время от времени будет возвращать NULL, даже если память ещё есть. Это позволит узнать, как будет вести себя программа, когда память действительно кончится. Аналогично можно поступать с такими функциями, как fopen(), CoCreateInstance(), CreateDC() и так далее.

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

    Заключение


    Дефекты в обработчиках ошибок встречаются часто. К сожалению, я не уверен, что приведенные в статье рекомендации достаточны, чтобы их избежать. Но я надеюсь, что вас заинтересовала эта проблема, и вы сможете придумать способы сократить количество недочетов в своих программах. Также я и другие читатели будут признательны, если вы поделитесь своими идеями и методиками, которые позволят избежать ошибки рассмотренного вида.
    PVS-Studio
    Статический анализ кода для C, C++, C# и Java

    Комментарии 35

      +14
      Единорог блюет цветами переваренной иконки?
        0
        Хорошее толкование! :)
          +2
          разложил в спектр
          +2
          Скажите, а когда вы планируете сделать анализаторы для других языков?
            +2
            Пока вообще не планируем.
            +1
            К чему такая привязка VS? 1) Нельзя ли сделать независимый такой анализатор а не просто консольный? 2) Как думаете можно ли и как легко сделать например плагин к Eclipse QtCreator или другой IDE?
              +1
              PVS-Studio Output Window vs plain text stdout :

              В беседах с программистами, которые подумывают об использовании статического анализатора кода, часто можно слышать такое мнение: «Никакого особенного интерфейса для такого инструмента не надо, достаточно просто command line tool. Да и вывод (stdout) всегда можно сохранить в файл». Я хочу показать ошибочность такого подхода на примере окна результатов PVS-Studio Output Window.
                0
                Ещё в копилку, почему просто выплюнуть в stdout не лучший вариант: www.viva64.com/ru/b/0125/
                  +1
                  Так а если сгенерить красивую xml'ку или yaml'ку с результатом? А для неё viewer'ы с интеграцией в IDE запилить?
                    0
                    XML давно делается. И ее даже можно открыть, к примеру в VS — именно так мы проверяем makefile based проекты без .sln.

                    НО! Вьювер должен быть не просто вьювер. А с полноценным Intellisense. Чтобы можно было перейти к функции/переменной, посмотреть легко как объявлено, какие параметры передаются и т.п.

                    Правда об этом почему-то забывают…
                      –1
                      Так по xml'ке же можно просто накатать плагин к IDE и её вьювером воспользоваться же.
                  +2
                  Разве у того же Klocwork'а нету текстового представления ошибок? И зачем давать ссылку на 2 предложения, если можно было просто написать 2 предложения — столько же места заняло бы, только нагляднее.
                    0
                    Вы не путайте текстовое представление ошибок как один ИЗ поддерживаемых форматов и «достаточно просто выплюнуть текст».
                      +1
                      Про выплюнуть — это вы написали, вас же спрашивали про command-line tool. Потому что тогда можно было бы сделать плагин к другим IDE. И кстати, там не просили вас это сделать, лишь спросили можно ли, и в каком направлении копать.
                        +1
                        Command line tool у нас давно есть.
                          +1
                          Ок, невнимательно прочитал начальный комментария — там спрашивали про независимый анализатор, а не просто про консольную версию. Вы, кстати, тоже не на тот вопрос ответили. Хотя, если не ошибаюсь, уже писали, что в планах поддержки чего-либо кроме VS у вас не планируется. И да, посмотрев по диагонали вашу ссылку — можете как-нибудь сравнить удобство создания 2-го makefile'a для анализа и kwshell?
                            +1
                            >> можете как-нибудь сравнить удобство создания makefile

                            Это ОООЧЕНЬ сильно зависит от makefile, используемого в проекте. В простых случаях достаточно добавить в существующий файл одну строку вызова PVS-Studio.exe…
                  –3
                  Вопрос неправильно поставил похоже. Просто хотелось сказать, что в мире существует не только VS. Множество проектов вообще разрабатываются не в ней. В данной статье как раз и раскрывается то, что неудобно работать просто с текстом. Можно сделать именно независимое решение. Например на основе того же eclipse, в котором мне кажется достаточно реализовано для того же функционала, что и в VS.
                  Т.е. получается что если нет солюшена для VS, то нам будет ну уж очень неудобно.
                +4
                2) Как думаете можно ли и как легко сделать например плагин к Eclipse QtCreator или другой IDE?

                Сложно. Любой плагин сделать сложно. Даже к VS. А уж что б он еще и работал… Люди, не погруженные в задачу не могут даже приблизительно прикинуть сложность этого и какие подводные камни приходится обходить.
                  –3
                  Но попытаться всегда можно. Сделать например опенсурс проект. Нужен только хороший куратор чтоли.
                    0
                    Ну да, мы не против заиметь куратора в лице Google, Intel и т.п.
                +7
                вообще-то я всегда читаю статьи про PVS-Studio (и тут и на сайте) с интересом. Но эта по-моему вышла не очень удачной. Проверка беззнакового значения на «меньше нуля» и несоответствие параметров формату fprintf-а прекрасно отлавливается gcc (и, наверно, другими компиляторами тоже). То есть половины примеров статьи не было бы, если б разработчики Firefox-а уделяли внимание предупреждениям gcc. А проверять проект, где предупреждения компилятора игнорируются, как-то неинтересно даже. Понятно, что там можно много чего найти.

                Кроме того, Firefox — ну ни как ни пример правильной обработки ошибок. У меня правда опыт ограниченный, но во всех случаях обнаружения ошибки при запуске firefox у меня вел себя как /bin/true (ну, или /bin/false, я не проверял $?) — то есть сразу и молча завершался, не открывая ни единого окна и без сообщения об ошибке. Пересборка с debug и verbose не помогала. При том, что где-то глубоко внутри ошибка была определена почти правильно, но до пользователя она никогда не доходила.
                  +14
                  Конечно, многие из ошибок можно найти с помощью компилятора. Но ведь не находят! Возьмем самое просто — проверку беззнакового значения на «меньше нуля». Ну не получается у людей их находить компилятором. Про warning-и компиляторов пишут статьи и книги, учат использовать в форумах. Но я теперь склоняюсь к мнению, что вся эта писанина — научная фантастика :).

                  На практика, эта ошибки найти не могут в VirtualDub, Chromium, Qt, Apache, TortoiseSVN, UltimateTCPIP, Ultimate ToolBox, Tracetool, IPP Samples, DOSBox, Miranda IM, StrongDC++, TrueCrypt, ReactOS, ...

                  Я злой сказочник?

                  Если, да то, как тогда объяснить это:

                  Chromium
                  static void CharAdvance(char** buffer, size_t* buffer_size, size_t count) {
                    if (count < 0) {
                      NaClFatal("Unable to advance buffer by count!");
                    } else {
                    ...
                  }

                  Qt
                  bool equals( class1* val1, class2* val2 ) const{
                  {
                    ...
                    size_t size = val1->size();
                    ...
                    while ( --size >= 0 ){
                      if ( !comp(*itr1,*itr2) )
                        return false;
                      itr1++;
                      itr2++;
                    }
                    ...
                  }

                  Apache
                  typedef  size_t      apr_size_t;
                  APU_DECLARE(apr_status_t) apr_memcache_getp(...)
                  {
                    ...
                    apr_size_t len = 0;
                    ...
                    len = atoi(length);
                    ...
                    if (len < 0) {
                      *new_length = 0;
                      *baton = NULL;
                    }
                    else {
                      ...  
                    }
                  }

                  TortoiseSVN
                  typedef index_t revision_t;
                  ...
                  void CCacheLogQuery::InternalLog (...,
                    revision_t endRevision,  ...)
                  {
                    ...
                    // we cannot receive logs for rev < 0
                    if (endRevision < 0)
                      endRevision = 0;
                  
                    ...
                  }

                  VirtualDub
                  typedef unsigned short wint_t;
                  void lexungetc(wint_t c) {
                    if (c < 0)
                      return;
                     g_backstack.push_back©;
                  }

                  UltimateTCPIP
                  void CUT_StrMethods::RemoveSpaces(LPSTR szString) {
                    ...
                    size_t loop, len = strlen(szString);
                    // Remove the trailing spaces
                    for(loop = (len-1); loop >= 0; loop--) {
                      if(szString[loop] != ' ')
                        break;
                    }
                    ...
                  }

                  Ultimate ToolBox
                  UINT itemID;
                  ...
                  void COXAutoListBox::DrawItem(...) 
                  {
                    ...
                    if (lpDrawItemStruct->itemID>=0)
                    {
                      ...
                    }
                    ...
                  }

                  Tracetool
                  static UINT_PTR m_socketHandle ;
                  
                  void TTrace::LoopMessages(void) 
                  {
                    ...
                    // Socket creation
                    if ( (m_socketHandle = socket(AF_INET,SOCK_STREAM,0)) < 0)
                    {
                      continue;
                    }
                    ...
                  }

                  IPP Samples
                  typedef unsigned int    Ipp32u;
                  UMC::Status Init(..., Ipp32u memSize, ...)
                  {
                    ...
                    memSize -= UMC::align_value<Ipp32u>(m_nFrames*sizeof(Frame));
                    if(memSize < 0)
                        return UMC::UMC_ERR_NOT_ENOUGH_BUFFER;
                    ...
                  }

                  DOSBox
                  void SERIAL_getErrorString(char* buffer, int length) {
                    ...
                    if((length - sysmsg_offset - strlen((const char*)sysmessagebuffer)) >= 0)
                       memcpy(buffer + sysmsg_offset, sysmessagebuffer,
                       strlen((const char*)sysmessagebuffer));
                    ...
                  }

                  Подсказка: strlen возвращает size_t. А значит, проверка переполнения буфера на срабатывает.

                  Miranda IM
                  extern DWORD nMessagesCount;
                  static void MirabilisImport(HWND hdlgProgressWnd)
                  {
                    ...
                    nGroupsCount = ImportGroups();
                    if (nGroupsCount < 0) {
                      AddMessage( LPGEN("Group import was not completed."));
                      nGroupsCount = 0;
                    }
                    ...
                  }

                  StrongDC++
                  uint64_t QueueManager::FileQueue::getTotalQueueSize(){
                    uint64_t totalsize = 0;
                    ...
                    if(totalsize < 0)
                      totalsize = 0;
                    ...
                  }

                  TrueCrypt
                  BOOL SelfExtractInMemory (char *path)
                  {
                    unsigned int fileDataEndPos = 0;
                    ...
                    if (fileDataEndPos < 0)
                    {
                      Error ("CANNOT_READ_FROM_PACKAGE");
                      return FALSE;
                    }
                    ...
                  }

                  ReactOS
                  BOOL PrepareService(LPCTSTR ServiceName)
                  {
                    DWORD LeftOfBuffer;
                    ...
                    if (LeftOfBuffer < 0)
                    {
                      DPRINT1("Buffer overflow for service name: '%s'\n", ServiceName);
                      return FALSE;
                    }
                    ...
                  }
                    +2
                    ну, у нас получается исправлять то, что находит компилятор :)

                    но я собственно говорил немного другое. Не то, что «компилятора достаточно», а что «было бы интереснее/убедительнее/нагляднее демонстрировать ошибки, не обнаруживаемые компилятором». Находить ошибки по предупреждениям компилятора можно и без PVS-Studio.
                      0
                      Ну это уж какие нашлись по этой теме…

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

                      Но в целом намек понял. Буду стараться в дальнейшем подбирать что-то более необычное.
                        –1
                        Некропостинг. Вот почему надо использовать PVS-Studio :). Находим ошибки в коде компилятора GCC с помощью анализатора PVS-Studio.
                          0
                          Весьма некро, действительно.

                          А про ошибки в GCC уже прочитал, спасибо. Ждем линуксной версии с нетерпением…

                    +1
                    Читаю ваши заметки и каждый раз по-белому завидую WinC++ программистам…
                      0
                      В примерах приведен пример «дорогого» кода — кода, который дорого писать, дорого поддерживать, дорого расширять. Это не промышленный код, добится его устойчивой работы будет опять же очень дорого. В хорошем промышленном коде большинства из приведенных конструкций в принципе не будет.
                      Основные принципы промышленного кода:
                      — видимая архитектура, интерфейсы
                      — читабельность алгоритма
                      — читабельность кода (соответствие соглашениям по кодированию)
                      — отсуствие «магических» констант в коде
                      — использование scope-зависимых автоматических конструкций (смартпоинтеры, смартлокеры и т.д.)
                      — структурированная единообразная обработка ошибок
                        0
                        Просто чтобы уточнить. Это Firefox-то не промышленный код?
                          0
                          Оки, попытаюсь уточнить. Когда я писал «приведен пример «дорогого» кода» я имел в виду только это, не пытаясь экстраполировать качество кода в данных примерах на весь код приложения. А цель поста — нужно не пытаться вносить точечные исправления в такие участки а писать их сразу (или переписывать) так, чтобы подобных ошибок просто не могло возникнуть. Возможно это звучит как пустые слова, но такие техники действительно есть и основные принципы я привел выше.
                            0
                            Заглянул к вам в профиль, обратил внимание что вы генеральный директор компании — очень приятно что генеральный директор находит время для продвижения своего продукта и даже обращает внимание на комментарии :)
                            Хочу задать вам несколько вопросов по продукту:
                            1. Насколько легко пакетная проверка кода с использованием вашего продукта интегрируется с решением MS TFS как расширение процесса автоматического тестирования (запуск процесса как часть сборки, интеграция отчетов)
                            2. Есть ли проверки специфичные для технологии COM?
                              0
                              1. TFS поддерживается (http://www.viva64.com/ru/d/0006/#ID0E1AAI). Вообще поддержка систем непрерывной интеграции (да и просто проверка из командной строки) делаются легко, если есть .sln-файл и посложнее, если его нет. Но раз вы сидите на tfs, то скорее всего есть .sln. Интеграции отчетов в tfs нет, поскольку наш отчет — это не просто «текст в таблице», а более мощная система с доп. функциями. Но если Вы покажите как хотите интегрироваться в отчеты, мы это сделаем легко.

                              2 Проверок для COM кажется нет.

                              Если есть интерес к продукту, пишите в почту. Пообщаемся.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое