Андрей Карпов, Приватные байки от разработчиков...
TRANSCRIPT
Приватные байки от разработчика анализатора
кода
Андрей Карпов
ООО «СиПроВер»
www.viva64.com
О докладчике
• Карпов Андрей Николаевич, 1981
• Присутствует на Habrahabr под именем Andrey2008 - habrahabr.ru/users/andrey2008/
• Технический директор ООО «СиПроВер»
• MVP в категории Visual C++
• Intel Black Belt Software Developer
• Один из основателей проекта PVS-Studio (статический анализатор кода для языков C/C++/C#).
www.viva64.com
Содержание
• Пункт N1
• Пункт N2
• Пункт N3
• Я буду рассказывать о том, о чем не могу писать в статьях.А ведь накопилось. И о странном.
www.viva64.com
На экономический вопрос «Почему программное обеспечение такое
дорогое?» столь же экономическим ответом был бы такой: «Потому
что его пытаются получить при помощи дешевого труда». А почему
пытаются? Да потому, что присущие ему трудности повсеместно
сильно недооцениваются.
Эдсгер Дейкстра
Как так получилось, что требуются вспомогательные инструменты?
www.viva64.com
Чувствуется пренебрежение к сложности
При сдвиге >> знакового типа int левые биты заполняются копией знакового бита.
На самом деле, это implementation-defined.
www.viva64.com
Ещё пример из той же книги по электронике
Файлы file1.c, file2.c, file3.c можно подключить с помощью директивы #include
#include "file1.h"
#include "file2.h"
#include "file3.h"
#include "file1.c"
#include "file2.c"
#include "file3.c"
www.viva64.com
Ещё пример из той же книги по электронике
Пространство, занимаемое структурой в памяти, равняется сумме размеров всех элементов.
www.viva64.com
Что же делать? Да здравствуют костыли!
• Мы все молодцы. Но нас заставляют делать быстро и дёшево.
• Компромисс: вспомогательные инструменты.
• Например, отладчики, статические анализаторы кода.
• Это не хорошо и не плохо. Так устроен мир.
Начало. Viva64
• Актуальность: нет инструмента для поиска 64-битных ошибок
• Казалось, надо приложить немного усилий и будет много денег
• Суровая реальность
www.viva64.com
Стартап
• Ожидание• Я буду генерировать
интересные креативные идеи и воплощать их в жизнь
• Реальность• Сгорела проводка
• Важный сотрудник заболел
• 10 $
• 1 копейка
www.viva64.com
Действительность
Как начать?
• "Я готов отчитаться за каждый доллар, но не спрашивайте меня, как я заработал первый миллион." — Генри Форд.
• Чем только не приходится заниматься…
• Любая подработка
• Гранты
• Аутсорсинг
www.viva64.com
Краткая история
• Viva64
• VivaMP
• VivaCore
• PVS-Studio
• PVS-Studio + C++Builder
• CppCat
• PVS-Studio + C#
www.viva64.com
О сравнении анализаторов
•Я не могу написать, что пришлось отбирать проекты• Visual Studio падал• Cppcheck зависал
•На самом деле, всё сводится к «шарику»
www.viva64.com
Всегда есть кому дорого и про Хобби
Не знаю, в каком мире вы живете, но, мне кажется, что многие компании склонны переоценивать важность своего софта.
На 9000 рублей можно кормить двух человек месяц, проехать 3000 километров, пролететь четверть земли. Купить более 20 книг, сделать операцию на глазу.
www.viva64.com
Ну тупые (c) Задорнов
char m_buffer[BUF_SIZE];
memset( m_buffer, 0, sizeof(*m_buffer) );
В этой строке очищается столько байт, сколько хранится в первом элементе массива.
Про это некрасиво писать, но программисты часто не видят ошибки. Это грустно, так как мешает продавать инструмент.
www.viva64.com
ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
memcpy( mat, src, sizeof( src ) );
}
Ну тупые (c) Задорнов
V511. The sizeof() operator returns size of the pointer, and not of the array, in 'sizeof(src)' expression.
Except it doesn't. The sizeof() operator returns the size of the object, and src is not a pointer - it is a float[3][3]. sizeof() correctly returns 36 on my machine.
Позже: Oop - yup - I essentially tested with with "char A[100];" rather than "void Foo(char B[100])"
www.viva64.com
SHFILEOPSTRUCT Data;
....
Data.pTo = L"";
Описание тоже никто не читает
V540 Member 'pTo' should point to string terminated by two 0 characters.
В C++ sizeof(*L"") == sizeof(wchar_t) == 2,так что ошибки тут нет.
www.viva64.com
Тяжела и неказиста жизнь простого продвиженца
• Помочь опубликовать я не могу, т.к. не открываю вложения
• - Так ведь в статье были ссылки.- Я не перехожу по обернутым ссылкам. (речь про bit.ly)
• Analyzer
• https
www.viva64.com
Как раз там, где нужен статический анализ, его продать невозможно
• Требуется не устранить максимальное количество ошибок, а пройти сертификацию.
• Заземлённые указатели
www.viva64.com
Байки разработчика с одной из АЭС
• «Некоторые персонажи в персонале станций могут дать фору Гомеру Симпсону»
• «Зачем нам git? Вот смотри, у меня всё в тетрадке записано».
• Ещё пример из жизни: в конструкторе копирования у класса происходит открытие файла конфигурации и полный его парсинг.
www.viva64.com
Он иногда присылал мне примечательные фрагменты
• В следующие две строки программист умудрился уместить как минимум 3 WTF:
inline virtual Foo& operator=(const Foo& x) {
if (&x); return *this;
}
www.viva64.com
if(a_input_a) a_input_a2 = adupl(a_input_a, type_array1);else a_input_a2 = 0L;if(a_time_a) a_time_a2 = adupl(a_time_a, type_array1);else a_time_a2 = 0L;if(a_update_ia) a_update_a2 = adupl(a_update_ia, type_array);else a_update_a2 = 0L;if(a_input_d) a_input_d2 = adupl(a_input_d, type_array1);else a_input_d2 = 0L;if(a_time_d) a_time_d2 = adupl(a_time_d, type_array1);else a_time_d2 = 0L;if(a_update_id) a_update_d2 = adupl(a_update_id, type_array);else a_update_d2 = 0L;if(a_input_av) a_input_av2 = adupl(a_input_av, type_array1);else a_input_av2 = 0L;if(a_time_av) a_time_av2 = adupl(a_time_av, type_array1);else a_time_av2 = 0L;if(a_update_iav) a_update_av2 = adupl(a_update_iav, type_array);else a_update_av2 = 0L;if(a_input_dv) a_input_dv2 = adupl(a_input_dv, type_array1);else a_input_dv2 = 0L;if(a_time_dv) a_time_dv2 = adupl(a_time_dv, type_array1);else a_time_dv2 = 0L;if(a_update_idv) a_update_dv2 = adupl(a_update_idv, type_array);else a_update_dv2 = 0L;if(a_input_df) a_input_df2 = adupl(a_input_df, type_array1);else a_input_df2 = 0L;if(a_time_df) a_time_df2 = adupl(a_time_df, type_array1);else a_time_df2 = 0L;if(a_update_idf) a_update_df2 = adupl(a_update_idf, type_array);else a_update_df2 = 0L;
Разгребая Авгиевы конюшни
www.viva64.com
Ещё впечатления
• Рядом с этим кодом был комментарий, в котором программист объяснял, что в новом стандарте C++ bool будет то же самое, что и int. Год написания комментария не известен.
#if !defined(BOOL_DEFINED)
typedef int bool;
#endif
www.viva64.com
Я за Си
Любой дурак может написать код, понятный компьютеру. Хорошие
программисты пишут код, понятный людям.
Мартин Фаулер
www.viva64.com
Я за Си (с точки зрения разработчика PVS-Studio)
Свой класс строки - это норма
• Рано или поздно, в любом состоявшемся проекте появляется свой класс строки.
• Я ждал, появится ли он в PVS-Studio.
• Он появился, и это было обосновано.
• Это нормально. Не стесняйтесь это делать.
www.viva64.com
Самое сложное в статическом анализе: не ругаться
• Испытания C++: 105 открытых проектов
• Испытания C#: 49 открытых проектов
• Пример V501
• Пример V640
V501.Опасным считается инфиксная операция, если совпадает левый и правый операнд.
while (X < X)
if (A == B || A == B)
www.viva64.com
V501. Дьявол кроется в деталях
• X*X
• while (*p++ == *a++ && *p++ == *a++)
• Слева и справа находятся числовые литералыif (0 == 0)… 15 | 15 …
• #define M1 100#define M2 100if (x == M1 || x == M2)
• float x = foo();if (x == x)
www.viva64.com
V501. Дьявол кроется в деталях
• / или - применяются к числовым константам: 1./1.
• Строка из Zlib:if (opaque) items += size - size; / * make compiler happy * /
• rand() - rand()rand() % N - rand() % N
• Слева и справа от '|', '&', '^', '%' находятся классы.if (str == str) – ищемif (vect ^ vect) – лучше пропускать
• sizeof(__int64) < sizeof(__int64)
www.viva64.com
V501. Дьявол кроется в деталях
• 0 << 31 | 0 << 30 | ...(0 << 6) | (0 << 3) | …
• '0' == 0x30 && 'A' == 0x41 && 'a' == 0x61
• Эта шаблонная функция для определения NaN чисел.
• Read(x) && Read(x)
• #define USEDPARAM(p) ((&p) == (&p)) и прочие
• Слева и справа расположен вызов функций с такими именами как pop, _pop.
• И так далее
www.viva64.com
V640.Текст оформлен так, как будто выполняются все действия.
if (a==1)
b = c; foo(b);
if (a==1)
b = c;
foo(b);
www.viva64.com
Дьявол кроется в деталях
• Все выражения находятся на 1 строке: if (a==1) b = c; F(b);
• В разных строках не совпадает количество символов табуляции
• if (a)return x;foo();
• else else else for(..;..;..)foo(); foo(); foo();foo(); foo(); foo();
• Присутствуют #if, #else, #endif, #pragma и т.п.
• И так далее.www.viva64.com
Редкие ключевые слова и расширения
• Приходится поддерживать редкое или нестандартное
• Это скрыто от глаз пользователей
• Примеры
www.viva64.com
VisualC++
• __forceinline
• __identifier
• __if_exists
• __if_not_exists
• __int3264
• __noop
• __pragma
• Вообще нигде не описаны: _int8, _int16, _int32, _int64
www.viva64.com
GCC
• __float80, __float128
• __int128
• __builtin_offsetof
• __imag__
• __real__
• __inline__
• __extension__
• __asm__
www.viva64.com
GCC
• struct { int a; int b; } X = { a : 0, b : 1 };
• func c_open(name *byte, mode int, perm int) int __asm__ ("open");
• __is_same (type1, type2)
• Some_Class A __attribute__ ((init_priority (2000)));Some_Class B __attribute__ ((init_priority (543)));
www.viva64.com
GCC (есть даже полезное для простых программистов, но лучше не использовать)
При использовании оператора "switch", вы можете задать диапазон значений
для "case".
switch (x)
{
case 1 ... 5: Foo1(); break;
case 'A' ... 'Z': Foo1(); break;
}
a = x ?: y; a = x ? x : y;
www.viva64.com
GCC. Аналог анонимных функций для Си
int Foo(int a) {
int x = Foo1(a);
Foo2();
return x;
}
// Можно записать так:
int Foo(int a) {
return ({ int x = Foo1(a); Foo2(); x; });
}
www.viva64.com
Наличие подобных конструкций связано с попыткой помочь делать макросы более безопасными. Вспомним классическую ошибку при использовании макроса max:
#define max(a,b) ((a) > (b) ? (a) : (b))
x = max(a++, b);
Операция "++" будет выполнена 2 раза, что не соответствует задуманному программистом. Чтобы этого избежать, предлагается написать так:
#define maxint(a,b) \
({int _a = (a), _b = (b); _a > _b ? _a : _b; })
www.viva64.com
GCC. Вложенные функции
double square_sum (double a, double b)
{
double square (double z)
{
return z * z;
}
return square(a) + square(b);
}
www.viva64.com
• Ещё один пример. Перенос строки в конце файла.
• Когда люди говорят «да это максимум на неделю дел то», они просто не понимают, о чём говорят.
• 500 т.р., это дорого! Будем писать сами.
• Программисты часто не различают «поделку» и программный продукт.• Работа в разных условиях
• Масштабируемость
• Интерфейс
• Документация
• И т.д.
Редкие ключевые слова и расширения
www.viva64.com
Разное
• Почему нет цен на сайте?• Программисты не умеют считать
• Почему сайт www.viva64.com такой страшный?• Зато он приносит нам деньги
• Вы ещё сайт Abraxas Software не видели http://www.abxsoft.com/
• Почему единорог?• А вы что думаете?
www.viva64.com
Готов отвечать на коварные вопросы
Андрей Карпов[email protected]