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

Post on 16-Apr-2017

678 Views

Category:

Software

2 Downloads

Preview:

Click to see full reader

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

2008

www.viva64.com

2015

www.viva64.com

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

• Viva64

• VivaMP

• VivaCore

• PVS-Studio

• PVS-Studio + C++Builder

• CppCat

• PVS-Studio + C#

www.viva64.com

Особенности маркетинга для 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

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

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

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

Андрей Карповkarpov@viva64.com

top related