Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 15.04.15 13:35
Оценка: 113 (12) +1
Всем доброго дня!

Мы написали фреймворк для юнит тестирования памяти .Net приложений. Хотелось бы обсудить насколько это может быть интересно и вообще получить фидбэк по функциональности.

Фреймворк распространяется в виде нугет пакета (https://www.nuget.org/packages/JetBrains.DotMemoryUnit/) и совершенно бесплатен. Все что необходимо иметь это юниттест раннер от джетбрейнса, то есть иметь установленный ReSharper 9.1 или dotCover 3.1.
Отдельный "запускатель" таких тестов нами запланирован на будущее.

Что умеет фреймворк:

— выбирать объекты по типам, интерфейсам, неймспейсам, поколениям
— сравнивать срезы памяти — выбирать новые объекты, умершие, выжившие
— добывать информацию о мемори траффике, в том числе поддерживаются ассерты на траффик с помощью атрибутов.
— у любой выборки объектов можно посмотреть количество памяти и объектов
— фреймворк интегрируется с NUnit и MSTest (вообще говоря с любым юнит тест фреймворком)

Краткое описание использования: http://blog.jetbrains.com/dotnet/2015/03/04/unit-testing-and-memory-profiling-can-they-be-combined/ (англ.)
Страничка со ссылками на хэлп и инструкциями по инсталляции: https://www.jetbrains.com/dotmemory/unit/ (англ.)

P.S.: Почему здесь, а не в jetbrains форуме? Потому что он бесплатный — это раз. И потому что хочется узнать насколько юнит тестирование памяти интересно — это два, а здесь аудитория, наверное, побольше.
Re: Юнит тестирование "памяти"
От: Jack128  
Дата: 15.04.15 14:24
Оценка:
Здравствуйте, fedor.reznik, Вы писали:

Как раз на прошлой неделе побывал эту штуку. В принципе вещь прикольная и даже местами полезная. Но непонятно, вот вылез у меня в тесте мемлик. OK, как теперь этот тест запустить под дот мемери, чтоб уже детально разобраться, в чем причина?
Re[2]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 15.04.15 14:33
Оценка: 6 (1)
J>В принципе вещь прикольная и даже местами полезная. Но непонятно, вот вылез у меня в тесте мемлик. OK, как теперь этот тест запустить под дот мемери, чтоб уже детально разобраться, в чем причина?

Спасибо, за отзыв.

Идея была в том, что Вам как бы и не надо запускать под dotMemory, если тест упал по ассерту и вы используете fluent api https://www.jetbrains.com/dotmemory/unit/help/Working_with_Memory.html, то в тестовом аутпуте будет ссылка на сохраненный воркспейс — можно по ней кликнуть и dm его откроет. Что, как, куда и в каких количествах сохранять конфигурируется с помощью атрибута DotMemoryUnit https://www.jetbrains.com/dotmemory/unit/help/Configuring_dotMemory_Unit.html, причем он иерархичен — то есть позволяет настроить значения для ассембли, а потом подтюнить на уровне тест классов и тест методов. Если же вы используете альтернативный подход (https://www.jetbrains.com/dotmemory/unit/help/Alternative_Way_of_Working_with_Memory.html), то вам в конце теста достаточно написать dotMemoryApi.SaveCollectedData(<путь>).
Re: Юнит тестирование "памяти"
От: baranovda Российская Империя  
Дата: 15.04.15 23:12
Оценка:
Здравствуйте, fedor.reznik, Вы писали:

FR>Всем доброго дня!

FR>Мы написали фреймворк для юнит тестирования памяти .Net приложений. Хотелось бы обсудить насколько это может быть интересно и вообще получить фидбэк по функциональности.

Дурацкий вопрос можно? Ловятся только утечки .Net или unmanaged тоже может?
Отредактировано 15.04.2015 23:17 α . Предыдущая версия .
Re[2]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 16.04.15 10:18
Оценка:
Здравствуйте, baranovda, Вы писали:

B>Дурацкий вопрос можно? Ловятся только утечки .Net или unmanaged тоже может?


Unmanaged не может( Но имею встречный вопрос: по каким критериям хотелось бы кверить куски unmanged память из юнит тестов написанных на .Net? А то она ведь совсем "разная" и просто мерить ее объем, имхо, большого смысла не имеет.
Re[3]: Юнит тестирование "памяти"
От: Sinix  
Дата: 16.04.15 11:09
Оценка: 59 (2) +1
Здравствуйте, fedor.reznik, Вы писали:

FR>Unmanaged не может( Но имею встречный вопрос: по каким критериям хотелось бы кверить куски unmanged память из юнит тестов написанных на .Net? А то она ведь совсем "разная" и просто мерить ее объем, имхо, большого смысла не имеет.

Если речь про утечки, порождённые managed-кодом, то я бы смотрел на наследников UnsafeHandle и на IntPtr.
Это и с текущим API в принципе можно сделать.


Да, такое пожелание: при дизайне API начинайте со сценариев использования

Серьёзно, примеры безумно многословны (отдельное фу-фу-фу за примеры картинками без ссылки на текст).
Большая часть прекрасно сводится к хелперам для типовых сценариев аля
MemAssert.WillNotLeak<SomeObj>(Threshold.FromKilobytes(12));

Не, конечно можно (и нужно) добавить подходящие хелперы самостоятельно, но как минимум заготовки (чтобы подсказать направление движения) должны присутствовать из коробки.


И, если уж речь зашла про работу с памятью, есть ли поддержка тестов для декларирования побочных эффектов?
Например, есть код на partially immutable-типах, было бы очень здорово находить ошибки вида "объект изменяется, хотя не должен".

В ту же степь — проверки для pure-функций (речь об вот этих вот),
или проверки вида "состояние объекта x эквивалентно состоянию на момент вот этого снапшота" — полезно для отладки автоматов, визиторов, undo-redo и тд и тп.

В принципе часть решается ручной расстановкой отладочных ассертов, но кто будет следить, что все ассерты расставлены верно?
Re[4]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 16.04.15 11:39
Оценка: 36 (1)
Здравствуйте, Sinix, Вы писали:

S>Да, такое пожелание: при дизайне API начинайте со сценариев использования


Так в общем-то мы с них и начинали. Это так сказать продукт не в последнюю очередь выросший из производственной необходимости)

S>Серьёзно, примеры безумно многословны (отдельное фу-фу-фу за примеры картинками без ссылки на текст).

За фу-фу-фу, посыпаю голову пеплом, но ничего сделать не могу такие вот движки у нас на сайте(

S>Большая часть прекрасно сводится к хелперам для типовых сценариев аля

S>
S>MemAssert.WillNotLeak<SomeObj>(Threshold.FromKilobytes(12));
S>

S>Не, конечно можно (и нужно) добавить подходящие хелперы самостоятельно, но как минимум заготовки (чтобы подсказать направление движения) должны присутствовать из коробки.

В первой версии хотелось предоставить максимально гибкий инструмент, а потом посмотреть на "хотелки". Кроме того, очевидно ли Вам, что строчка MemAssert.WillNotLeak<SomeObj>(Threshold.FromKilobytes(12)) — приведет к взятию снапшота (не быстрая операция) и этот ассерт валиден только в данной точке? А еще нам совсем не хотелось писать свой язык ассертов, чтобы не ломать экспириенс.

Правильно ли я понял, что Ваш ассерт сводится к такому:

dotMemory.Check(memory => {
    var os = memory.GetObjects(where => where.Type.Is<SomeObj>());
    Assert.That(os.SizeInBytes, Is.LessThan(12*1024));
});


А если хочется ассертить сразу несколько вещей (типов)? Лямбда это позволяет. Хотя можно оттопырить таких методов у чекпойнта. Подумаем.

S>И, если уж речь зашла про работу с памятью, есть ли поддержка тестов для декларирования побочных эффектов?

S>Например, есть код на partially immutable-типах, было бы очень здорово находить ошибки вида "объект изменяется, хотя не должен".

S>В ту же степь — проверки для pure-функций (речь об вот этих вот),

S> или проверки вида "состояние объекта x эквивалентно состоянию на момент вот этого снапшота" — полезно для отладки автоматов, визиторов, undo-redo и тд и тп.

А вот это интересно! Осталось только договориться что такое "измененный объект" и "состояние объекта"?
Например, в dotMemory мы умеем показывать значение филдов, соответственно, здесь это тоже реализуемо.
Re[5]: Юнит тестирование "памяти"
От: Sinix  
Дата: 16.04.15 13:24
Оценка: 10 (1) +1
Здравствуйте, fedor.reznik, Вы писали:

FR>Так в общем-то мы с них и начинали. Это так сказать продукт не в последнюю очередь выросший из производственной необходимости)

Тогда дважды ок
Из практики — все хорошие проекты так и начинались.


FR>В первой версии хотелось предоставить максимально гибкий инструмент, а потом посмотреть на "хотелки".

Не, само общее API обязательно должно быть. Проблема в том, что API никак не подсказывает пользователю, как ему решать свои проблемы.
Вместо этого на пользователя вываливается стартовый пример с готовым карго-решением (aka делай вот так и всё будет зашибись).

Причём один и тот же по сути пример в блоге записан тремя разными подходами. Иногда "Assert.That", иногда "Assert.AreEqual". Такая мешанина ещё больше запутывает.

В общем надо или писать понятное API, начиная с сценариев со стороны пользователя, или строить какую-то ментальную модель, и уже по ней обучать пользователя.
Чтобы тот понял, что главная фишка в api от dotMemory, и что смотреть надо на него, а не на 5 способов записать один и тот же Assert


FR>Кроме того, очевидно ли Вам, что строчка MemAssert.WillNotLeak<SomeObj>(Threshold.FromKilobytes(12)) — приведет к взятию снапшота (не быстрая операция) и этот ассерт валиден только в данной точке? А еще нам совсем не хотелось писать свой язык ассертов, чтобы не ломать экспириенс.

Хороший вопрос

Скорее всего тут нужен метод с делегатом, как в "dotMemory.Check(()=>...)", или оборачивание всей проверяемой области в using. Второе, наверно, будет лучше.

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

Ну, или начинать API с конкретных сценариев: сначала написать "код мечты", а затем сообразить, как подогнать под него более общее API.


FR>Правильно ли я понял, что Ваш ассерт сводится к такому:

Ага.

FR>А если хочется ассертить сразу несколько вещей (типов)? Лямбда это позволяет. Хотя можно оттопырить таких методов у чекпойнта. Подумаем.

Угу. Наглядный пример, как легко упустить важные моменты в погоне за красивым кодом
В принципе решается оборачиванием в using аля
using (WillNotLeak(someCondition))
{
  // some code
}


или той же лямбдой. Но это надо на реальном коде проверять смначала. Сорри, что повторяюсь но тут лучше перестраховаться, чем потом объяснять, что имел в виду.




FR>А вот это интересно! Осталось только договориться что такое "измененный объект" и "состояние объекта"?

FR>Например, в dotMemory мы умеем показывать значение филдов, соответственно, здесь это тоже реализуемо.

Навскидку тут всего два варианта:

1. Сравнение графа объектов. Примитивы понятно как сравнивать, типы с IEquatable — тоже, остальное — обходом графа, циклы пропускать
2. Через code instrumentation отслеживать изменения полей. Намного сложнее и куда больше шансов ложных срабатываний

Дорогое конечно удовольствие, но другого способа тестировать самые адские баги типа таких
Автор: nikov
Дата: 02.07.14
никто пока не придумал.
Re[6]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 16.04.15 13:38
Оценка:
Здравствуйте, Sinix, Вы писали:

Спасибо, за дельный фидбэк

Идея про тесты на иммутабельность, в частности, и состояния объекта, в общем, очень интересная.

Будем мозговать.
Отредактировано 16.04.2015 13:38 fedor.reznik . Предыдущая версия .
Re: Юнит тестирование "памяти"
От: Tom Россия http://www.RSDN.ru
Дата: 17.04.15 13:55
Оценка:
Интересная мысль, интересная затея.
Единственное что область применения довольно узкая.
Народная мудрось
всем все никому ничего(с).
Re[2]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 17.04.15 14:24
Оценка: 18 (1)
Здравствуйте, Tom, Вы писали:

Tom>Интересная мысль, интересная затея.

Tom>Единственное что область применения довольно узкая.

А почему, собственно, на Ваш взгляд узкая?
Я, понимаю, что профилировать перформанс и память — это операция, к сожалению, не частая. Ибо лень, и вообще пока петух не клюнет...
С этим бороться можно только просветительством и все равно будет лень

Но тут то! Есть какой-нибудь TabCollectionVM, есть в нем TabVM c командой Close — написал один раз тест на то, что если после вызова команды Close у, например, последнего табика TabVM`ов больше нет — и спи спокойно.
Или что при переоткрытии документа в программе старого больше нет (какой-нить IDocument в одном экземпляре).
Или алгоритм какой-нить — все у него хорошо, но только за время работы на определенных данных он генерит 17Gb мемори трафика, что приводит к необъяснимым тормозам — хотя по задумке все должно быть O(log N).

Это все примеры из жизни, из чего продукт и родился.

Казалось бы поймал сценарный лик -> написал тест -> радуешься. Все как с юнит тестами на баги.
Да может тут не совсем уж юнит тесты в вакууме, когда тестируется одна сущность, а скорее black/white box тестирование. Но может оно и к лучшему?
В конце концов, не у всех код идеально юнит тестируем, а у те у кого все же 100% TDD — всегда смогут собрать себе необходимый набор компонентов.

Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?
Re[3]: Юнит тестирование "памяти"
От: Tesh США  
Дата: 19.04.15 19:45
Оценка:
Здравствуйте, fedor.reznik, Вы писали:

FR>Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?


Не скажу за весь софт, но мне за 8 лет приходилось заниматься профилированием раза 2-3.
И каждый раз это было вызвано наличием какой-то проблемы.

Если нагрузочное тестирование, либо боевая эксплуатация не выявили проблем, то смысл что-то профилировать? Выглядит как преждевременная оптимизация.
Отредактировано 19.04.2015 19:45 Tesh . Предыдущая версия .
Re[4]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 20.04.15 08:24
Оценка:
Здравствуйте, Tesh, Вы писали:

T>Не скажу за весь софт, но мне за 8 лет приходилось заниматься профилированием раза 2-3.

T>И каждый раз это было вызвано наличием какой-то проблемы.

T>Если нагрузочное тестирование, либо боевая эксплуатация не выявили проблем, то смысл что-то профилировать? Выглядит как преждевременная оптимизация.


Да, это правда. В типичных случаях, что-то заранее профилировать никто не будет.

С другой стороны, я участвовал как минимум в трех коммерчески успешных проектах, в которых, не было тестов. Совсем. От слова вообще.
Значит ли это, что тесты писать — это преждевременно себя напрягать? Наверное, нет.

Далее, если проблема возникла, то обычно ее фиксить дорого (потому что надо срочно) и, за частую, поздно.
Поэтому очевидные сценарии, имхо, имеет смысл профилировать перед каждым релизом — Вы же не выпускаете продукт без тестирования?

И dotMemoryUnit — это такое средство автоматизировать этот процесс, а вовсе не yet another profiler)
И именно в этом смысле, как мне кажется, он имеет гораздо более широкое применение.
Re[5]: Юнит тестирование "памяти"
От: Tesh США  
Дата: 20.04.15 13:28
Оценка:
Здравствуйте, fedor.reznik, Вы писали:

FR>С другой стороны, я участвовал как минимум в трех коммерчески успешных проектах, в которых, не было тестов. Совсем. От слова вообще.

FR>Значит ли это, что тесты писать — это преждевременно себя напрягать? Наверное, нет.

Если там не было тестов, значит их наличие не было экономически обосновано. Тесты не бесплатны и требуют время как на написание, так и на поддержку.

FR>Далее, если проблема возникла, то обычно ее фиксить дорого (потому что надо срочно) и, за частую, поздно.

FR>Поэтому очевидные сценарии, имхо, имеет смысл профилировать перед каждым релизом — Вы же не выпускаете продукт без тестирования?

Для этого существуют приемочное и нагрузочное тестирования.

FR>И dotMemoryUnit — это такое средство автоматизировать этот процесс, а вовсе не yet another profiler)

FR>И именно в этом смысле, как мне кажется, он имеет гораздо более широкое применение.

Обычные тесты помогают убедиться в том, что код работает корректно с точки зрения бизнес-логики или архитектуры. Это по идее чаще востребовано, чем покрытие кода тестами для ответа на вопрос "а не течет ли чего" или "эффективно ли выделяется память". Такие тесты тоже могут пригодиться, но не думаю что они будут столь же широко распространены как тесты логики.
Отредактировано 20.04.2015 13:33 Tesh . Предыдущая версия . Еще …
Отредактировано 20.04.2015 13:32 Tesh . Предыдущая версия .
Re[5]: Юнит тестирование "памяти"
От: Tesh США  
Дата: 20.04.15 13:31
Оценка:
Здравствуйте, fedor.reznik, Вы писали:

Немного оффтопика.
Для dotMemory планируется добавление возможности загрузки дампов памяти?
Re[6]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 20.04.15 13:33
Оценка:
Здравствуйте, Tesh, Вы писали:

T>Для этого существуют приемочное и нагрузочное тестирования.


Так вот предлагается частично автоматизировать это приемочно/нагрзочное тестрирование
Re[6]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 20.04.15 13:39
Оценка:
Здравствуйте, Tesh, Вы писали:

T>Здравствуйте, fedor.reznik, Вы писали:


T>Немного оффтопика.

T>Для dotMemory планируется добавление возможности загрузки дампов памяти?

Планируется: https://youtrack.jetbrains.com/issue/DMRY-8.

Постоянно рученек не хватает((

Кстати, раз уж оффтоп пошел, а какой для Вас основной юзкейс для дампов?
Или перефразируя — в каких условиях Вы не можете обойтись dm снапшотами, которые, а это уже очевидно на данном этапе исследований, будут содержать гораздо больше информации для анализа?
Re[3]: Юнит тестирование "памяти"
От: Tom Россия http://www.RSDN.ru
Дата: 20.04.15 15:16
Оценка:
FR>А почему, собственно, на Ваш взгляд узкая?
Потому что лики на клиентской стороне мало кому нужно профилировать а на порядочном state less сервере ликом неоткуда взяться

FR>Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?

Профилирование разное бывает.
Народная мудрось
всем все никому ничего(с).
Re[7]: Юнит тестирование "памяти"
От: Tom Россия http://www.RSDN.ru
Дата: 20.04.15 15:20
Оценка:
FR>Постоянно рученек не хватает((
FR>Кстати, раз уж оффтоп пошел, а какой для Вас основной юзкейс для дампов?
FR>Или перефразируя — в каких условиях Вы не можете обойтись dm снапшотами, которые, а это уже очевидно на данном этапе исследований, будут содержать гораздо больше информации для анализа?

ИМХО отсутствие дампов — болшая проблема.
Мой кейз — смотрим состояние продакшин сервера, видим процесс подозрительно много памяти ест.
Делаем дамп и препарируем.
Народная мудрось
всем все никому ничего(с).
Re[8]: Юнит тестирование "памяти"
От: fedor.reznik  
Дата: 20.04.15 15:48
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>ИМХО отсутствие дампов — болшая проблема.

Я согласен, что проблема. Вопрос в ее масштабах.

Tom>Мой кейз — смотрим состояние продакшин сервера, видим процесс подозрительно много памяти ест.

Tom>Делаем дамп и препарируем.

Варианты на текущий момент:
1. Берем дотМемори — аттачимся, снимаем сколько угодно снапшотов и препарируем.
2. Нельзя поставить на продакшен дотМемори из инсталлера? Берем RemoteAgent, копируем на продакшен, запускаем (это экзешничек с wcf сервисом), аттачимся удаленно, см. п.1

Такие варианты совсем не спасают?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.