Сложное (и только ли для программистов?) продолжение истории с firmware Toyota, и пара слов о полезном "в быту"

11 ноябрь, 2013 - 16:58Андрей Зубинский

Нашумевшая история firmware Toyota вызвала бурные обсуждения на профильных интернет-ресурсах. Не буду даже приводить ссылок, чтобы не замутнять и без того не совсем простой текст. К тому же к обсуждениям подключилась «интернет-аудитория», сами понимаете, какая получается свалка в итоге.

Из всей истории с точки зрения человека, в той или иной мере связанного с программированием как профессией, можно выявить разве что одно вопиющее на первый взгляд противоречие. А именно, каким образом у одного из самых мощных и популярных автомобильных производителей вообще могло произойти такое?

Сразу отбросим совершенно дурацкие домыслы (вроде «нанятые за гроши непрофессионалы писали») и всевозможные ссылки на кризис и спешку. Должно быть что-то другое, какая-то совершенно другая причина, из которой можно чему-то научиться, если её понять. В конце концов, любая инженерия в своей основе подразумевает очень высокую ценность не столько реверс-процедур (хоть реверс-инжиниринг и сомнительно законный, все взрослые серьёзные люди понимают, что все взрослые серьёзные люди им занимаются), сколько ответа но вопрос «почему кто-то сделал именно так, а не иначе». В случае с firmware Toyota ситуация очень сложная, потому что «реверсить» нечего – мы располагаем минимумом косвенной информации, и барьер коммерческой тайны, окружающий исходные тексты злополучной «прошивки» наверняка никогда никем не будет преодолён – слишком серьёзный рыночный игрок строил этот барьер. Но кое-что у нас всё же есть.

Во-первых, нам известен вычислитель, из-за программ для которого всё и случилось. Соберём в кучку всё известное. Итак, Renesas V850. 32-битовое процессорное ядро этого вычислителя – уникальная разработка NEC, не имеющая аналогов, в 2008 году V850 были самыми продаваемыми 32-битовыми встраиваемыми вычислителями в мире. Уникальность процессорного ядра – важный нюанс. Он означает, что производитель реальных микросхем такой архитектуры на рынке – всего один (был NEC, теперь Renesas). А факт наличия всего одного производителя не может не сказаться на инструментальной поддержке для программистов. Даже несмотря на популярность. Мир встраиваемых систем сильно отличается от мира «потребительского железа», здесь свои правила и риски. И, если внимательно посмотреть на ситуацию с инструментальным ПО, для V850 фактически выпускают всего две системы разработки промышленного качества знаменитая компания IAR и не менее знаменитая Green Hills. Есть ещё набор инструментов GNU, но мы о нём даже не будем говорить, автомобильная промышленность предусматривает свои сертификации, требования и т.д. У обоих производителей соответствуют требованиям автомобильной промышленности (MISRA) только C-компиляторы. Стало быть, сразу вычеркнем C++ (вообще, это отдельный предмет разговора, C++ и встраиваемые системы), и, несмотря на собственные внутренние стандарты Toyota, будем смело утверждать, что злополучное firmware написано на подмножестве C с «ограничителями», составленными из требований MISRA и собственных внутренних требований Toyota. Это ещё не всё. Из открытой информации о «деле Toyota» мы знаем, что firmware разработано для некоторой неизвестной операционной системы реального времени (RTOS). А из простой логики подчинения всего рациональным соображениям мы можем прийти к следующему заключению – маловероятно, что в Toyota для собственных нужд занимались разработкой собственной RTOS и её сертификацией (там объёмная пачка сертификатов). Крайне маловероятно. Производителю такого масштаба и такой интенсивности всех производственных процессов просто чудовищно накладно заниматься системным программированием, да ещё и для всяких (какие есть и скоро будут на рынке) архитектур. Это соображение косвенно подтверждается фактом наличия в модуле ECU второго контроллера с «прошивкой» вообще от стороннего производителя. Также крайне маловероятно, что в Toyota выбрали путь «повышенного сопротивления» - использование RTOS от одного производителя, а инструментального набора – от другого. Любой вменяемый руководитель проекта или программист прекрасно знает, чем оборачивается такое удовольствие. Да и при масштабах выпуска автомобилей (и, соответственно, потребления лицензированного системного ПО для моря контроллеров в них, ECU ведь далеко не единственный насыщенный вычислительным модуль в современном автомобиле, их там несколько десятков) начинает играть ключевую роль фактор объёмов, из которых следуют очень специальные скидки и прочие «пряники». В том числе и поэтому любой масштабный производитель стремится уменьшить число поставщиков (а ведь ещё есть логистика…). Складывая всё воедино, с высокой степенью вероятности можно прийти к выводу, что в ECU использовано ПО Green Hills (IAR де-факто не поставляет RTOS, только пакеты совместимости с системами сторонних производителей), потому что из двух упомянутых компаний именно Green Hills предлагает всё нужное от одного поставщика, ещё и жестоко сертифицированное (заявлено подтверждённое соответствие ISO 26262 (ASIL D), IEC 61508 (SIL 4) и EN 50128 (SWSIL 4), и т.д.).

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

А вывод из этой части рассуждений предельно прост – системное ПО и инструментальный набор просто обязан быть самого высокого качества, со всеми соответствиями всем отраслевым, мировым и национальным стандартам. Иначе эксплуатацию (читай - продажи) автомобилей просто запретят.

Хорошо, что-то мы уже реконструировали с очень высокой степень доверительности. И с самого начала отбросили часть откровенной чуши. Но чуши так много, что надо бы ещё кой-чего «подчистить». Мы даже не можем догадываться как организованы процессы разработки встраиваемого ПО в Toyota, но мы чётко можем сказать чем они не являются. Никаких agile процессов, конечно же, в помине там нет. Даже не считаясь с критичностью приложений, просто потому, что системные и функциональные требования находятся полностью в ведомстве производителя. Ну и потому, что стиль «доделаем и переделаем в ходе эксплуатации» совершенно невозможен. Скорее всего, речь идёт о классическом строгом и жёстком проектном процессе с детальным проектированием, обязательным моделированием, суровым многоэтапным тестированием и документированием, и всем прочим, почему-то объявленным сейчас очень нехорошим и не соответствующим требованиям реальности (не «почему-то», конечно, а из-за размеров хора, «поющего» со сцены web-программирования, они так громко и нестройно орут, что с других сцен и из оркестровых ям никого уже и не слышно). Это как бы и не надо было расписывать, но, оказывается, если судить по интернет-обсуждениям, в которых участвуют программисты всяких мастей и специальностей, очевидное для слишком многих оказывается невероятным. И, само собой, занимаются всем этим далеко не «студенты», а узко специализированные профессионалы, прекрасно знающие своё дело. В конце-то концов, машины с таким ECU с такой странной «прошивкой» в основной своей массе ездили и ездят, несмотря на совершенно несуразную цифру - 11 тысяч глобальных переменных в программе жёсткого реального времени.

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

И вот, в итоге, у меня при написании ещё прошлой тематической записи в блоге получилась загадочная картина. Я не спешил всем этим делиться, ожидая предсказуемой массовой реакции.

Загадочность очевидна – как могло так случиться, что при всём вышесказанном возникли такие вопиющие несуразности, которые косвенно следуют из всего одной цифры. 11 тысяч. Любой программист, даже не имеющий понятия о realtime-системах, прекрасно понимает, что каждая глобальная переменная – источник потенциальных проблем, потому что её состояние доступно из всего кода, она в поле видимости всего, и даже тщательное косвенное обеспечение атомарности операций с такой переменной не гарантирует невозможности возникновения каких-то нюансов при непрерывном и длительном исполнении программы. Как и тестирование в таком случае ничего не гарантирует, только иллюстрирует работоспособность в пределах тестов.
В общем, вся эта история не давала мне покоя, и я пытался много думать :)  и копошился в интернете, выискивая что-нибудь, проясняющее картину. При этом мне хорошо понятно, что часть кода ECU была сгенерирована из высокоуровневых моделей, а не «написана руками», и что это может объяснять изобилие глобальных переменных – генераторы кода вроде как гарантируют всё, что нужно гарантировать в таком случае. Но мне также понятно, что в реальной разработке «сгенерировать всё» из высокоуровневой модели практически невозможно (просто потому, что степень детализации высокоуровневой модели в таком случае будет равна степени детализации конечной программы, и даже существенно выше, с учётом требований реальности), и что чаще всего высокоуровневые модели применяются для разработки очень уж хитрых и вычурных контуров управления, цифровых фильтров и прочего, у чего «непрограммистские» показатели (передаточные функции, например) куда важнее сугубо «программистской» составляющей. В общем, сложилась почти патовая ситуация – феномен, которого не может быть, но он есть.

В итоге поисков мне «подвернулись» несколько весьма специфических статей (причём родом из автомобильной и авиационной промышленности, что характерно), посвященных...  специфике тестирования встраиваемого ПО.

Это очень суровая тема. Специфику которой надо пояснять, например, даже практикующим программистам из не-embedded областей.

Дело в том, что у встраиваемых вычислителей нет никаких консолей, все каналы ввода-вывода их могут быть полностью задействованы программно (и чаще всего задействованы), все физические выводы, «ноги» (порты ввода-вывода) также задействованы практически полностью (потому что это «железо», и оно тиражируется в огромных количествах, и каждый физический вывод микросхемы пересчитывается в очень ощутимые деньги, так что роскошь «свободных выводов» в этом мире – действительно роскошь). Кроме этого, во встраиваемых системах настолько взаимосвязаны физический и программный миры, что всякие эмуляторы и симуляторы – это, конечно, помогает немного, но без отладки и тестирования реальной системы, использующей реальные компоненты (больше того – даже из конкретной партии), ни о каком действительно качественном тестировании речь идти не может. Даже при формальной верификации кода.

В общем, тестирование встраиваемых систем, ещё и критических в смысле надёжности всего в целом, в куда они встроены, – ещё тот кошмар.

И, оказывается, в нём применяются те же методы, что и в прочих тестированиях. Например, юнит-тесты (не буду пояснять терминов, значения которых можно посмотреть).

И вот тут начинается очень и очень интересное. Дальше будет несколько специфическое для непрограммистов, я постараюсь объяснить «на пальцах», чтобы было внятно всем. Мало ли. Вдруг оно кому пригодится.

Основной язык программирования встраиваемых систем – C. Великий и ужасный. Даже и не совсем язык высокого уровня, и не совсем ассемблер, сейчас уже трудно сказать что он такое, он – C. В этом языке де-юре нет явных средств для высокоуровневой организации кода – модулей, например. Де-факто модули в нём имитируются использованием отдельных файлов исходных текстов, в которых полноценная инкапсуляция имитируется особенностями семантики модификатора static. Переменные и функции, находящиеся в одном файле исходного текста и отмеченные этим модификатором, принципиально не доступны и не видны прочему коду, находящемуся в других файлах исходных текстов. К тому же у этого модификатора есть несколько приятных побочных действий, очень хорошо подходящих реальности встраиваемых систем, в которых ресурсы оперативной памяти крайне ограничены, функциональность также ограничена, и фокусов с динамическим выделением памяти или с повторным использованием ячеек памяти вообще во большинстве случаев лучше избегать – static-переменные остаются «на своих местах и со своим содержанием» независимо от текущей области видимости в потоке исполнения (что значит – их не надо сохранять в стеке, а стек – ресурс очень дорогой и потенциально опасный). В общем, static-переменные в embedded C-программировании – это совершенно необходимая штука, без которой нормально ничего не получается.

И вот, оказывается, при юнит-тестировании С-«модулей», в которых в static-переменные упрятаны локальные для модуля данные, в которые гарантированно не может «залезть» код других модулей, приходится выносить в… глобальные переменные. Это как бы один из нюансов реальности тестирования. И он подтверждается статьями профессионалов.

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

Кажется, это ещё один парадокс, которых и без того много в области тестирования критического ПО. Модное и трудное в требовании полного изменения ментальности TDD-проектирование (Test Driven Development) во встраиваемых системах усложняется требованием проведения тестов на «реальном железе», из которого совершенно непонятно как в общем случае «вынимать» результаты тестов. Юнит-тестирование приводит к деформации модульной структуры кода.

Очень интересная область, в которой, в силу потенциальных угроз, с ростом популярности Internet of Things, кажется, назревает серьёзный кризис. Который может быть разрешён, по моему скромному мнению, только введением стандартной аппаратной поддержки тестирования встраиваемого ПО в первую очередь в архитектуры микроконтроллеров. Существующий стандарт, в той или иной мере поддерживаемый в разных микроконтроллерах, JTAG, ориентирован больше на тестирование аппаратных средств. Речь же идёт о другом – о механизмах поддержки высокоуровневого тестирования встраиваемого ПО. Сейчас они ограничиваются поддержкой процедур отладки кода – точками останова и просмотром состояния регистров. Но это не совсем то, а иногда и совсем не то.

Если к этой аппаратной непригодности современных микроконтроллеров к высокоуровневому тестированию ПО добавить чудовищный дисбаланс между количеством тематических публикаций, посвященных тестированию ПО вообще и тестированию встраиваемого ПО (в реальности – всего несколько изданий), и всё это возвести в степень, которой недавно пугала Cisco сотоварищи – ожидаемого 15-трилионного рынка IoT, –   получится как-то не очень хорошо. На мой взгляд.

Стало быть, кто там у нас занимается вычислительными архитектурами (аспиранты, учёные)? Вот вам такое свято место, которое, как ни странно, оказалось практически пустым. Тем более, что в embedded мире никто не гонится за сверхмощными вычурно оптимизирующими поток исполнения команд архитектурами – здесь, напротив, реально нужна архитектурная простота (и потому что инструментальные средства надо разрабатывать, и потому что для задач реального времени нужна в том числе тотальная временная предсказуемость исполнения кода), малая площадь кристалла, низкое энергопотребление и пригодность архитектуры одновременно для решения задач реального времени и поддержки тестирования встраиваемого ПО (чего никто пока не сделал и даже не вносил в перечень требований). Очень даже замечательная область для приложения усилий, куда более благодатная, чем вечнозелёные попытки побить грандов большого процессоростроения на поле сверхвысокой производительности (я ещё не забыл смешную историю с «Эльбрусом», например).

Вот такая картина, извините, что много букв получилось.

Теперь о более приземлённом. Без лишнего. Отыскал замечательную программу, написанную нашими ребятами из Хмельницкого Национального Университета. JMCAD. Это Java-приложение, объединяющее в себе интерактивный конструктор высокоуровневой модели какой-либо системы (моделирование действительно очень высокого уровня, без «привязки» к конкретной прикладной области), скрытые механизмы построения вычисляемой модели, а также собственно её решения и визуализации результатов. Для быстрой проверки гипотез, как «системный калькулятор» в добавку к математическим пакетам, записной книжке и учебнику по чему-нибудь инженерному или даже бизнес-ориентированному (например, для временного интерактивного моделирования технологических процессов), и вообще как очень забавная игрушка (почему нет?, кто запрещает?) – совершенно замечательная программа.

Авторы – большие молодцы, спасибо им персональное.

Откланиваюсь.