Все сломано: Внедрение rust-minidump в Mozilla – часть 1

В течение последнего года я возглавлял разработку rust-minidump, чистой Rust-замены для половины обработки minidump в google-breakpad.

На самом деле, в некотором смысле я закончил эту работу, потому что Mozilla уже развернула его в качестве бэкенда обработки аварий для Firefox 6 месяцев назад, он работает в два раза быстрее и кажется более надежным. (И вы знаете, это не ужасающий клубок C++, который анализирует и оценивает произвольный ввод из интернета. Мы сделали все возможное, чтобы изолировать Breakpad, но все равно… ужас.)

Это довольно фантастический результат, но всегда есть над чем работать, потому что Minidumps – это черная бездна, которая становится все глубже, чем дальше вы погружаетесь… подождите, нет, я забегаю вперед. Сначала свет, потом бездна. Да. Сначала свет.

Что я могу сказать, так это то, что у нас есть очень надежная реализация основной функциональности разбора и анализа minidump для самых больших платформ (x86, x64, ARM, ARM64; Windows, MacOS, Linux, Android). Но если вы хотите прочитать минидампы, сгенерированные на PlayStation 3, или обработать дамп Full Memory, вы не сможете воспользоваться этой функцией.

Мы приложили много усилий для документирования и тестирования этой штуки, поэтому я в ней уверен!

К сожалению! Уверенность! Стоит! Стоит! Ничего!

Вот почему это история о том, как мы сделали все возможное, чтобы сделать этот кошмар настолько надежным, насколько могли, и все равно получили 360 ударов из космоса от внезапных и невероятных усилий @5225225.

Эта статья состоит из двух частей:

  1. что такое минидампы и как мы сделали rust-minidump
  2. как мы стали абсолютными хозяевами простого фаззинга

Вы читаете часть 1, в которой мы наращиваем наше высокомерие.

Предыстория: Что такое минидамп, и зачем писать rust-minidump?

Ваша программа аварийно завершается. Вы хотите узнать, почему программа упала, но это произошло на машине пользователя на другом конце света. Полный coredump (вся память, выделенная программой) огромен – мы не можем допустить, чтобы пользователи присылали нам файлы размером 4 ГБ! Хорошо, давайте просто соберем наиболее важные области памяти, такие как стеки и места падения программы. О, и я думаю, если мы тратим время, давайте запихнем туда метаданные о системе и процессе.

Поздравляем, вы изобрели минидампы. Теперь вы можете превратить 100-потоковый coredump, который в противном случае занял бы 4 ГБ, в небольшой файл размером 2 МБ, который можно отправить по Интернету и провести посмертный анализ.

Точнее, это сделала компания Microsoft. Так давно, что в их документации даже не обсуждается поддержка платформы. Поддерживаемые версии MiniDumpWriteDump – это просто “Windows”. Предположительно, Microsoft Research разработала машину времени, чтобы гарантировать это.

Затем появился Google (примерно в 2006-2007 годах) и сказал: “Было бы здорово, если бы мы могли делать минидампы на любой платформе”. К счастью, Microsoft создала формат довольно расширяемым, поэтому было несложно расширить его для Linux, MacOS, BSD, Solaris и так далее. Эти расширения стали google-breakpad (или просто Breakpad), который включал в себя тонну различных инструментов для генерации, разбора и анализа расширенного формата minidump (и родного формата Microsoft).

Mozilla очень помогла с этим, потому что, очевидно, наша инфраструктура отчетов о сбоях (“Talkback”) была жалкой примерно до 2007 года, и это казалось хорошим улучшением. Излишне говорить, что на данный момент мы очень заинтересованы в минидампах breakpad.

Переходим к сегодняшнему дню, и в результате уморительного поворота судьбы такие продукты, как VSCode, означают, что Microsoft теперь поддерживает приложения, работающие на Linux и MacOS, поэтому она использует breakpad в производстве и должна обрабатывать не-Microsoft minidumps где-то в своей инфраструктуре отчетов о сбоях, так что чужое расширение их собственного формата теперь почему-то является их проблемой!

Тем временем, Google вроде как перешел на Crashpad. Я говорю “вроде”, потому что в нем все еще много от Breakpad, но они больше заинтересованы в создании инструментария поверх него, чем в улучшении самого Breakpad. Внеся несколько изменений в Breakpad: честно говоря, я тоже не хочу работать над ним. Тем не менее, для нас это было небольшой проблемой, потому что это означало, что проект становился все более неукомплектованным.

К тому времени, когда я начал работать над созданием отчетов о сбоях, Mozilla практически отказалась от рассылки исправлений/улучшений для Breakpad и просто использовала свой собственный исправленный форк. Но даже в отсутствие необходимости выпускать исправления, каждое изменение в Breakpad наводило на нас ужас: многие предложенные улучшения в нашей инфраструктуре аварийных отчетов замирали на отметке “время реализовать это в Breakpad”.

Почему работа над Breakpad так невыносима, спросите вы?

Разбор и анализ минидампов – это, по сути, упражнение в написании фрактального анализатора форматов, вложенных в форматы, вложенные в форматы. Для многих операционных систем. Для многих аппаратных архитектур. И все входные данные, которые вы разбираете и анализируете, ужасны и глючны, так что вам приходится писать действительно разрешительный парсер и ползти вперед, как только можете.

Какой-то конкретный инструментарий MSVC, который был частью Windows XP, имел ошибку в формате отладочной информации? Жаль, но все равно символизируйте кадр стека!

Программа упала, потому что ужасно испортила свой собственный стек? Жаль, все равно создайте обратную трассировку!

Сам автор минидампа сошел с ума и записал кучу мусора в один поток? Жаль, но все равно сделайте любой вывод, какой сможете!

Эй, знаете, у кого есть большой опыт работы с действительно сложными разрешающими парсерами, написанными на C++? Mozilla! Это как основная функциональность веб-браузера.

Знаете ли вы секретное решение Mozilla по написанию действительно сложных разрешающих парсеров на C++?

Мы перестали это делать.

Мы разработали язык Rust и портировали на него наши самые неприятные парсеры.

Мы делали это много раз, и когда мы это делали, мы всегда говорили: “Вау, это намного надежнее и проще в обслуживании, и теперь это даже быстрее”. Rust – действительно хороший язык для написания парсеров. А вот C++ – нет.

Поэтому мы переписали его на Rust (или, как называют это дети, “окислили его”). Breakpad большой, поэтому мы не охватили все его возможности. Мы специально написали и развернули:

  • dump_syms, который обрабатывает артефакты родной сборки в символьные файлы.
  • rust-minidump, который представляет собой набор критериев для разбора и анализа минидампов. Или, более конкретно, мы развернули minidump-stackwalk, который является высокоуровневым интерфейсом cli для всего rust-minidump.

Заметно, что в этой картине отсутствует запись минидампа, или то, что google-breakpad называет клиентом (потому что он запускается на машине клиента). Мы работаем над созданием писателя минидампов на основе rust, но пока мы не можем рекомендовать его использовать (хотя эта работа значительно ускорилась благодаря помощи Embark Studios).

Это, пожалуй, самая сложная и трудная работа, потому что у нее ужасная задача: использовать кучу родных системных API для сбора кучи специфической для ОС и аппаратной части информации о сбое и делать это для программы, которая только что упала, на машине, которая вызвала сбой программы.

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

Предыстория: Stackwalking и конвенции вызова

Одна из самых важных задач rust-minidump (minidump-stackwalk) – взять состояние потока (регистры общего назначения и стековую память) и создать обратную трассировку для этого потока (развернуть/пройтись по стеку). Это удивительно сложная и запутанная работа, которая усложняется тем, что мы пытаемся проанализировать память процесса, который запутался настолько, что потерпел крах.

Это означает, что наши стековые обходчики по своей природе работают с сомнительными данными, а все наши методы стекового обхода основаны на эвристике, которая может ошибиться, и мы очень легко можем оказаться в ситуации, когда стековый обход идет назад, вбок или бесконечно, и нам просто нужно попытаться справиться с этим!

Также довольно часто случается, что у стекохода начинаются галлюцинации, что я называю “стекоход нашел что-то, что выглядело достаточно правдоподобно, и отправился в странное приключение по стеку, создав целую кучу бесполезных мусорных кадров”. Галлюцинация наиболее распространена в нижней части стека, где она также наименее оскорбительна. Это происходит потому, что каждый пройденный кадр – это еще один шанс, что что-то пойдет не так, но и все более неинтересно, потому что вы редко заинтересованы в подтверждении того, что поток начался в той же функции, в которой начинаются все потоки.

Все эти проблемы исчезли бы, если бы все согласились должным образом сохранять СОВЕРШЕННО ХОРОШИЙ РЕГИСТР УКАЗАТЕЛЕЙ КАДРОВ. Шучу, включение указателей кадров тоже не работает, потому что Microsoft изобрела хаосные указатели кадров, которые не могут быть использованы для разворачивания! Я предполагаю, что это произошло потому, что они случайно наступили не на ту бабочку, когда путешествовали в прошлое, чтобы изобрести минидампы. (Я уверен, что 20 лет назад это решение было более разумным, но оно не выдержало конкуренции).

Если вы хотите узнать больше о различных техниках разворачивания, я писал о них здесь, в статье о компактной информации Apple о разворачивании. Здесь я также попытался задокументировать форматы информации о размотке STACK WIN и STACK CFI от breakpad, которые больше похожи на таблицы размотки DWARF и PE32 (которые, по сути, являются крошечными языками программирования).

Если вы хотите узнать больше о ABI в целом, я написал целую статью о них здесь. В конце этой статьи также есть введение в то, как работают соглашения о вызовах. Понимание соглашений о вызовах является ключом к реализации разматывателей.

Насколько сильно вы тестировали вещи?

Надеюсь, теперь вы немного поняли, почему анализ минидампов – это огромная головная боль. И, конечно, вы знаете, чем закончилась история: этот фаззер надрал нам задницы! Но, конечно, чтобы по-настоящему насладиться нашим поражением, вы должны увидеть, как сильно мы старались сделать хорошую работу! Настало время возвеличить наше высокомерие и похлопать себя по спине.

Так сколько же работы было проделано, чтобы сделать rust-minidump надежным до того, как над ним поработал fuzzer?

Довольно много!

Я никогда не буду утверждать, что вся проделанная нами работа была идеальной, но мы определенно проделали хорошую работу, как для синтетических входных данных, так и для реальных. Возможно, самым большим “недостатком” нашей методологии был тот факт, что мы были сосредоточены только на том, чтобы заставить работать Firefox. Firefox работает на множестве платформ и видит множество беспорядочных вещей, но это все еще довольно целостный продукт, который использует только столько возможностей минидампов.

Это одно из приятных преимуществ нашей недавней работы с Sentry, которая, по сути, является компанией, предоставляющей услуги по составлению отчетов о сбоях. У них гораздо больше возможностей для стресс-тестирования всевозможных странных уголков формата, которых нет у Firefox, и они определенно нашли (и исправили!) некоторые места, где что-то не так или отсутствует! (И недавно они также запустили его в производство! 🎉)

Но не верьте мне на слово, посмотрите, какие различные тесты мы провели:

Синтетические минидампы для модульных тестов

rust-minidump включает в себя генератор синтетических минидампов, который позволяет придумать высокоуровневое описание содержимого минидампа, а затем выдает фактический двоичный файл минидампа, который мы можем передать в полный парсер:

// Давайте сделаем синтонный минидамп с этой конкретной Crashpad Info…

let module = ModuleCrashpadInfo::new(42, Endian::Little)
.add_list_annotation("annotation")
.add_simple_annotation("simple", "module")
.add_annotation_object("string", AnnotationValue::String("value".to_owned()))
.add_annotation_object("invalid", AnnotationValue::Invalid)
.add_annotation_object("custom", AnnotationValue::Custom(0x8001, vec![42]));

let crashpad_info = CrashpadInfo::new(Endian::Little)
.add_module(module)
.add_simple_annotation("simple", "info");

let dump = SynthMinidump::with_endian(Endian::Little).add_crashpad_info(crashpad_info);

// convert the synth minidump to binary and read it like a normal minidump
let dump = read_synth_dump(dump).unwrap();
Вход в полноэкранный режим Выход из полноэкранного режима

// Теперь проверим, что минидамп сообщает ожидаемые значения…

minidump-synth намеренно избегает совместного использования кода компоновки с реальной реализацией, чтобы некорректные изменения компоновки “случайно” не прошли тесты.

Небольшое отступление для истории: этот фреймворк тестирования был начат первоначальным руководителем этого проекта, Тедом Мьельчареком. Он начал rust-minidump как побочный проект для изучения Rust, когда вышла версия 1.0, но у него просто не было времени закончить его. В то время он работал в Mozilla, а также был одним из основных авторов Breakpad, поэтому rust-minidump имеет много схожих дизайнерских решений и терминологии.

Этот случай не является исключением: наш minidump-synth является бесстыдной копией утилиты synth-minidump в коде Breakpad, которая изначально была написана другим нашим коллегой Джимом Бланди. Джим – один из единственных людей в мире, которые, как я признаю, пишут действительно хорошие тесты и документацию, так что я абсолютно счастлив откровенно копировать его работу здесь.

Поскольку все это было учебным экспериментом, Тед, по понятным причинам, был менее строг в тестировании, чем обычно. Это означало, что многое из minidump-synth было нереализовано, когда я появился, что также означало, что многие функции minidump были совершенно не протестированы. (Он создал совершенно замечательный скелет, просто у него не было времени, чтобы заполнить его полностью!)

Мы потратили много времени на заполнение большей части реализации minidump-synth, чтобы мы могли написать больше тестов и отловить больше проблем, но это определенно самая слабая часть наших тестов. Некоторые вещи были реализованы до того, как я пришел сюда, так что я даже не знаю, каких тестов не хватает!

Это хороший аргумент для некоторых проверок покрытия кода, но, вероятно, в ответ будет сказано “вау, вы должны написать гораздо больше тестов”, и мы все посмотрим на это и скажем “вау, мы, конечно, должны”, а потом, вероятно, мы никогда не дойдем до этого, потому что есть много вещей, которые мы должны сделать.

С другой стороны, Sentry был очень полезен в этом отношении, потому что у них уже есть зрелый набор тестов, полный странных угловых случаев, которые они создали со временем, поэтому они могут легко определить вещи, которые действительно важны, знают, каким должно быть исправление, и могут предоставить уже существующие тестовые случаи!

Интеграционные и моментальные тесты

Мы постарались сделать все возможное, чтобы решить проблемы с покрытием в наших модульных тестах, добавив больше целостных тестов. Есть несколько проверенных Real Minidumps, для которых у нас есть интеграционные тесты, чтобы убедиться, что мы правильно обрабатываем Real Inputs.

Мы даже написали кучу интеграционных тестов для приложения CLI, которое делает снимок своего вывода, чтобы убедиться, что мы никогда случайно не изменим результаты.

Отчасти это делается для того, чтобы убедиться, что мы не сломаем вывод JSON, для которого мы также написали очень подробный документ схемы и пытаемся сохранить стабильность, чтобы люди могли полагаться на него, пока детали реализации все еще в движении.

Да, minidump-stackwalk должен быть стабильным и разумным для использования в производстве!

Для наших snapshot-тестов мы используем insta, который я считаю фантастическим, и больше людей должны его использовать. Все, что вам нужно сделать, это assert_snapshot! любой вывод, который вы хотите отслеживать, и он волшебным образом позаботится о хранении, загрузке и дифферинцировании.

Вот один из тестов snapshot, в котором мы вызываем интерфейс CLI и делаем снимок stdout:

#[test]
fn test_evil_json() {
// For a while this didn't parse right
let bin = env!("CARGO_BIN_EXE_minidump-stackwalk");
let output = Command::new(bin)
.arg("--json")
.arg("--pretty")
.arg("--raw-json")
.arg("../testdata/evil.json")
.arg("../testdata/test.dmp")
.arg("../testdata/symbols/")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.unwrap();

let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();

assert!(output.status.success());
insta::assert_snapshot!("json-pretty-evil-symbols", stdout);
assert_eq!(stderr, "");
}

Войти в полноэкранный режим Выход из полноэкранного режима

Модульное тестирование Stackwalker

Stackwalker – легко самая сложная и тонкая часть новой реализации, потому что каждая платформа может иметь небольшие причуды, и вам нужно реализовать несколько различных стратегий разворачивания и тщательно настроить все, чтобы все работало хорошо на практике.

Самой страшной частью этого были разматыватели информации кадра вызова (CFI), потому что они, по сути, представляют собой маленькие виртуальные машины, которые нам нужно разобрать и выполнить во время выполнения. К счастью, breakpad уже давно сгладил эту проблему, определив упрощенный и унифицированный формат CFI, STACK CFI (ну, почти унифицированный, x86 Windows все еще была особым случаем, как STACK WIN). Так что даже если DWARF CFI имеет тонну сложных функций, нам в основном нужно реализовать калькулятор с обратной польской нотацией, за исключением того, что он может читать регистры и загружать память по адресам, которые он вычисляет (а для STACK WIN он имеет доступ к именованным переменным, которые он может объявлять и изменять).

К сожалению, описание этого формата в Breakpad довольно неконкретно, так что мне пришлось в основном выбирать семантику, которую я считал разумной, и идти с ней. Это сделало меня крайне параноидальным в отношении реализации. (И да, в этой части я буду больше говорить от первого лица, потому что именно на эту часть я потратил больше всего времени и многое сделал с нуля. Вся вина здесь лежит на мне!)

Парсер+оценщик STACK WIN / STACK CFI состоит из 1700 строк. 500 из них – это подробная документация и обсуждение формата, а 700 – это огромная куча из ~80 тестовых примеров, где я пытался придумать все возможные варианты.

Я даже проверил два теста, которые, как я знал, провалились, просто чтобы быть честным, что есть пара случаев, которые нужно исправить! Один из них – угловой случай с делением на отрицательное число, который почти наверняка просто не имеет значения. Другой – ошибка ввода, которую на самом деле производят старые x86 инструментальные цепочки Microsoft, и с которой приходится иметь дело парсерам. Последняя была исправлена до начала фаззинга.

А 5225225 все еще обнаружил целочисленное переполнение на этапе предварительной обработки STACK WIN! (На самом деле, это не так уж удивительно, это хакерский беспорядок, который пытается прикрыть то, насколько запутанными были таблицы разворачивания x86 Windows).

(Код здесь не слишком интересен, это просто тонна утверждений, что заданная входная строка приводит к заданному выходу/ошибке).

Конечно, я не был удовлетворен тем, что просто придумал свою собственную семантику и протестировал ее: Я также перенес большинство собственных тестов breakpad’a stackwalker на rust-minidump! Это определенно нашло кучу ошибок, которые у меня были, но также научило меня некоторым странным причудам в стаквокерах Breakpad, с которыми я не уверен, что согласен. Но в данном случае я летел настолько вслепую, что даже совместимость с Breakpad была своего рода облегчением.

Эти тесты также включали несколько тестов для путей без CFI, которые были такими же шаткими и причудливыми. Мне по-прежнему очень не нравятся многие странные правила сканирования стека, разработанные для конкретной платформы, но я вынужден работать в предположении, что они могут быть несущими нагрузку. (У меня определенно было несколько случаев, когда я отключал тест на брейкпад, потому что он был “очевидной бессмыслицей”, а затем налетал на него во время тестирования. Я быстро научился признавать, что глупости случаются и их нельзя игнорировать).

Одна важная вещь, которую я не стал повторять, – это некоторые из действительно сложных хаков для STACK WIN. Например, есть несколько мест, где они ввели дополнительное сканирование стека, чтобы попытаться справиться с тем фактом, что кадры стека могут иметь таинственное дополнительное выравнивание, о котором таблицы разворачивания окон просто не говорят вам? Наверное?

Почти наверняка есть какие-то экзотические ситуации, в которых rust-minidump работает хуже из-за этого, но это также, вероятно, означает, что мы работаем лучше в некоторых случайных других ситуациях. Мне так и не удалось добиться полного совпадения результатов, но в какой-то момент все расхождения были в достаточно странных ситуациях, и, насколько я понимаю, оба стаквокера давали одинаково плохие результаты в плохой ситуации. В отсутствие каких-либо причин предпочесть один из них другому, расхождение казалось приемлемым, чтобы сохранить чистоту реализации.

Вот упрощенная версия одного из перенесенных тестов breakpad, если вам интересно (к счастью, minidump-synth основан на том же фреймворке для мокинга двоичных данных, который используют эти тесты):

#[test]
fn test_x86_frame_pointer() {
let mut f = TestFixture::new();
let frame0_ebp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();

// Setup the stack and registers so frame pointers will work
stack.start().set_const(0x80000000);
stack = stack
.append_repeated(12, 0) // frame 0: space
.mark(&frame0_ebp) // frame 0 %ebp points here
.D32(&frame1_ebp) // frame 0: saved %ebp
.D32(0x40008679) // frame 0: return address
.append_repeated(8, 0) // frame 1: space
.mark(&frame1_ebp) // frame 1 %ebp points here
.D32(0) // frame 1: saved %ebp (stack end)
.D32(0); // frame 1: return address (stack end)
f.raw.eip = 0x4000c7a5;
f.raw.esp = stack.start().value().unwrap() as u32;
f.raw.ebp = frame0_ebp.value().unwrap() as u32;

// Check the stackwalker's output:
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000c7a5);
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::FramePointer);
assert_eq!(f1.instruction, 0x40008678);
}
}
Войти в полноэкранный режим Выход из полноэкранного режима

Выделенный инструмент для дифферинцирования, моделирования и отладки на производстве

Поскольку минидампы настолько ужасно фрактальны и угловаты, я потратил много времени, пугаясь тонких проблем, которые превратятся в огромные катастрофы, если мы попытаемся развернуть их на производстве. Поэтому я также потратил кучу времени на создание socc-pair, которая берет идентификатор сообщения о сбое из системы сообщений о сбоях Mozilla и извлекает минидамп, вывод старой реализации на основе брейкпада и дополнительные метаданные.

Затем он запускает локальную реализацию rust-minidump (minidump-stackwalk) на минидампе и выполняет доменно-специфический diff над двумя входными данными. Наиболее существенной частью этого является нечеткое различие на стековых дорожках, которое пытается лучше справиться с ситуациями, например, когда одна реализация добавляет дополнительный кадр, но в остальном обе сходятся. Он также использует методы, которые каждая реализация использовала, чтобы попытаться определить, чей вывод более достоверен, когда они полностью расходятся.

В итоге я также добавил в него кучу функций для насмешек и бенчмаркинга, поскольку я находил все больше и больше мест, где я просто хотел имитировать производственную среду.

Кроме того, я добавил очень подробный журнал трассировки для stackwalker, чтобы я мог легко отладить после смерти, почему он принял те или иные решения.

Этот инструмент обнаружил так много проблем и, что более важно, помог мне быстро определить их причины. Я очень рад, что сделал это. Благодаря ему мы знаем, что на самом деле исправили несколько проблем, которые возникали при старой реализации breakpad, и это здорово!

Вот урезанная версия отчета socc-pair (да, я злоупотреблял синтаксисом diff, чтобы получить подсветку ошибок). Это отличный хак, и я люблю его как ребенок):

comparing json...

: {
crash_info: {
address: 0x7fff1760aca0
crashing_thread: 8
type: EXCEPTION_BREAKPOINT
}
crashing_thread: {
frames: [
0: {
file: wrappers.cpp:1750da2d7f9db490b9d15b3ee696e89e6aa68cb7
frame: 0
function: RustMozCrash(char const*, int, char const*)
function_offset: 0x00000010
- did not match
+ line: 17
- line: 20
module: xul.dll

.....

unloaded_modules: [
0: {
base_addr: 0x7fff48290000
- local val was null instead of:
code_id: 68798D2F9000
end_addr: 0x7fff48299000
filename: KBDUS.DLL
}
1: {
base_addr: 0x7fff56020000
code_id: DFD6E84B14000
end_addr: 0x7fff56034000
filename: resourcepolicyclient.dll
}
]
~ ignoring field write_combine_size: "0"
}

- Total errors: 288, warnings: 39

benchmark results (ms):
2388, 1986, 2268, 1989, 2353, 
average runtime: 00m:02s:196ms (2196ms)
median runtime: 00m:02s:268ms (2268ms)
min runtime: 00m:01s:986ms (1986ms)
max runtime: 00m:02s:388ms (2388ms)

max memory (rss) results (bytes):
267755520, 261152768, 272441344, 276131840, 279134208, 
average max-memory: 258MB (271323136 bytes)
median max-memory: 259MB (272441344 bytes)
min max-memory: 249MB (261152768 bytes)
max max-memory: 266MB (279134208 bytes)

Output Files: 
* (download) Minidump: b4f58e9f-49be-4ba5-a203-8ef160211027.dmp
* (download) Socorro Processed Crash: b4f58e9f-49be-4ba5-a203-8ef160211027.json
* (download) Raw JSON: b4f58e9f-49be-4ba5-a203-8ef160211027.raw.json
* Local minidump-stackwalk Output: b4f58e9f-49be-4ba5-a203-8ef160211027.local.json
* Local minidump-stackwalk Logs: b4f58e9f-49be-4ba5-a203-8ef160211027.log.txt
Вход в полноэкранный режим Выход из полноэкранного режима

Постановка и развертывание в производство

Когда мы были достаточно уверены в реализации, большую часть оставшегося тестирования взял на себя Уилл Кан-Грин (Will Kahn-Greene), который отвечает за многие детали серверной части нашей инфраструктуры аварийных отчетов.

Уилл потратил много времени на настройку множества механизмов для управления развертыванием и мониторингом rust-minidump. Он также проделал большую работу по очистке всех наших серверных конфигурационных скриптов для обработки любых различий между двумя реализациями. (Хотя я потратил много времени на совместимость, мы оба согласились, что это была хорошая возможность очистить старые ошибки).

Как только все это было настроено, он включил его в staging, и мы впервые увидели, как rust-minidump работает в ~production:

Ужасно!

Наши staging-серверы принимают около 10% входных данных, которые также поступают на наши production-серверы, но даже при таком уменьшенном масштабе мы очень быстро обнаружили несколько новых угловых случаев и получали тонны сбоев, что, мягко говоря, неловко для вещи, которая обрабатывает чужие сбои.

Уилл проделал большую работу, отслеживая и сообщая о проблемах. К счастью, все они были довольно легко устранимы. В конце концов, все выровнялось, и казалось, что все работает так же надежно, как и старая реализация на рабочем сервере. Единственное место, где мы не получали никакого результата, – это ужасно усеченные минидампы, которые с таким же успехом могли быть пустыми файлами.

Изначально у нас были грандиозные планы запустить socc-pair на всех обрабатываемых серверах staging или что-то в этом роде, чтобы быть действительно уверенными в результатах. Но к тому времени, когда мы дошли до этого момента, мы были полностью измотаны и чувствовали себя довольно уверенно в новой реализации.

В конце концов Уилл просто сказал: “Давайте включим это в продакшн”, а я сказал: “ААААААААААААААААААА”.

Этот момент был чистым ужасом. Угловых случаев всегда было больше. Мы никак не могли просто закончить. Это, вероятно, подожжет всю Mozilla и удалит Firefox из интернета!

Но Уилл убедил меня. Мы написали несколько документов с подробным описанием всех тонких различий и разослали их всем, кому могли. И вот наконец настал момент истины: Уилл включил его в производство, и я смог увидеть, насколько хорошо он работает в производстве:

*драматическая барабанная дробь*.

Все работало отлично.

После всего этого стресса и беспокойства мы включили его, и все было в порядке.

Черт возьми, я скажу это: он работал хорошо.

Он был быстрее, меньше падал, и мы даже знали, что он исправил некоторые проблемы.

Остаток той недели я пребывал в некотором ступоре, потому что все время ждал, что вот-вот упадет другой ботинок. Я ждал, что кто-то появится из тумана и объяснит, что я каким-то образом сломал Thunderbird или что-то в этом роде. Но нет, он просто работал.

Мы уехали на каникулы, и я все ждал, что он сломается, но все было в порядке.

Честно говоря, я до сих пор в шоке от этого!

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

А фаззер все еще надирал нам задницы после этого.

Но это уже часть 2! Спасибо за чтение!

The post Все сломалось: Установка rust-minidump в Mozilla – часть 1 appeared first on Mozilla Hacks – блог веб-разработчика.

Оцените статью
Procodings.ru
Добавить комментарий