Мы написали фреймворк для юнит тестирования памяти .Net приложений. Хотелось бы обсудить насколько это может быть интересно и вообще получить фидбэк по функциональности.
Фреймворк распространяется в виде нугет пакета (https://www.nuget.org/packages/JetBrains.DotMemoryUnit/) и совершенно бесплатен. Все что необходимо иметь это юниттест раннер от джетбрейнса, то есть иметь установленный ReSharper 9.1 или dotCover 3.1.
Отдельный "запускатель" таких тестов нами запланирован на будущее.
Что умеет фреймворк:
— выбирать объекты по типам, интерфейсам, неймспейсам, поколениям
— сравнивать срезы памяти — выбирать новые объекты, умершие, выжившие
— добывать информацию о мемори траффике, в том числе поддерживаются ассерты на траффик с помощью атрибутов.
— у любой выборки объектов можно посмотреть количество памяти и объектов
— фреймворк интегрируется с NUnit и MSTest (вообще говоря с любым юнит тест фреймворком)
P.S.: Почему здесь, а не в jetbrains форуме? Потому что он бесплатный — это раз. И потому что хочется узнать насколько юнит тестирование памяти интересно — это два, а здесь аудитория, наверное, побольше.
Как раз на прошлой неделе побывал эту штуку. В принципе вещь прикольная и даже местами полезная. Но непонятно, вот вылез у меня в тесте мемлик. OK, как теперь этот тест запустить под дот мемери, чтоб уже детально разобраться, в чем причина?
J>В принципе вещь прикольная и даже местами полезная. Но непонятно, вот вылез у меня в тесте мемлик. OK, как теперь этот тест запустить под дот мемери, чтоб уже детально разобраться, в чем причина?
Здравствуйте, fedor.reznik, Вы писали:
FR>Всем доброго дня! FR>Мы написали фреймворк для юнит тестирования памяти .Net приложений. Хотелось бы обсудить насколько это может быть интересно и вообще получить фидбэк по функциональности.
Дурацкий вопрос можно? Ловятся только утечки .Net или unmanaged тоже может?
Здравствуйте, baranovda, Вы писали:
B>Дурацкий вопрос можно? Ловятся только утечки .Net или unmanaged тоже может?
Unmanaged не может( Но имею встречный вопрос: по каким критериям хотелось бы кверить куски unmanged память из юнит тестов написанных на .Net? А то она ведь совсем "разная" и просто мерить ее объем, имхо, большого смысла не имеет.
Здравствуйте, fedor.reznik, Вы писали:
FR>Unmanaged не может( Но имею встречный вопрос: по каким критериям хотелось бы кверить куски unmanged память из юнит тестов написанных на .Net? А то она ведь совсем "разная" и просто мерить ее объем, имхо, большого смысла не имеет.
Если речь про утечки, порождённые managed-кодом, то я бы смотрел на наследников UnsafeHandle и на IntPtr.
Это и с текущим API в принципе можно сделать.
Да, такое пожелание: при дизайне API начинайте со сценариев использования
Серьёзно, примеры безумно многословны (отдельное фу-фу-фу за примеры картинками без ссылки на текст).
Большая часть прекрасно сводится к хелперам для типовых сценариев аля
Не, конечно можно (и нужно) добавить подходящие хелперы самостоятельно, но как минимум заготовки (чтобы подсказать направление движения) должны присутствовать из коробки.
И, если уж речь зашла про работу с памятью, есть ли поддержка тестов для декларирования побочных эффектов?
Например, есть код на partially immutable-типах, было бы очень здорово находить ошибки вида "объект изменяется, хотя не должен".
В ту же степь — проверки для pure-функций (речь об вот этих вот),
или проверки вида "состояние объекта x эквивалентно состоянию на момент вот этого снапшота" — полезно для отладки автоматов, визиторов, undo-redo и тд и тп.
В принципе часть решается ручной расстановкой отладочных ассертов, но кто будет следить, что все ассерты расставлены верно?
Здравствуйте, Sinix, Вы писали:
S>Да, такое пожелание: при дизайне API начинайте со сценариев использования
Так в общем-то мы с них и начинали. Это так сказать продукт не в последнюю очередь выросший из производственной необходимости)
S>Серьёзно, примеры безумно многословны (отдельное фу-фу-фу за примеры картинками без ссылки на текст).
За фу-фу-фу, посыпаю голову пеплом, но ничего сделать не могу такие вот движки у нас на сайте(
S>Большая часть прекрасно сводится к хелперам для типовых сценариев аля 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 мы умеем показывать значение филдов, соответственно, здесь это тоже реализуемо.
Здравствуйте, 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 отслеживать изменения полей. Намного сложнее и куда больше шансов ложных срабатываний
Дорогое конечно удовольствие, но другого способа тестировать самые адские баги типа таких
Здравствуйте, Tom, Вы писали:
Tom>Интересная мысль, интересная затея. Tom>Единственное что область применения довольно узкая.
А почему, собственно, на Ваш взгляд узкая?
Я, понимаю, что профилировать перформанс и память — это операция, к сожалению, не частая. Ибо лень, и вообще пока петух не клюнет...
С этим бороться можно только просветительством и все равно будет лень
Но тут то! Есть какой-нибудь TabCollectionVM, есть в нем TabVM c командой Close — написал один раз тест на то, что если после вызова команды Close у, например, последнего табика TabVM`ов больше нет — и спи спокойно.
Или что при переоткрытии документа в программе старого больше нет (какой-нить IDocument в одном экземпляре).
Или алгоритм какой-нить — все у него хорошо, но только за время работы на определенных данных он генерит 17Gb мемори трафика, что приводит к необъяснимым тормозам — хотя по задумке все должно быть O(log N).
Это все примеры из жизни, из чего продукт и родился.
Казалось бы поймал сценарный лик -> написал тест -> радуешься. Все как с юнит тестами на баги.
Да может тут не совсем уж юнит тесты в вакууме, когда тестируется одна сущность, а скорее black/white box тестирование. Но может оно и к лучшему?
В конце концов, не у всех код идеально юнит тестируем, а у те у кого все же 100% TDD — всегда смогут собрать себе необходимый набор компонентов.
Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?
Здравствуйте, fedor.reznik, Вы писали:
FR>Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?
Не скажу за весь софт, но мне за 8 лет приходилось заниматься профилированием раза 2-3.
И каждый раз это было вызвано наличием какой-то проблемы.
Если нагрузочное тестирование, либо боевая эксплуатация не выявили проблем, то смысл что-то профилировать? Выглядит как преждевременная оптимизация.
Здравствуйте, Tesh, Вы писали:
T>Не скажу за весь софт, но мне за 8 лет приходилось заниматься профилированием раза 2-3. T>И каждый раз это было вызвано наличием какой-то проблемы.
T>Если нагрузочное тестирование, либо боевая эксплуатация не выявили проблем, то смысл что-то профилировать? Выглядит как преждевременная оптимизация.
Да, это правда. В типичных случаях, что-то заранее профилировать никто не будет.
С другой стороны, я участвовал как минимум в трех коммерчески успешных проектах, в которых, не было тестов. Совсем. От слова вообще.
Значит ли это, что тесты писать — это преждевременно себя напрягать? Наверное, нет.
Далее, если проблема возникла, то обычно ее фиксить дорого (потому что надо срочно) и, за частую, поздно.
Поэтому очевидные сценарии, имхо, имеет смысл профилировать перед каждым релизом — Вы же не выпускаете продукт без тестирования?
И dotMemoryUnit — это такое средство автоматизировать этот процесс, а вовсе не yet another profiler)
И именно в этом смысле, как мне кажется, он имеет гораздо более широкое применение.
Здравствуйте, fedor.reznik, Вы писали:
FR>С другой стороны, я участвовал как минимум в трех коммерчески успешных проектах, в которых, не было тестов. Совсем. От слова вообще. FR>Значит ли это, что тесты писать — это преждевременно себя напрягать? Наверное, нет.
Если там не было тестов, значит их наличие не было экономически обосновано. Тесты не бесплатны и требуют время как на написание, так и на поддержку.
FR>Далее, если проблема возникла, то обычно ее фиксить дорого (потому что надо срочно) и, за частую, поздно. FR>Поэтому очевидные сценарии, имхо, имеет смысл профилировать перед каждым релизом — Вы же не выпускаете продукт без тестирования?
Для этого существуют приемочное и нагрузочное тестирования.
FR>И dotMemoryUnit — это такое средство автоматизировать этот процесс, а вовсе не yet another profiler) FR>И именно в этом смысле, как мне кажется, он имеет гораздо более широкое применение.
Обычные тесты помогают убедиться в том, что код работает корректно с точки зрения бизнес-логики или архитектуры. Это по идее чаще востребовано, чем покрытие кода тестами для ответа на вопрос "а не течет ли чего" или "эффективно ли выделяется память". Такие тесты тоже могут пригодиться, но не думаю что они будут столь же широко распространены как тесты логики.
Здравствуйте, Tesh, Вы писали:
T>Здравствуйте, fedor.reznik, Вы писали:
T>Немного оффтопика. T>Для dotMemory планируется добавление возможности загрузки дампов памяти?
Кстати, раз уж оффтоп пошел, а какой для Вас основной юзкейс для дампов?
Или перефразируя — в каких условиях Вы не можете обойтись dm снапшотами, которые, а это уже очевидно на данном этапе исследований, будут содержать гораздо больше информации для анализа?
FR>А почему, собственно, на Ваш взгляд узкая?
Потому что лики на клиентской стороне мало кому нужно профилировать а на порядочном state less сервере ликом неоткуда взяться
FR>Или я не прав? Или реально профилирование — это такой уж совсем крайний, пожарный случай в жизни большинства софта?
Профилирование разное бывает.
FR>Постоянно рученек не хватает(( FR>Кстати, раз уж оффтоп пошел, а какой для Вас основной юзкейс для дампов? FR>Или перефразируя — в каких условиях Вы не можете обойтись dm снапшотами, которые, а это уже очевидно на данном этапе исследований, будут содержать гораздо больше информации для анализа?
ИМХО отсутствие дампов — болшая проблема.
Мой кейз — смотрим состояние продакшин сервера, видим процесс подозрительно много памяти ест.
Делаем дамп и препарируем.
Здравствуйте, Tom, Вы писали:
Tom>ИМХО отсутствие дампов — болшая проблема.
Я согласен, что проблема. Вопрос в ее масштабах.
Tom>Мой кейз — смотрим состояние продакшин сервера, видим процесс подозрительно много памяти ест. Tom>Делаем дамп и препарируем.
Варианты на текущий момент:
1. Берем дотМемори — аттачимся, снимаем сколько угодно снапшотов и препарируем.
2. Нельзя поставить на продакшен дотМемори из инсталлера? Берем RemoteAgent, копируем на продакшен, запускаем (это экзешничек с wcf сервисом), аттачимся удаленно, см. п.1