Редактор отдела планирования | Натали
Автор | JOHANNES BADER et al.
Переводчик |
Редактор | Винсент
Руководство по искусственному интеллекту на переднем крае: Facebook разработал инструмент под названием Getafix, который может автоматически находить исправления ошибок и предоставлять их инженерам для утверждения, что значительно повышает эффективность работы инженеров и общее качество кода. Getafix не только использует мощный алгоритм кластеризации для анализа контекста проблемного кода, чтобы найти более подходящее решение, но также предлагает решение, которое легко понять инженерам-людям. Getafix — это первый автоматизированный инструмент восстановления, который масштабно внедряется в производственную среду Facebook, что еще больше повышает стабильность и производительность приложений Facebook, которыми пользуются миллиарды пользователей.
Для получения дополнительных галантерейных товаров, пожалуйста, обратите внимание на публичный аккаунт WeChat «AI Frontline» (ID: ai-front)
Современные производственные кодовые базы сложны и постоянно обновляются. Чтобы создать систему, которая могла бы автоматически находить исправления ошибок — без помощи инженеров — мы создали инструмент, который мог бы научиться исправлять ошибки на основе предыдущих изменений кода, внесенных инженерами. Он находит некоторые скрытые шаблоны и использует эти шаблоны для определения средств, которые, скорее всего, исправят новые ошибки.
Инструмент под названием Getafix был запущен в производство в Facebook для дальнейшего повышения стабильности приложений, используемых миллиардами людей. Getafix обычно используется в сочетании с двумя другими инструментами Facebook, но эту технологию можно использовать и в других местах. В настоящее время он может предлагать исправления ошибок, обнаруженных Infer, нашим инструментом статического анализа, который выявляет такие проблемы, как исключения нулевого указателя в коде Android и Java. Он также предоставляет исправления через SapFix для ошибок, обнаруженных нашей интеллектуальной автоматизированной системой тестирования Sapienz. Теперь мы подробно рассмотрим, как Getafix учится исправлять ошибки (произвольные проблемы с кодом, а не только те, которые вызывают сбой приложения).
Цель Getafix — позволить компьютерам выполнять повседневные задачи, но под наблюдением человека, поскольку вопрос о том, требует ли ошибка сложного исправления, по-прежнему должен решать человек. Этот инструмент применяет новый метод иерархической кластеризации к тысячам предыдущих изменений кода, одновременно исследуя сами изменения кода и их контекст. Он может обнаруживать основной шаблон ошибок и предоставлять исправления, которые ранее не обнаруживались автоматическими инструментами исправления.
Getafix также может значительно сократить удельный объем памяти, который может потребоваться изменить в программе в процессе исправления ошибок, чтобы быстрее выбрать подходящий метод исправления; кроме того, он больше не накладывает экстремальных вычислительных ограничений по времени на предыдущий брутфорс и логические технологии высокие требования. Этот более эффективный подход позволил Getafix успешно развернуться в производстве. В то же время, поскольку Getafix может учиться на прошлых изменениях кода, достаточно генерировать исправления, которые легче понять инженерам-людям.
Getafix в настоящее время развернут в рабочей среде Facebook и отвечает за автоматическое исправление ошибок нулевого разыменования, о которых сообщает Infer, а также предлагает исправления для сбоев, связанных с нулевым разыменованием, помеченных Sapienz. Кроме того, Getafix использовался для решения проблем с качеством кода, обнаруженных при повторном просмотре существующего кода в более новых версиях Infer.
Чем Getafix отличается от традиционных простых автоматических инструментов для ремонта
В текущей отраслевой практике функция автоматического исправления в основном используется для различных базовых проблем, в то время как восстановление кода проще. Например, анализатор может выдать предупреждение о «фатальном исключении», подчеркнув, что разработчик мог забыть добавить бросок перед новым Exception(…) . Инструменты автоисправления могут выполнять настройки напрямую, а конкретные настройки могут быть определены правилами lint — другими словами, им не нужно знать конкретную ситуацию работающего приложения.
Getafix совсем другой, предоставляющий более общую функциональность и включающий контекстно-зависимые факторы для решения проблем. В следующем примере кода Getafix завершает следующие исправления ошибки Infer в строке 22:
Обратите внимание, что это исправление зависит не только от переменной ctx, но и от возвращаемого типа метода. В отличие от простых исправлений lint, такие исправления нельзя включить в сам Infer.
На рисунке ниже показаны исправления, предоставленные Getafix для ошибок Infer; хотя ошибки от Infer всегда одни и те же (вызовы нулевых методов, риск выдачи исключения NullPointerException), каждое конкретное исправление по-прежнему уникально. Следует также подчеркнуть, что метод ремонта Getafix точно такой же, как и обычная операция разработчиков-людей.
Глубокое понимание ключевых технических деталей Getafix
Организация Getafix показана в наборе инструментов на следующем рисунке. В этом разделе мы опишем три основных компонента Getafix, их соответствующие возможности и проблемы.
Tree Differencer идентифицирует изменения на уровне дерева
Дифференциатор на основе абстрактного синтаксического дерева в первую очередь отвечает за идентификацию фактических следов редактирования между двумя исходными файлами, например последовательных изменений одного и того же файла. Например, он обнаруживает изменения со следующей степенью детализации: обертывание операторов с помощью if, добавление аннотации @Nullable или импорта, ранний возврат условий в существующем методе и т. д. В следующем примере вставка условного оператора, если собака имеет значение null и возврат досрочно, переименование общедоступного в приватное и перемещение метода определяются как фактические изменения. В то время как линейные инструменты сравнения только помечают методы как полное удаление и вставку, Tree Differencer может обнаруживать это перемещение и обрабатывать вставки в перемещаемых методах как фактические изменения.
Основная проблема Tree Differencer заключается в том, как эффективно и точно выровнять «до» и «после» части уровня дерева, чтобы определить правильные фактические изменения и их сопоставления.
Новый метод добычи шаблона восстановления
Getafix выполняет анализ шаблонов, используя новые методы иерархической кластеризации, а также подход, направленный против единства, существующий метод, способный обобщать различные символьные выражения. После этого он строит набор потенциально релевантных различий дерева, выбирает наиболее распространенную процедуру в этом наборе и переводит ее в режим восстановления. Эти шаблоны могут быть абстрактными и содержать различные «дыры», на которые направлены преобразования программы.
На следующем примере изображения показан набор иерархий или дендрограмм, созданных на основе набора правок. (В этом примере мы переходим прямо к редактированию из предыдущего примера.) В каждой строке показан режим редактирования — фиолетовым цветом обозначено «до» и синим — «после» — и некоторые метаданные. Каждая вертикальная черная полоса соответствует определенному уровню в иерархии, где режим редактирования в верхней части черной полосы представляет собой режим, полученный путем инвертирования всех других правок на том же уровне в структуре. Остальные правки соединены более тонкими черными линиями. Anti-unity сочетает в себе условие «вернуться раньше, если собака пуста» из предыдущего примера с другим редактированием — единственная разница в последнем заключается в том, что «собака пьет». В результате будет сгенерирован абстрактный шаблон восстановления, представляющий общность. Символ h0, введенный антиединством, представляет собой «дыру», которая может быть конкретизирована в зависимости от контекста.
Далее, этот режим редактирования можно комбинировать с другими режимами редактирования, которые имеют более разнообразные имена переменных, но имеют ту же общую структуру. При прочесывании древовидного контекста весь процесс будет создавать все более и более абстрактные шаблоны редактирования. Например, он может объединить это редактирование с редактированием, связанным с кошками, в результате чего абстрактное редактирование будет расположено над графиком.
Что еще более важно, этот иерархический процесс сопоставления предоставляет Getafix мощную основу для обнаружения различных повторно используемых шаблонов в изменениях кода. Изображение ниже суммирует в общей сложности 2288 правок, чтобы исправить ошибки Infer, сообщающие об ошибках с нулевыми указателями в нашей кодовой базе, в набор древовидных карт (горизонтальное расположение, минимизированное). Шаблоны восстановления, которые мы надеемся обнаружить, несомненно, скрыты в этой древовидной карте.
Интеллектуальный анализ шаблонов, основанный на подходе, направленном против единства, не является чем-то новым, но для устранения новых ошибок с минимальным количеством исправлений нам необходимо дополнительно усилить результаты анализа шаблонов.
Одним из изменений является введение части окружающего кода, то есть той части результата редактирования, которая не изменилась. Таким образом, мы можем обнаружить не только шаблоны, которые люди воспринимают при изменении, но и определенные шаблоны, которые существуют в контексте, когда изменение применяется. Например, в первой дендрограмме выше мы заметили, что два разных редактирования добавляют if(dog==null)return перед dog.drink(…); . Хотя dog.drink(…); не изменился, его следует рассматривать как контекстуальную информацию для частей шаблона «до» и «после», чтобы помочь нам понять, где применяется это исправление. На более высоком уровне редактирования контекст dog.drink() объединяется с другими контекстами в абстрактный контекст h0.h1(), чтобы ограничить область применения шаблона. В следующем разделе мы представим другой, более реалистичный пример.
Алгоритмы жадной кластеризации, как правило, с меньшей вероятностью изучают вышеизложенное, согласно предыдущей литературе об инструментах автоматического рисования. Это связано с тем, что жадные алгоритмы кластеризации, как правило, поддерживают одно представление каждого кластера, поэтому, если контекст не присутствует во всех изменениях обучающих данных, алгоритм не будет вводить этот контекст. Например, если редактирование будет вставлено, если (list != null) return, когда do(list.get()); объединяется с dog.drink(), как указано в приведенном выше примере, тогда жадный алгоритм кластеризации отбросит все о Раннее возвращает контекст конкретной позиции курсора. Напротив, иерархический подход Getafix к кластеризации сохраняет как можно больше контекста на каждом уровне, тем самым обеспечивая уровень общности общей структуры. В какой-то степени, хотя часть общего контекста, который мы хотим изучить, может быть утеряна, он все же будет присутствовать на каком-то низком уровне структуры.
В дополнение к окружающему коду мы также связывали изменения с отчетами об ошибках Infer, что побуждало эти изменения понимать сопоставление между режимами редактирования и соответствующими отчетами об ошибках. На первой древовидной диаграмме выше вы можете видеть, что Infer рассматривает «errorVar» как переменную источника ошибки в отчете об ошибке и дает уязвимость h0 после анти-унификации. Взяв это за основу, мы можем затем изменить интересующую переменную на h0 при публикации нового отчета об ошибке Infer, сделав весь шаблон исправления более конкретным.
Как Getafix создает патчи
В качестве последнего шага нам нужно рассмотреть, как получить исходный код с ошибками и сгенерировать шаблон исправления из добытых выводов, чтобы сгенерировать патч исправления для исходного кода. В связи с этим у нас часто есть несколько режимов ремонта на выбор (как показано на предыдущей древовидной диаграмме). Поэтому следующая задача — как выбрать правильный режим для исправления конкретной ошибки. Если шаблон применяется к нескольким местоположениям, Getafix также должен выбрать правильное совпадение. Следующий пример иллюстрирует наш общий подход и практическое решение этой проблемы в Getafix.
Пример 1: Рассмотрим шаблон, который мы раскопали ранее: h0.h1() → if (h0 == null) return h0.h1();
Ниже мы кратко опишем, как сгенерировать следующий патч для совершенно незнакомого кода.
Getafix создает патч со следующими шагами:
Найдите под-AST, который соответствует части «до»: mListView.clearListeners();
Создание экземпляров уязвимостей h0 и h1
Замените sub-AST частью после создания экземпляра
Обратите внимание, что h0 в последней части связан, потому что он содержит немодифицированный контекст h0.h1();, что поможет ограничить количество мест, к которым применяется шаблон. Если контекст не изменен, шаблон будет → if (h0 == null) return;. Очевидно, что этот шаблон будет работать во многих непреднамеренных местах, например, после mListView.clearListeners(); или даже после mListView = null; .
На самом деле также возможно, что режим только для вставки также появляется где-то выше в дендрограмме, где режим с h0.h1(); этот контекст был инвертирован режимом, отвечающим за вставку return перед другим оператором. Следующие примеры иллюстрируют, как Getafix справляется с ситуациями, когда такие шаблоны слишком широки.
Пример 2. Рассмотрим следующий шаблон: h0.h1() → h0!=null && h0.h1()
Обычно этот патч должен исходить из шаблона исправления для условия if или выражения возврата, поэтому мы, безусловно, ожидаем, что он будет работать в таких контекстах. Но это также применимо и к другим ситуациям, таким как оператор вызова, упомянутый в приведенном выше примере: mListView.clearListeners();. Стратегия ранжирования Getafix пытается оценить эффективность исправления шаблона и назначить ему контексты, которые с наибольшей вероятностью обеспечат эффект исправления. Эта стратегия позволяет системе не полагаться на этап проверки при последующих запусках, тем самым значительно сокращая время вычислений.
Приведенный выше шаблон будет конкурировать с другими шаблонами, такими как более конкретный if (h0.h1()) { ... } → if (h0!=null && h0.h1()) { ... } или только в примере 1. Шаблоны, которые применяются к операторам вызова вместо выражений. Поскольку более конкретные шаблоны, как правило, имеют меньше совпадающих позиций, Getafix будет рассматривать их как более подходящие решения для текущей ситуации и присваивать им более высокий рейтинг.
Практическое применение и производительность Getafix
Getafix, который в настоящее время развернут в Facebook, отвечает за предоставление автоматических исправлений ошибки нулевого разыменования, о которой сообщает Infer. Кстати, Infer — это наш инструмент статистического анализа, который предлагает исправления ошибок, обнаруженных Sapienz, связанных с нулевыми разыменованиями. Кроме того, Getafix несет ответственность за некоторые важные ошибки, обнаруженные Infer в прошлом.
В ходе эксперимента, в ходе которого мы сравнили рекомендации по исправлению, рассчитанные Getafix, с предыдущими исправлениями, написанными человеком, мы обнаружили, что при исправлении различных ошибок вызова метода Infer null в наборе данных, состоящем примерно из 200 небольших правок, необходимо изменить менее 5 строк. Кроме того, примерно в четверти случаев лучшие исправления, предложенные Getafix, точно соответствовали исправлениям, созданным людьми.
В другом эксперименте мы просмотрели подмножество кодовой базы Instagram и попытались пакетно исправить около 2000 вызовов нулевых методов, которые там существовали. Getafix может попробовать исправить примерно 60% ошибок, и 90% этих попыток проходят автоматическую проверку — это означает, что он компилируется, и Infer больше не будет вас предупреждать. Всего Getafix автоматически исправила 1077 (около 53%) ошибок вызова нулевого метода.
Помимо предложений по исправлению новых ошибок Infer, мы используем тот же подход для устранения старых ошибок Infer, которые были отложены в предыдущих проверках кода. Мы устранили сотни ошибок Infer, которые возвращали необнуляемые значения, и ошибок Infer, в которых поля не могли быть обнулены. Интересно, что после того, как эта работа была проделана, Getafix стал лучше справляться с необнуляемыми возвратами и необнуляемыми полями в предложениях автоисправлений, при этом процент успешных исправлений увеличился на 56% и 51%, соответственно, до 62% и 59%. %. В целом набор предложений от Getafix помог нам успешно исправить сотни дополнительных ошибок за последние три месяца.
Getafix также генерирует предложения по исправлению для SapFix для обработки сбоев, обнаруженных Sapienz. Примерно половина исправлений, которые SapFix приняла за последние несколько месяцев, принадлежат Getafix и действительно работают (проходят все тесты). Из всех исправлений, которые Getafix предоставила SapFix, около 80% прошли все тесты.
Увеличение влияния Getafix
Getafix помогает нам достичь нашей большой цели — позволить компьютерам выполнять рутинную работу по исправлению ошибок. Поскольку мы продолжаем улучшать наши собственные инструменты тестирования и проверки, ожидается, что Getafix сможет лучше предотвращать различные сбои после развертывания в будущем.
Мы также заметили, что шаблоны исправлений, обнаруженные Getafix, были не просто реакцией на ошибки, о которых сообщала Infer; фактически, они также могли предлагать исправления на основе результатов ручной проверки кода. Этот дополнительный источник режима исправлений предоставит захватывающие возможности для автоматических повторяющихся проверок кода. Другими словами, в будущем мы можем передать ошибки в кодовой базе, которые были отмечены и исправлены много раз, непосредственно автоматическим инструментам без необходимости ручной проверки.
Getafix является частью нашего целостного подхода к созданию больших корпусов кода и интеллектуальных инструментов для статистического анализа связанных метаданных. Ожидается, что появление таких инструментов улучшит все аспекты жизненного цикла разработки программного обеспечения, включая обнаружение кода, качество кода и эффективность выполнения. Ценная информация, которую мы получаем от Getafix, поможет нам создать и развернуть множество других не менее важных инструментов в этой области.