Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
NixOS широко использует виртуальные машины на базе QEMU для запуска своего набора тестов. Чтобы не генерировать образ диска для каждого теста, тестовый драйвер обычно загружается с помощью ресурса Plan 9 File Protocol (9p) (сервер, реализованный QEMU) для Nix Store, который содержит все программы и конфиги, необходимые для теста.
Я работал над тестом VM, который копировал довольно большой объем данных (~278 тыс. файлов общим объемом ~5,3 Гбайт) из Nix Store на 9p, и был потрясен тем, сколько времени заняло копирование этих данных. На NVMe-устройствах я ожидал бы, что это займет несколько секунд или минут, но в итоге тест занял более 2 часов, большая часть которых ушла на копирование файлов с 9p. Поскольку для инкрементальной работы это непозволительно долго, я решил немного покопаться в этом вопросе и смог сократить продолжительность теста до всего 7 минут. В этой статье я опишу это небольшое приключение.
Профилирование QEMU
В качестве предисловия: У меня не так много опыта в отладке проблем с производительностью! Большинство из того, что я использовал, было для меня в новинку. Первое, что я хотел сделать, это выяснить, на что тратится так много времени. Я предположил, что это происходит в QEMU, а не в госте, и то, что это предположение оказалось верным, было вопросом чистого везения. Этот вопрос в stack overflow описывал проблему похожую на мою (но, к моему сожалению, не полностью). Это привело меня к тому, что я попытался использовать профилировщик для бедных — небольшой хак, состоящий из gdb, немного shell и awk.
Истории о неожиданностях и неудачах: профилировщик для бедных
Решив попробовать этот подход, я сразу же столкнулся с небольшим препятствием. gdb сказал:
warning: Target and debugger are in different PID namespaces;
thread lists and other data are likely unreliable.
Connect to gdbserver inside the container.
(предупреждение: Таргет и отладчик находятся в разных пространствах имен PID;
Списки потоков и другие данные, скорее всего, ненадежны.
Подключитесь к gdbserver внутри контейнера.)
Nix использует пространства имен Linux для обеспечения некоторой степени изоляции сборок от системы, на которой выполняется сборка, чтобы уменьшить влияние окружения конкретной машины на результат сборки ("чистота"). Сюда относятся и пространства имен PID, которые не позволяют процессам внутри пространства имен касаться процессов вне этого пространства имен. gdb был недоволен тем, что находится в пространстве имен PID, отличном от пространства имен процесса, на который он нацелился! Сначала я попытался запустить свой gdb в песочнице с помощью nsenter. Первый сюрприз, с которым я столкнулся, заключался в том, что вход в пространство имен PID не заставляет утилиты из procps, такие как ps, pgrep и top, сообщать только о процессах внутри нового пространства имен:
[root@oak:~]# pgrep -a qemu
1678991 /nix/store/6shk4z9ip57p6vffm5n9imnkwiks9fsa-qemu-host-cpu-only-for-vm-tests-7.0.0/bin/qemu-kvm [...]
[root@oak:~]# nsenter --target=1678991 --pid