Наша команда разрабатывает ядро кросс-платформенного приложения, которое должно быть построено на Windows под управлением Visual Studio 2015, Linux с gcc 4.9+, MacOS, iOS, Android и Windows Phone 8.1+.
Для автоматической проверки кода на Jenkins сборки настраиваются под все необходимые конфигурации.
Задача сборок — отловить код, который не строится на одной или нескольких платформах или не проходит модульные тесты, и не допустить его попадания к командам конечных приложений до тех пор, пока не будут сделаны соответствующие исправления.
Этот CI-процесс позволяет разработчику локально использовать удобную для него операционную систему и среду разработки, будь то Visual Studio, XCode, QtCreator или вообще vim+ninja, не опасаясь, что его изменения не сработают или провалят тесты в другая среда.
В идеальном мире красная сборка Jenkins (это то, что мы используем в качестве сервера сборки) указывает на проблему в коде.
Увидев красную лампочку на мониторе, висящем в углу комнаты, «дежурный по сборке» должен пойти и исправить найденную проблему.
В действительности причины сбоя сборки могут быть самыми разными, например, потеря связи с узлом, на котором происходила компиляция, нехватка места на диске или прибытие инопланетян.
Такие ложные срабатывания отнимают у команды лишнее время, притупляют внимание и в целом снижают доверие к CI внутри команды.
Я хочу рассказать историю борьбы с одной из таких проблем.
Проблема была специфична для MSBuild и проявлялась примерно в таком сообщении в журнале:
Некоторое время проблема появлялась нечасто, раз в несколько дней, и заставляла меня лишь ругаться и еще раз перезапускать упавшую сборку.20:03:56 "D:\jenkins\workspace\task\ws\.
\SomeTarget.vcxproj" (default target) (429) -> 20:03:56 (_QtMetaObjectCompilerH target) -> 20:03:56 D:\jenkins\workspace\task\ws\.
\SomeQtBasedTarget.targets(52,5): error MSB4175: The task factory "XamlTaskFactory" could not be loaded from the assembly "Microsoft.Build.Tasks.Core".
Could not find file 'D:\jenkins\workspace\task\ws\TEMP\fv5nnzin.dll'.
[D:\jenkins\workspace\jenkins\workspace\task\ws\.
\SomeTarget.vcxpro]
Но после перехода от виртуальных машин к новым блестящим аппаратным узлам ситуация ухудшилась; случайные сбои могли происходить несколько раз в день.
Совершенно неприемлемой ситуацию делало длительное время сборки проекта (десятки минут, с которыми мы, кстати, боролись параллельно).
Иногда нужно было запустить срочный фикс через CI, но после длительного ожидания можно было поймать сбой, и тогда приходилось ждать снова.
Итак, что именно привело к ошибке? Для создания проектов мы используем цыпочка у которого есть 2 способа вызова внешней команды во время сборки - это действия И правила .
Действия реализуются через CustomBuild внутри файлов vcxproj.
Пример из документации: <ItemGroup>
<CustomBuild Include="faq.txt">
<Message>Copying readme.</Message>
<Command>copy %(Identity) $(OutDir)%(Identity)</Command>
<Outputs>$(OutDir)%(Identity)</Outputs>
</CustomBuild>
</ItemGroup>
И всё у них хорошо, они не взрываются.
Правила используют другой механизм.
Комментарий в коде гласит:
Правила MSBuild реализуются с использованием трех файлов: файла XML, файла .Как это работает? Для каждого такого правила MSbuild в %TEMP% генерирует исходный код на C# (файл .targets и файла .
props. Видеть blogs.msdn.com/b/vcblog/archive/2010/04/21/quick-help-on-vs2010-custom-build-rule.aspx .
cs), из которого пытается скомпилировать dll и сразу использовать ее, а если это не сработает, выдает исключение .
В комментарии говорится:
Это происходит, если произошел сбой при компиляции сборки.Действительно, в системном журнале за пару секунд до момента ошибки (согласно журналу сервера сборки) можно найти что-то вроде следующей записи об ошибке компилятора C#:Мы просто проходим, потому что разберемся с провалом ниже.
Faulting application name: csc.exe, version: 4.6.1055.0, time stamp: 0x563c1a09
Faulting module name: KERNELBASE.dll, version: 6.3.9600.18233, time stamp: 0x56bb4ebb
Exception code: 0xc0000142
Fault offset: 0x00000000000ecdd0
Faulting process id: 0x1af4
Faulting application start time: 0x01d1d13dbec0f5bd
Faulting application path: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe
Faulting module path: KERNELBASE.dll
Report Id: fc6cf36d-3d30-11e6-8260-0cc47ab21249
Faulting package full name:
Faulting package-relative application ID:
Поиск подобных ошибок в Google показал, что дело в размере.
Куча рабочего стола для неинтерактивных сеансов.
Действительно, все было похоже: код ошибки совпадал, а подчиненный агент Jenkins работал как служба Windows. Приняв эту гипотезу в разработку, я начал экспериментировать со значением раздела SharedSection в записи реестра HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\Windows. Попутно мне случайно удалось сделать сборку склонной к сбою почти со 100% вероятностью, что несколько облегчило итерации отладки.
Прочитав еще немного, я добрался до флажка «Разрешить взаимодействие с рабочим столом» в свойствах службы Jenkins, а затем до опции NoInteractiveServices в HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows. Но все эти попытки не принесли плодов.
Иногда сборки проходили, но закономерности уловить не удавалось.
Продолжая возиться с особенностями запуска процессов из сервиса Jenkins, я наткнулся следующий текст на StackOverflow. Автор рассказывает об особенностях поведения MSBuild по умолчанию при указании опции /M для параллельной сборки нескольких проектов.
Дело в том, что MSBuild создает необходимое количество копий самого себя — узлов, ожидающих выполнения задач.
В процессе сборки задачи распределяются по этим узлам и выполняются параллельно.
После завершения сборки узла Нет гаснут и продолжают ждать поступления новых задач.
Это произошло с нами на Дженкинсе; после завершения сборки процессы MSBuild продолжали зависать в памяти.
Я начал экспериментировать.
Воспроизведя крах сборки несколько раз подряд, я убил все висевшие в памяти процессы MSBuild, и, о чудо, следующая сборка прошла успешно! Затем я вооружился инструкциями из StackOverflow и добавил в наш скрипт сборки настройку переменной MSBUILDDISABLENODEREUSE и передачу параметра /nr:false вызову MSBuild. После этого все процессы MSBuild стали умирать в конце сборки, а не оставаться висеть в памяти.
Решение оказалось рабочим.
Прошло почти 2 недели, а проблема так и не повторилась.
И хотя я не до конца понял основные причины ошибки, мне удалось найти решение, которое сработало и, надеюсь, поможет кому-то еще.
Теги: #msbuild #Visual Studio #C++ #jenkins #C++ #компиляторы #разработка для Windows
-
Санкт-Люсия
19 Oct, 24 -
Трепетать. Renderobject — Измеряй И Властвуй
19 Oct, 24 -
Микроменеджмент: Время Создавать Зомби
19 Oct, 24