Pull to refresh

Comments 54

Самый кривой из музыкального софта — Finale. Но у него, к сожалению, закрытые исходники. А из открытого интереснее было бы посмотреть на LilyPond: за всё время работы с ним я вообще никакой лажи не замечал (кроме той единственной, которая оговорена в документации, и поэтому не баг, а фича).
LilyPond есть в планах. Следите за новостями в нашем блоге.

Ну и Frescobaldi до кучи — он к Лилипонду имеет непосредственное отношение.

Encore старенький не тискали? Вот это глюкалово было. Sibelius подавал надежды, но как-то не взлетело в итоге, хотя приятный продукт на первый взгляд. Так что Finale. На Маке оно, правда, попристойнее работает, новые версии если брать. На Windows c 2003-2005 особых проблем не было. Новее если там уже да, глюк на глюке.
image
Видимо программист больше на балалайке играл, чем программировал.
Очень интересно…

V547, Expression 'adjustedOctave < 8' is always false

может он имел ввиду "'adjustedOctave <= 8'"?
Просмотр?
И потом, это мог быть «не используемый», но нужный код для отладки? Ведь если он хотел, допустим из любопытства посмотреть «а можно» транспонировать на 9 октав ( хотя у фортепиано всего 8-мь )?

Как — то Вы проверяете вязь колдовских символов, а что проверяете, домен знания, видимо нтуитивно не охватываете.

Буду глядеть дальше, если будет время. Спасибо за интересые данные по музыкальному ПО :)

V547 Expression '""' is always true. LilyPondOptionsDialog.cpp 64
***
Может Вам в бухгалтерские пастбища попастись. Верьте или нет, как то такого же жучка нашел на интерфейсе сверстки бюджета

if( budget = 0 )
{
budget = 0 + ( сложные вычисления после запятой );
}
V773 The function was exited without releasing the 'testFile' pointer. A memory leak is possible. RIFFAudioFile.cpp 561
*********
Сложный вопрос… это ведь не служебное приложение. Почему не делегировать ОС утечку ресурса озу вместе с дескриптором? Где гарантия, что попытка закрыть источник утечки, пользуясь ресурсом библиотеки стандартных сбоев, справится лучше. Ведь сбой, наверняка будет тоже, жучком ОС.

Какой такой наверняка жучок ОС? Тут скорее рассматриваются случаи вида "нет прав на чтение файла" или "файл заблокирован пишущим процессом".

>> mayorovp 30.10.17 в 08:55
>>…
>> Какой такой наверняка жучок ОС? Тут скорее рассматриваются случаи вида «нет >> прав на чтение файла» или «файл заблокирован пишущим процессом».

Для того, чтобы

V773 The function was exited without releasing the 'testFile' pointer. A memory leak is possible.

был возможен, этот блок

std::ifstream *testFile =
new std::ifstream(filename.toLocal8Bit(),
std::ios::in | std::ios::binary);

if (!(*testFile))
return UNKNOWN;

должен отработать безупречно, и вылететь в области, изящно обоначенной многоточием
"..."
А вот за ответ на вопрос «Какой такой наверняка жучок ОС?» Майкроофт и Гугл платят по 15 тыщ в твердой валюте, каждый, поштучно.

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

Вы ошибаетесь, утечка памяти возникнет при выполнении return UNKNOWN;. Область обозначенная многоточием тут ни при чем.


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

Спасибо, повеселили.

Hет, там не возникают, если он вышел

return UNKNOWN;.

значит дескриптора нет. Без дескриптора нет утечки. Вы вообще думаете, что пишете? Чтобы был ресурс, у которого есть риск утечки, код должен пройти обсуждаемый блок без ошибок и получить ресурс — дескриптор от ОС, и вылететь где -то по дороге к

testFile->close();

Как же Вы не понимете…

Так тут утечка памяти же. Под testFile память была выделена, а освободить ее забыли.


PS Вы вообще думаете, что пишете?

Указатель не на блок памяти.

std::ifstream — это обычный класс, который технически ничем не отличается от других классов. В частности, он имеет ненулевой размер.


Так что std::ifstream *testFile — это именно что указатель на блок в памяти.

Указатель не на блок памяти.
А на что тогда?

Напишите, пожалуйста, название компании в которой Вы работаете. Мне кажется, этой компании срочно требуется анализатор кода PVS-Studio и я попробую связаться с руководством. Я представляю, что там напрограммировано… :)
Я не знаю, что Вы себе представляете, но я был уверен, что файл дескриптор не выдает блок памяти, пока не припомнил одно исключение.

После чего перепроверил и нашел, что ошибся.

Исключение представляет вызов файла, которого нет.

После чего предлагаю Вам настойчиво перечитать комментарий автора(ов)

«Если с файлом возникают проблемы, то указатель testFile не освобождается при выходе из функции. Это распространённый паттерн, приводящий к утечке памяти.»
Мне кажется, что в этом контексте речь идет о другой проблеме.
При чем здесь какие-то особенные ситуации и исключения… Создаётся объект с помощью оператора new. Этот указатель не кладётся в какой-то умный указатель, а хранится в самом обыкновенном указателе. Следовательно, перед выходом из функции следует вызвать для этого указатель оператор delete. Если этого не сделать, произойдёт утечка памяти. Попутно, если объект не удаляется, это может статья причиной утечки не только памяти, но и других ресурсов (например, не будет закрыт файл). Но это другая история. Утечка памяти будет в любом случае.

Быть может, Вы путаете C++ с каким-то другим языком, где встроен сборщик мусора?
Я действительно перепутал, повторив ошибку 20-ти летней давности.

Тогда я написал студенческий проэкт, СУБД на си, и использовал указатель на NULL для оптимизации узнавания файл дескрипторов, которые не открылись.

Проэкт был годный, но вот похожую ошибку сделал при анализе похожего кода.
Не прав, указатель действительно на блок памяти.
А кто будет удалять объект, созданный с помощью оператора new?
std::ifstream *testFile =
  new std::ifstream(filename.toLocal8Bit(),
  std::ios::in | std::ios::binary);

Создали объект и положили указатель в обыкновенную переменную. Где вызов delete testFile?

Результат — утечка.
Oбъект есть дескриптор, нет дескриптора, нечего delete.
Unicorn facepalm
image

Я понимаю, это прозвучит высокомерно, но предлагаю прочитать книжку про язык Си++, прежде чем дискутировать.
При чем авторы ПО анализа кода указывают на источник проблемы буквально в следующей строке комментария:

«Если с файлом возникают проблемы, то указатель testFile не освобождается при выходе из функции. Это распространённый паттерн, приводящий к утечке памяти.»

Смысл парагафа статьи в том, что код действительно имеет некоторые, трудно уловимые огрехи, которые могут убить служебное ПО, у которого подобная утечка может стать причиной резкого снижения производительности, которое практически невозможно «поймать» пользуясь диагностикой или trace. Вдруг программа начинает работать медленно… и прекрасно «расправляет крылья» после перезапуска. В режиме обслуживания ползователей, или под stress test.

Без такого анализатора поиск источника потерь может затянутся на месяцы. Миллиардные вложения в разработку ПО могут быть потеряны.
Проблема состоит в том, что открыв файл «по живому» работа с ресурсом не заканчивается, но открывает объект, который в web назавают «сессия/сеанс» а в СУБД транзакция.

Запирать их в try / catch не решает проблему, поскольку источник во вне try/catch, и шлифовка требует полной интеграции с OS по всем векторам срыва. Простыни с спорами, как это делать, пестреют на некоторых форумах, и их очень интересно читать. Это тома, которые обычно кончаются в ассемблере, и авторы никогда не приходят к согласию. У музыкального ПО нет ресурсов, чтобы так глубоко исследовать компьютеры, а учитывая что человек пишущий ноты сидит у экрана и при срыве может отреагировать нажав на кнопку «OK/Cancel» просто не очень резонно.

Что действительно помогает, это отдельный поток, фиксирующий изменения внесённые рукой пользователя, чтобы такой вот внезапный срыв не испортил, скажем, три месяца работы композитора.
Следующий по порядку: V601 The integer type is implicitly cast to the char type. MidiEvent.cpp 181
— Oпределение тональности не верно.
Вот код:

case MIDI_KEY_SIGNATURE:
tonality = (int)midiEvent.m_metaMessage[0];

if (tonality < 0) {
sharpflat = -tonality + " flat";
} else {
sharpflat = tonality;
sharpflat += " sharp";
}

— вот спецификации:
4.1 MIDI Key Signature meta message

The MIDI key signature meta message specifies the key signature and scale of a MIDI file.

This message belongs to the category of MIDI meta messages. Since this is a meta message the MIDI event that carries this message may exist in MIDI files, but it is not sent over MIDI ports to a MIDI device.

This message consists of five bytes of data. The first byte is the status byte 0xFF, which shows that this is a meta message. The second byte is the meta message type 0x59, which shows that this is the key signature meta message. The third byte is 0x02, which shows that there are two remaining bytes. The fourth byte has values between -7 and 7 and specifies the key signature in terms of number of flats (if negative) or sharps (if positive). The fifth and last byte of the message specifies the scale of the MIDI file, where if this byte is 0 the scale is major and if the byte is 1 the scale is minor.

— Может у автора кода есть решение, которое где то затерялось. Если нет, мне кажется можно решить не используя циклы, но если не придумаю ничего интересного, напишу циклами.
Я (и думаю не только я) ничего не понял.
«ничего» состоит из сочетания кода и фрагмента миди спецификаций.

Укажите конкретно, что не понятно, по слову, с удовольствием разъясню.
> Пока это проект с самым низким качеством кода из всех в обзоре. Будем продолжать исследование дальше.

Похоже, что это общая черта музыкальных проектов. Я участвовал в разработке одного приложения из топ 10 музыкального раздела iTunes. Это был проект с худшим кодом, который я встречал за всю свою программистскую практику.
Раз пошли по муз. софту. Давайте, может тогда и фото захватить.
Например darktable
А можно где-нибудь увидеть полный лог анализа? Просто в некоторых примерах из статьи (в частности в разделе про always false и always true) части кода пропущены, и хотелось бы взглянуть и на них.
Отчёты анализатора не содержат код. Вы можете скачать исходники и найти примеры по имени файла и строке кода.
Вопрос по ошибке в Fingering.cpp 83 (там, где unconditional break): у вас же ещё одна диагностика есть (что-то типа «код не соответствует оформлению/отступам»; не помню, к сожалению, точный текст). Она в этом месте генерируется?
Была, припоминаю. Не стал дублировать просто, выписал первую попавшуюся.
Таки розегарден работает или нет?
Что касается недсотижимого кода, то это распространенное явление после больших правок — иногда лень чистить, иногда нет времени. Однако кроме как расход лишней памяти на диске у разработчика никаких последствий не несет — компилятор все это просекает, сообщает, если предупреждения не отключены, а код не генерирует в любом случае.
Во многих проектах куча классов, функций и методов, которые никогда не включаются в результат просто потому, что лень править CmakeLists.txt
Что касается недсотижимого кода, то это распространенное явление после больших правок — иногда лень чистить, иногда нет времени. Однако кроме как расход лишней памяти на диске у разработчика никаких последствий не несет — компилятор все это просекает, сообщает, если предупреждения не отключены, а код не генерирует в любом случае.

Время восхитительных историй о безопасности кода
image

А как же тогда быть, например, с уязвимостью CVE-2014-1266 в iOS? Чуть подробнее см. здесь.
Непонятно, как недостижимый код может попасть в бинарник. У меня gcc на такие штуки ругается еще при компиляции. А процедуры/функции, на которые нет ссылок, не могут попасть в бинарник в принципе, только если в составе совместно используемого объекта, а это совсем другая песня.
iOS здесь при чем? Речь же об опенсорс?
Немножко потролю. :) Если в мире опенсорса и gcc всё хорошо, то откуда же все эти ошибки берутся? Неужели, например, разработчики Valgrind не умеют использовать предупреждения компилятора?
static
Bool doHelperCallWithArgsOnStack (....)
{
  ....
   if (guard) {
      if (guard->tag == Iex_Const
          && guard->Iex.Const.con->tag == Ico_U1
          && guard->Iex.Const.con->Ico.U1 == True) {
         /* unconditional -- do nothing */
      } else {
         goto no_match; //ATC
         cc = iselCondCode( env, guard );
      }
   }
  ....
}

Подробнее.
Нормальный борщ. Очень часто в код пишутся куски того, что планируется использовать в будущем или выбрасывается какая-нибудь фигня, при этом структура переходов оставляется. Взглянешь на код и вспомнишь, что забыл бы в любом другом случае.
Я, например, всегда полный оператор if пишу со всеми вариантами и скобками, даже если никогда в них никакого кода не будет. Например:
if (a == b) {
do_something();
} else {
}

И дрючу тех, кто пишет так:
if (a == b) {
do_something();
}

или, не приведи бог, так
if (a == b)
do_something();

Иногда выбрасывается большая часть кода с помощью goto, что гораздо безопаснее, чем комментить блок.
Что вы понимаете под словом «безопаснее»?
Проблема недостижимого кода — не в том коде который попал в бинарник и получил управление, а в том коде который не попал в бинарник или не получил управление.
Не совсем понятно, в чем же проблема с кодом, который не попал в бинарник?
В том, что это уже смахивает на логическую ошибку, которую удалось выявить с помощью статического анализа.

Если код есть — значит, его кто-то писал. Если код кто-то писал — это делалось с какой-то целью. Если код в итоге не попал в бинарник — цель не достигнута.

V670 редкая, скорее всего, потому что gcc (да и вообще многие компиляторы) ругаются на разный порядок переменных в объявлении и в конструкторе.


Скажем, вот на такое


#include <iostream>
using namespace std;

struct Test {
    int var1;
    int var2;
    int var3;
    Test() : var2(42), var1(var2), var3(var1) {}

    void run() {
        cout << var1 << " " << var2 << " " << var3 << endl;
    }
};

int main() {
    Test test;
    test.run();
    return 0;
}

gcc ругается как-то так


test.cpp: In constructor 'Test::Test()':
test.cpp:6:6: warning: 'Test::var2' will be initialized after [-Wreorder]
  int var2;
      ^~~~
test.cpp:5:6: warning:   'int Test::var1' [-Wreorder]
  int var1;
      ^~~~
test.cpp:8:2: warning:   when initialized here [-Wreorder]
  Test() : var2(42), var1(var2), var3(var1) {}
  ^~~~

а clang чуть поумнее и поэтому ругается как-то так:


test.cpp:8:11: warning: field 'var2' will be initialized after field 'var1'
      [-Wreorder]
        Test() : var2(42), var1(var2), var3(var1) {}
                 ^
test.cpp:8:26: warning: field 'var2' is uninitialized when used here [-Wuninitialized]
        Test() : var2(42), var1(var2), var3(var1) {}
                                ^
2 warnings generated.
Тут вообще много всего сделано плохо, как я посмотрю. Лично для меня всегда является загадкой смешивание базовых объектов Qt и STL (строки, работа с файлами и т.д.) при полном непонимании их работы.

Берем этот пример:
bool
MidiFile::write(const QString &filename)
{
  std::ofstream *midiFile =
    new std::ofstream(filename.toLocal8Bit(),
                        std::ios::out | std::ios::binary);

  if (!(*midiFile)) {
    RG_WARNING << "write() - can't write file";
    m_format = MIDI_FILE_NOT_LOADED;
    return false;
  }
  ....
  midiFile->close();

  return true;
}


Параметром передается путь файла типа QString. Потом зачем-то вместо QFile используется ofstream. Потом строка конвертируется с помощью toLocal8Bit, то есть с большой вероятностью не latin символы потеряются. Ну и потом все эти проблемы с утечками.
Утечки выявляются и без необходимости купить PVS — вальгринд рулит не хуже.
valgrind, безусловно, софт уровня маст хэв. Хотя им нужно очень тщательно гонять софт, чтобы найти все проблемы, что не всегда легко.

Но в данном участке кода (и, по всей видимости, не только в нем) проблема от не очень хорошего знания самих плюсов, и фреймворка Qt в частности. Серьезно, ошибки уровня студентов 1 курса.
Sign up to leave a comment.