Андрей Карпов, Приватные байки от разработчиков...

49
Приватные байки от разработчика анализатора кода Андрей Карпов ООО «СиПроВер» www.viva64.com

Upload: sergey-platonov

Post on 16-Apr-2017

681 views

Category:

Software


2 download

TRANSCRIPT

Page 1: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Приватные байки от разработчика анализатора

кода

Андрей Карпов

ООО «СиПроВер»

www.viva64.com

Page 2: Андрей Карпов, Приватные байки от разработчиков анализатора кода

О докладчике

• Карпов Андрей Николаевич, 1981

• Присутствует на Habrahabr под именем Andrey2008 - habrahabr.ru/users/andrey2008/

• Технический директор ООО «СиПроВер»

• MVP в категории Visual C++

• Intel Black Belt Software Developer

• Один из основателей проекта PVS-Studio (статический анализатор кода для языков C/C++/C#).

www.viva64.com

Page 3: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Содержание

• Пункт N1

• Пункт N2

• Пункт N3

• Я буду рассказывать о том, о чем не могу писать в статьях.А ведь накопилось. И о странном.

www.viva64.com

Page 4: Андрей Карпов, Приватные байки от разработчиков анализатора кода

На экономический вопрос «Почему программное обеспечение такое

дорогое?» столь же экономическим ответом был бы такой: «Потому

что его пытаются получить при помощи дешевого труда». А почему

пытаются? Да потому, что присущие ему трудности повсеместно

сильно недооцениваются.

Эдсгер Дейкстра

Как так получилось, что требуются вспомогательные инструменты?

www.viva64.com

Page 5: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Чувствуется пренебрежение к сложности

При сдвиге >> знакового типа int левые биты заполняются копией знакового бита.

На самом деле, это implementation-defined.

www.viva64.com

Page 6: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Ещё пример из той же книги по электронике

Файлы 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

Page 7: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Ещё пример из той же книги по электронике

Пространство, занимаемое структурой в памяти, равняется сумме размеров всех элементов.

www.viva64.com

Page 8: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Что же делать? Да здравствуют костыли!

• Мы все молодцы. Но нас заставляют делать быстро и дёшево.

• Компромисс: вспомогательные инструменты.

• Например, отладчики, статические анализаторы кода.

• Это не хорошо и не плохо. Так устроен мир.

Page 9: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Начало. Viva64

• Актуальность: нет инструмента для поиска 64-битных ошибок

• Казалось, надо приложить немного усилий и будет много денег

• Суровая реальность

www.viva64.com

Page 10: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Стартап

• Ожидание• Я буду генерировать

интересные креативные идеи и воплощать их в жизнь

• Реальность• Сгорела проводка

• Важный сотрудник заболел

• 10 $

• 1 копейка

www.viva64.com

Page 11: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Действительность

Page 12: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Как начать?

• "Я готов отчитаться за каждый доллар, но не спрашивайте меня, как я заработал первый миллион." — Генри Форд.

• Чем только не приходится заниматься…

• Любая подработка

• Гранты

• Аутсорсинг

www.viva64.com

Page 13: Андрей Карпов, Приватные байки от разработчиков анализатора кода

2008

www.viva64.com

Page 14: Андрей Карпов, Приватные байки от разработчиков анализатора кода

2015

www.viva64.com

Page 15: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Краткая история

• Viva64

• VivaMP

• VivaCore

• PVS-Studio

• PVS-Studio + C++Builder

• CppCat

• PVS-Studio + C#

www.viva64.com

Page 16: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Особенности маркетинга для C++ программистов

www.viva64.com

Page 17: Андрей Карпов, Приватные байки от разработчиков анализатора кода

О сравнении анализаторов

•Я не могу написать, что пришлось отбирать проекты• Visual Studio падал• Cppcheck зависал

•На самом деле, всё сводится к «шарику»

www.viva64.com

Page 18: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Всегда есть кому дорого и про Хобби

Не знаю, в каком мире вы живете, но, мне кажется, что многие компании склонны переоценивать важность своего софта.

На 9000 рублей можно кормить двух человек месяц, проехать 3000 километров, пролететь четверть земли. Купить более 20 книг, сделать операцию на глазу.

www.viva64.com

Page 19: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Ну тупые (c) Задорнов

char m_buffer[BUF_SIZE];

memset( m_buffer, 0, sizeof(*m_buffer) );

В этой строке очищается столько байт, сколько хранится в первом элементе массива.

Про это некрасиво писать, но программисты часто не видят ошибки. Это грустно, так как мешает продавать инструмент.

www.viva64.com

Page 20: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 21: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 22: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Тяжела и неказиста жизнь простого продвиженца

• Помочь опубликовать я не могу, т.к. не открываю вложения

• - Так ведь в статье были ссылки.- Я не перехожу по обернутым ссылкам. (речь про bit.ly)

• Analyzer

• https

www.viva64.com

Page 23: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Как раз там, где нужен статический анализ, его продать невозможно

• Требуется не устранить максимальное количество ошибок, а пройти сертификацию.

• Заземлённые указатели

www.viva64.com

Page 24: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Байки разработчика с одной из АЭС

• «Некоторые персонажи в персонале станций могут дать фору Гомеру Симпсону»

• «Зачем нам git? Вот смотри, у меня всё в тетрадке записано».

• Ещё пример из жизни: в конструкторе копирования у класса происходит открытие файла конфигурации и полный его парсинг.

www.viva64.com

Page 25: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Он иногда присылал мне примечательные фрагменты

• В следующие две строки программист умудрился уместить как минимум 3 WTF:

inline virtual Foo& operator=(const Foo& x) {

if (&x); return *this;

}

www.viva64.com

Page 26: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 27: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Ещё впечатления

• Рядом с этим кодом был комментарий, в котором программист объяснял, что в новом стандарте C++ bool будет то же самое, что и int. Год написания комментария не известен.

#if !defined(BOOL_DEFINED)

typedef int bool;

#endif

www.viva64.com

Page 28: Андрей Карпов, Приватные байки от разработчиков анализатора кода

О разработке PVS-Studio

www.viva64.com

Page 29: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Я за Си

Любой дурак может написать код, понятный компьютеру. Хорошие

программисты пишут код, понятный людям.

Мартин Фаулер

www.viva64.com

Page 30: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Я за Си (с точки зрения разработчика PVS-Studio)

Page 31: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Свой класс строки - это норма

• Рано или поздно, в любом состоявшемся проекте появляется свой класс строки.

• Я ждал, появится ли он в PVS-Studio.

• Он появился, и это было обосновано.

• Это нормально. Не стесняйтесь это делать.

www.viva64.com

Page 32: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Самое сложное в статическом анализе: не ругаться

• Испытания C++: 105 открытых проектов

• Испытания C#: 49 открытых проектов

• Пример V501

• Пример V640

Page 33: Андрей Карпов, Приватные байки от разработчиков анализатора кода

V501.Опасным считается инфиксная операция, если совпадает левый и правый операнд.

while (X < X)

if (A == B || A == B)

www.viva64.com

Page 34: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 35: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 36: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 37: Андрей Карпов, Приватные байки от разработчиков анализатора кода

V640.Текст оформлен так, как будто выполняются все действия.

if (a==1)

b = c; foo(b);

if (a==1)

b = c;

foo(b);

www.viva64.com

Page 38: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Дьявол кроется в деталях

• Все выражения находятся на 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

Page 39: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Редкие ключевые слова и расширения

• Приходится поддерживать редкое или нестандартное

• Это скрыто от глаз пользователей

• Примеры

www.viva64.com

Page 40: Андрей Карпов, Приватные байки от разработчиков анализатора кода

VisualC++

• __forceinline

• __identifier

• __if_exists

• __if_not_exists

• __int3264

• __noop

• __pragma

• Вообще нигде не описаны: _int8, _int16, _int32, _int64

www.viva64.com

Page 41: Андрей Карпов, Приватные байки от разработчиков анализатора кода

GCC

• __float80, __float128

• __int128

• __builtin_offsetof

• __imag__

• __real__

• __inline__

• __extension__

• __asm__

www.viva64.com

Page 42: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 43: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 44: Андрей Карпов, Приватные байки от разработчиков анализатора кода

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

Page 45: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Наличие подобных конструкций связано с попыткой помочь делать макросы более безопасными. Вспомним классическую ошибку при использовании макроса 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

Page 46: Андрей Карпов, Приватные байки от разработчиков анализатора кода

GCC. Вложенные функции

double square_sum (double a, double b)

{

double square (double z)

{

return z * z;

}

return square(a) + square(b);

}

www.viva64.com

Page 47: Андрей Карпов, Приватные байки от разработчиков анализатора кода

• Ещё один пример. Перенос строки в конце файла.

• Когда люди говорят «да это максимум на неделю дел то», они просто не понимают, о чём говорят.

• 500 т.р., это дорого! Будем писать сами.

• Программисты часто не различают «поделку» и программный продукт.• Работа в разных условиях

• Масштабируемость

• Интерфейс

• Документация

• И т.д.

Редкие ключевые слова и расширения

www.viva64.com

Page 48: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Разное

• Почему нет цен на сайте?• Программисты не умеют считать

• Почему сайт www.viva64.com такой страшный?• Зато он приносит нам деньги

• Вы ещё сайт Abraxas Software не видели http://www.abxsoft.com/

• Почему единорог?• А вы что думаете?

www.viva64.com

Page 49: Андрей Карпов, Приватные байки от разработчиков анализатора кода

Готов отвечать на коварные вопросы

Андрей Карпов[email protected]