Ориентация главы
Заключительная глава тома 5 (композиционного типа) не учит новым модулям — она сцепляет инструменты наблюдения, разбросанные по трём главам, в боевой конвейер отладки: внедрение отказов (глава 6) активно создаёт экстремальные условия (OOM, пути отказа), статистика памяти (глава 6) даёт числовые улики порядка величин и тренда, журналы (главы 38/39) восстанавливают сцену и цепочку причин. У каждого свой пост: внедрение создаёт «болезнь», статистика локализует «на каком она слое», журналы отвечают «что тогда произошло». Тезис композиционной главы: способность к отладке — не груда инструментов, а совместный дизайн их взаимодействия — где ставить точку внедрения, как резать окно статистики, какие поля писать в журнал: эти решения «как сцепить» заслуживают дизайна больше, чем сами инструменты.
50.1引入
Реальный сценарий отладки: у службы в продакшене изредка растёт память, раз в неделю спасает перезапуск. Путь без системы наблюдения: догадки («может, соединения не закрываются?») → добавить printf → перезапустить и смотреть → снова догадываться — цикл на недели. Путь с боевым конвейером: открыть окно статистики (сравнение снимков до/после подтверждает «какой порядок величин и какой тип растёт») → заподозрив путь, включить отладочную кучу (VisitLive находит неосвобождённые распределения и место рождения) → заодно прогнать внедрение отказов для проверки путей отказа (инъекция OOM — и смотрим, полон ли откат; утечки часто идут в паре с неполными путями отказа) → журналы с уровнями и полями уже развёрнуты структурно — таймлайн читается напрямую. Одна и та же проблема: недели превращаются в часы — разница не в сообразительности, а в том, что три звена конвейера снабжают друг друга.
Второй сценарий профилактический: новый модуль управления ресурсами нужно принять. Инъекция по очереди сканирует все точки отказа распределений (скелет OOM главы 6), статистика подтверждает обнуление живого, журнал записывает цепочку ошибок каждого пути отказа — предрелизный медосмотр и посмертное вскрытие используют один и тот же конвейер, различаясь лишь порядком (медосмотр: сперва инъекция, потом наблюдение; вскрытие: сперва наблюдение, потом инъекция).
50.2概念
Панорама боевого конвейера
Выбор точек внедрения: не сканировать всё подряд
В лоб ответ на внедрение отказов — «инъекция в каждую точку распределения» (метод упражнений главы 6: правильно, но медленно). Стратегия выбора продакшен-класса: границы прежде — откат отказа в точках захвата ресурсов (открыть/распределить/подключиться) самый сложный, сканировать в первую очередь; новый код прежде — пути отказа нового кода продакшеном не проверялись, перед релизом сканировать обязательно; изменённый код прежде — реферакторенные пути пересканировать. Три приоритета кладут бюджет сканирования (время) на лезвие — проекция тестового взгляда главы 26 «граничные значения прежде охвата» на измерение ресурсов.
Согласованный дизайн окон статистики и полей журнала
Связка полей инструментов наблюдения — главный урок совместного дизайна. Границы окна статистики (два момента Reset/Get) должны быть совместимы с метками времени журнала — «что происходило внутри окна» отвечает таймлайн журнала; структурные поля журнала должны включать измерения, важные для статистики, — каждая журнальная запись запроса несёт поле величины распределения (alloc_bytes), и при аномалии статистики по журналу отыскивается, какой класс запросов внёс вклад; утверждения тестов внедрения должны идти в журнал (структурно), а не в printf — отчёты тестов становятся обысканными и сравнимыми. Дизайн полей трёх инструментов планируется совместно, до начала работы — в этом и смысл существования «композиционных глав»: одиночная глава учит пользоваться инструментом, композиционная — проектировать их интерфейсы.
Этика отладочной кучи: накладные расходы и момент
Детальные записи отладочной кучи (memory_debug) стоят заметных накладных расходов — держать их в продакшене постоянно — роскошь. Стратегия момента: включать по нужде (по тревоге или при разборе — открыть окно), воспроизводить внутри окна (с включённой кучей прогреть триггерную нагрузку), вовремя выключать (взяв улики, закрыть; не мешать измерению производительности). Модуль статистики — наоборот: почти ноль расходов, в продакшене можно держать всегда (часовой наблюдения тренда). Форма их содружества: статистика всегда начеку на вопрос «есть ли проблема», отладочная куча по запросу — «где проблема»: дешёвое постоянно, дорогое по нужде — это экономика системы наблюдения.
Место цепочки ошибок в диагностике
Цепочка ошибок (цепь причин) главы 4 имеет в диагностическом конвейере собственное место: поле ошибки журнала (xrtLogFieldError) несёт полную цепь причин — 资源释放失败 → 底层 IO 错误 → 系统错误码 (ошибка освобождения ресурса → ошибка нижележащего IO → системный код ошибки) — трёхзвенная цепь видна одним взглядом. При разборе «поверхностная ошибка» (освобождение не удалось) и «корень» (диск полон) живут в одном объекте — не нужно сшивать причинность по строкам журнала. Глубина дизайна цепочки (дисциплина «обёртки на границах» главы 4) напрямую определяет диагностическую ценность журнала: обёрнуто хорошо — одно сообщение журнала видит всё дело; обёрнуто плохо — три сообщения причинность не сшивают.
50.3示例
Полная программа: медосмотр трёх звеньев
Минимальная реализация конвейера медосмотра — окно статистики, внедрение отказа, журнальная запись, три звена одним проходом:
/* health_check.c - предрелизный медосмотр: инъекция -> статистика -> журнал в связке */ #define XRT_MODULE_ALL #define XRT_IMPLEMENTATION #include <xrt.h> #include <stdio.h> static xlogger* gLog; static bool businessRun(void) { /* проверяемый бизнес: два распределения; штатный путь освобождает одно, путь отказа обязан освободить все */ ptr pA = xrtMalloc(64); ptr pB = xrtMalloc(128); bool bOk = false; if ( (pA != NULL) && (pB != NULL) ) { bOk = true; } xrtFree(pA); xrtFree(pB); return bOk; } int main(void) { xmemstats Stats; int iPass = 0; int iFail = 0; gLog = xrtLogCreate(XRT_STR_LITERAL("health"), XLOG_DEBUG); if ( gLog == NULL ) { return 1; } /* звено 1: открыть окно статистики */ xrtMemStatsEnable(); xrtMemStatsReset(); /* звено 2: скан инъекцией (первые N успешны, N+1-й отказывает) */ for ( uint64 N = 0; N < 3; ++N ) { xrtMemDebugEnable(true); xrtMemDebugFailAfter(N); if ( businessRun() ) { ++iPass; } else { ++iFail; /* звено 4: отказ идёт структурным журналом (цепочка ошибок при нём) */ xrtLog(gLog, XLOG_WARN, XRT_STR_LITERAL("inject point failed")); } xrtMemDebugFailClear(); xrtMemDebugEnable(false); } /* звено 3: показания окна - живое должно быть нулём (полный откат пути отказа) */ xrtMemStatsGet(&Stats); printf("pass=%d fail=%d live=%llu\n", iPass, iFail, (unsigned long long)(Stats.MallocBytes - Stats.FreeBytes)); xrtLogFree(gLog); return (Stats.MallocBytes - Stats.FreeBytes) == 0 ? 0 : 1; }
Где стоит этот код. Компактное соединение трёх звеньев: Reset/Get статистики размечает окно измерения (до и после прогона бизнеса); цикл FailAfter(N) сканирует первые три точки распределения (businessRun распределяет ровно дважды — третья точка не находит цели и проходит естественно); журнал оставляет структурную запись на ветви отказа (в реальном проекте здесь поле цепочки ошибок). live=0 — ключевое утверждение всей программы: при любой точке внедрения, после отказа бизнеса все ресурсы возвращены — машинное свидетельство «полного отката пути отказа». Замените каркас на вашу бизнес-функцию — и получите медосмотр модуля перед релизом.
Полная программа: локализатор утечек
Конвейер вскрытия — статистика нашла аномалию, отладочная куча указывает источник утечки:
/* leak_hunt.c - VisitLive ищет неосвобождённые распределения и место рождения */ #define XRT_MODULE_ALL #define XRT_IMPLEMENTATION #include <xrt.h> #include <stdio.h> static bool reportLeak(const xmemdebugallocation* pAlloc, ptr pData) { size_t* pCount = (size_t*)pData; ++(*pCount); printf("leak: %zu bytes at %s:%d\n", pAlloc->Size, pAlloc->File, (unsigned)pAlloc->Line); return true; /* продолжаем обход */ } int main(void) { size_t iLeaks = 0; xrtMemDebugEnable(true); /* имитация бизнеса: один блок нарочно не освобождён */ (void)xrtMalloc(256); iLeaks = xrtMemDebugVisitLive(reportLeak, &iLeaks); printf("total leaks=%zu\n", iLeaks); xrtMemDebugEnable(false); return 0; }
Где стоит этот код. Ядро вскрытия: xrtMemDebugVisitLive обходит распределения, живые на данный момент, — каждое с размером и местом рождения (файл:строка; заслуга семейства At главы 5); обратный вызов печатает список и считает. В реальном употреблении «имитацию бизнеса» заменяет воспроизводящая нагрузка, и список утечек — дорожная карта ремонта. Содружество с конвейером медосмотра: статистика увидела ненулевой live (часовой подал голос) → включили отладочную кучу и воспроизвели (лупа) → VisitLive выдал список (цепь улик) → после ремонта — возврат к медосмотру (проверка). Две программы вместе — полный контур «профилактика-диагностика-лечение»: медосмотр перед релизом останавливает новые болезни, часовой в рантайме сообщает об аномалиях, локализация по нужде выдаёт список.
Четыре контрольных пункта дизайна звеньев (наблюдательная редакция главы 36)
Глава 36 задала четыре пункта для конвейера данных; у конвейера наблюдения — их зеркало.Пункт 1: вопрос наблюдения ясен? — каждый инструмент отвечает на один ясный вопрос (статистика — «есть ли/сколько», отладка — «где», внедрение — «что при экстремуме», журнал — «когда/почему») — инструментов, отвечающих на два вопроса, не бывает, а потребность в двух наблюдениях не запихивают в одно.Пункт 2: поля распланированы? — границы окон, корреляционные ID, поля величин, цепочки ошибок объединяются на этапе проектирования (объект правоприменения ловушки 1).Пункт 3: расходы оплачены в нужном месте? — часовой постоянно, лупа по нужде (объект правоприменения ловушки 2).Пункт 4: утверждения машинизируемы? — выводы медосмотра (live=0, список точек внедрения) — обысканный структурный вывод, а не отчёт для человеческих глаз. Четыре пункта пройдены — и система наблюдения «эксплуатируема», а не просто «запускаема».
Взгляд из композиционной главы на том 5: наблюдательное лицо системных сервисов
Том 5 закрывается; оглядываясь, видишь: конвейер отладки — сгущение всего тома. Две главы о журналах дали таймлайн и структурные поля, глава 6 (том 1) — внедрение и статистику, глава 4 — цепь причин, файловое семейство — носитель «снимков окна на диск для сравнения» (запись данных окон статистики в файлы для diff до/после), асинхронность и сигналы — форму развёртывания «наблюдать не останавливая службу» (перезагрузка наблюдательной конфигурации по пути SIGHUP).Каждый модуль системных сервисов снабжает наблюдение боеприпасами — вот конкретный смысл тезиса «наблюдаемость — не модуль, а свойство архитектуры». С этим взглядом перечитайте контрактные зоны глав тома: как минимум половина статей служит диагностике.
Рабочий взгляд: наблюдение — платная страховка
Вложения в систему наблюдения похожи на страховку: пока ничего не случилось — чистые расходы (накладные расходы, время проектирования полей, поддержка скриптов медосмотра), когда случилось — вся отдача (локализация за часы против догадок неделями). Три совета по страхованию: базовый полис обязателен — часовой статистики всегда включён + структурные журналы с корреляционными ID: минимальная цена, широчайшее покрытие; доплата в зоне бедствия — ресурсоёмким модулям (пулы/соединения/кеш) — скрипты медосмотра и учения по локализации утечек; репетиция процесса выплат — путь «тревога → включение кучи → воспроизведение → улики» репетировать раз в квартал (нерепетируемая страховка равна не купленной). Взгляд страховки поднимает наблюдение из «технического решения» в «решение о рисках» — при урезанном бюджете что резать, а что держать, отвечает актуарная математика.
50.4契约
- Порядок конвейера: разведка (статистика) → локализация (отладочная куча) → проверка (внедрение) → таймлайн (журнал) — выход каждого звена входит в следующее.
- Бюджет внедрения: границы прежде, новый код прежде, изменённый код прежде — сканирование на лезвии, а не полный перебор.
- Связка полей: границы окон совместимы с метками времени журнала, журнал несёт поля измерений статистики, утверждения идут структурным журналом — планировать совместно до работы.
- Экономика наблюдения: статистика постоянно (часовой), отладочная куча по нужде (лупа) — дешёвое всегда, дорогое по запросу.
- Место цепочки ошибок: поле ошибки журнала несёт полную цепь причин — глубина обёртки определяет диагностическую ценность (дисциплина границ главы 4).
- Изоморфизм медосмотра и вскрытия: один конвейер, разный порядок — перед релизом и в рантайме не учат два раза.
Путь эволюции: строительство наблюдения с нуля
Система наблюдения не строится за день; у четырёх ступеней эволюции свои вложения и отдачи.Ступень 1 (работает): printf заменён структурным журналом + часовой статистики всегда включён — неделя работы, покрывает «если что случится — хотя бы таймлайн есть».Ступень 2 (диагностируемо): корреляционные ID во всех полях журнала + цепочки ошибок обёрнуты по конвенции + окна статистики рутинизированы — таймлайн сшивает причинность.Ступень 3 (проверяемо): медосмотр ключевых модулей перед релизом (сканирование инъекцией + утверждение нуля) + учения по локализации утечек — новые болезни перехвачены до релиза.Ступень 4 (самовосстанавливаемо): тревога автоматически открывает окно и берёт улики + горячая перезагрузка наблюдательной конфигурации (путь SIGHUP) + регрессия медосмотра в CI — наблюдение само становится частью службы. Большинство команд останавливается на первой с половиной; третья — точка перегиба окупаемости (выгода перехвата новых болезней начинает превышать вложения); четвёртая — для служб масштаба. На какой ступени ваша команда и каков следующий рубеж — вопрос, на который должен отвечать технический руководитель.
Разбор станции: практическая форма сканирования инъекцией
«Построчное сканирование» скелета OOM главы 6 в реальной работе заслуживает полного разворота.Обмер диапазона: сперва окно статистики даёт общее число распределений (скажем, 200), бюджет сканирования режется по приоритетам (границы прежде — первые 50 точек захвата ресурсов).Тройка утверждений: на каждую точку внедрения — отказ случился как положено (инъекция сработала), цепочка ошибок полна (журнал проверяем), живое обнулилось (откат полон).Отсев ложных срабатываний: некоторые «отказы» — штатная деградация бизнеса (повтор удался) — утверждения обязаны различать «отказ обработан правильно» и «отказ проглочен ошибочно»; первое — pass, второе — fail.Архивирование продукта: результаты скана пишутся в файл (список точек + тройка результатов), при регрессии сравниваются различия — атомарная доставка главы 46 гарантирует целостность самого архива. Эта форма повышает «инъекцию» из игрушки до регрессируемого тестового актива.
Разбор станции: диагностический словарь полей журнала
Дизайн полей структурного журнала — «модель данных» системы наблюдения; завершает главу словарь: поля корреляции — request_id (весь путь запроса), trace_id (между службами), session_id (сессия пользователя) — первичные ключи сшивки таймлайна; поля величин — alloc_bytes/duration_us/count — измерения обратного поиска при аномалиях статистики; поля состояния — level, error (цепочка ошибок), result — источник статистики распределений и доли отказов; поля контекста — module, source (место в коде), endpoint — точки приземления локализации. Имена полей стабильны (журнал — downstream-зависимость; переименование — разрушительное изменение); набор полей версионируется (новые добавлять можно, старым смысл не менять); сам словарь идёт в документацию — новичок умеет читать журнал, только тогда умеет пользоваться таймлайном. Словарь един духом со схемой JSON главы 32: модель данных прежде накопления данных.
Три высокочастотных диагностических сценария
Три сценария приземляют конвейер на конкретные симптомы.Сценарий первый: память медленно ползёт — часовой статистики подтверждает (сравнение окон: растёт pooled или direct) → поиск по типу роста (pooled — к утечкам пула, direct — к голым распределениям) → окно отладочной кучи и воспроизведение → VisitLive выдаёт список → после ремонта — регрессия инъекцией.Сценарий второй: отказы без закономерности — журнал фильтруется по категории ошибки (стратегия уровней главы 38) → корреляционный ID сшивает полный таймлайн отказавшего запроса → локализация на общем состоянии → инъекция параллельного давления (класс гонок) или инъекция отказа IO (класс среды) для воспроизведения.Сценарий третий: крушение на старте — инъекция в самые ранние точки распределения (OOM стартового пути часто не тестирован) → цепочка ошибок первого звена → после ремонта — скан всего стартового пути. Общая структура трёх сценариев: симптом → выбор входа конвейера → порядок звеньев → форма улик — перепишите их в справочник команды, и паника новичка в первое дежурство уменьшится вдвое.
Предупреждение об антипаттерне: проектирование от наблюдения
Последнее предупреждение уравновешивает позицию главы: наблюдение важно, но наблюдение не заменяет проектирование. Форма антипаттерна: обработка ошибок абы как («журнал всё покажет»), расхлябанность ресурсной дисциплины («статистика найдёт»), пропуски путей отказа («инъекция поймает») — наблюдение принято в компенсаторы проектных дефектов. Правильное разделение: проектирование гарантирует правильность, наблюдение проверяет правильность — цепочку ошибок главы 4 сперва обёрни по дисциплине границ, и только тогда журнал получит читаемую цепь; владение главы 5 сперва уравновесь, и только тогда статистика обнулится; пути отказа сперва допиши, и только тогда инъекция пройдёт. Ценность системы наблюдения стоит на качестве проектирования — фундамент это дизайн, страховка это наблюдение, и порядок не обратим. С этим предупреждением покидайте том 5: в томе 6 оно вернётся в усиленной форме — правильность параллелизма держится на дизайне (протоколы блокировок/передача сообщений), наблюдение лишь находит нарушения.
Типичные ошибки
Симптом: статистика показывает аномалию, но в журнале за тот период записей нет; вывод тестов внедрения рассыпан по printf и не совмещается с таймлайном журнала; на каждом разборе приходится «изобретать на месте» комбинацию инструментов.
Причина: инструменты развёрнуты по одиночным главам, поля и окна никогда не проектировались в связке — три инструмента записывают каждое на своём языке.
/* окно статистики на скорую руку, результаты инъекции в printf, журнал добавляется по воспоминанию */ xrtMemStatsReset(); RunLoad(); printf("done\n"); /* инъекция/результаты через printf - вне таймлайна */
/* границы окна, поля журнала и вывод утверждений согласованы на этапе проектирования */ xrtLog(gLog, XLOG_INFO, XRT_STR_LITERAL("window start")); /* граница окна - в журнал */ xrtMemStatsReset(); RunLoad(); xrtMemStatsGet(&Stats); xrtLogFieldInt(gLog, XLOG_INFO, XRT_STR_LITERAL("live_bytes"), (int64)(Stats.MallocBytes - Stats.FreeBytes)); /* показания - в структурный журнал: таймлайн обыскиваем */
Симптом: производительность службы просела на двадцать процентов; оптимизация идёт всё медленнее (тюнингуют накладные расходы кучи, а не бизнес).
Причина: «лупу» употребили как «часового» — цену детальных записей платят постоянно; правильная экономика — статистика всегда, отладка по нужде.
int main(void) { xrtMemDebugEnable(true); /* включена со старта - детальные расходы всю дорогу */ /* ... месяцы продакшена ... */ }
int main(void) { xrtMemStatsEnable(); /* часовой постоянно: почти ноль расходов */ /* ... продакшен ... */ } void onMemoryAlert(void) /* при срабатывании тревоги */ { xrtMemDebugEnable(true); /* лупа по нужде: открыть окно, воспроизвести, взять улики */ RunRepro(); xrtMemDebugVisitLive(...); /* и сразу закрыть */ xrtMemDebugEnable(false); }
Эта глава закрывает том 5 и является вторым появлением композиционной главы в наблюдательной области (глава 36 — область обработки данных). Оставить она хочет не «употребление трёх инструментов» (одиночные главы научили), а способность собирать инструменты в систему — дизайнерское чутьё связки полей, распределение расходов экономики наблюдения, изоморфное переиспользование медосмотра и вскрытия, страховые решения о вложениях. Эти способности «как сцепить» в томе 12 (глава 133, система тестирования) и томе 13 (операционная приёмка боевых проектов) будут названы снова — и тогда, глядя назад с конвейерным мышлением этой главы, вы увидите, что тестирование и эксплуатация — два продолжения этого конвейера: вперёд, в процесс релиза (медосмотр), и назад, в работающую эксплуатацию (вскрытие). Следующий том входит в параллелизм — а система наблюдения это «прибор ночного видения» параллельных программ: воспроизведение гонок, локализация deadlock-ов, измерение пропускной способности — всё держится на конвейере этого тома, работающем в параллельном контексте.
Заранее скажем факт: наблюдение в параллельном мире труднее описанного в этом томе. Семантика окон статистики при параллелизме обязана учитывать «распределения в полёте»; сканирование точек внедрения при многопоточности должно не дать инъекции одного потока расстроить утверждения другого; порядок журналов при многопоточности перемешивается (миллисекундная точность меток нужна для восстановления истинного порядка). Том 6 заново вызовет инструменты этого тома в параллельном контексте — статистика пулов потоков главы 55, наблюдение противодавления каналов главы 57, аудит отмены групп задач главы 60 — каждый вызов будет помечен «отличиями от однопоточного употребления».Конвейер этого тома — фундамент; том 6 добавит к нему параллельные поправки — дом на слабом фундаменте падает первым в параллельном шторме.
Практические задания
Воспроизведите образец медосмотра: сканирование трёх точек инъекцией, проверка нуля окном статистики, журнал на ветви отказа — замените businessRun собственной функцией «два распределения, одно может отказать».
Для произвольной нагрузки прогоните тройку «открыть окно — нагрузка — снять показания», с границами окна и показаниями, целиком идущими в структурный журнал (метка времени + поля); три круга подряд — и по таймлайну журнала восстановите окно и показания каждого круга — минимальное доказательство связки полей.
Сделайте медосмотр модуля пулов главы 22: инъекцией просканируйте все точки распределения создания/изъятия-возврата/разрушения пула (бюджет по принципу «границы прежде»), статистикой проверьте обнуление живого при любой инъекции, цепочки ошибок всех путей отказа — в журнал. Критерии приёмки: отчёт медосмотра (структурный журнал) прямо грепается в список отказавших точек инъекции; скрипт медосмотра перезапускаем (для регрессии); за время медосмотра ноль ложных срабатываний.
Итоги
| Тема | Кратко |
|---|---|
| Порядок конвейера | статистика разведывает → отладка локализует → инъекция проверяет → журнал хронометрирует - звенья снабжают друг друга |
| Бюджет инъекций | границы/новое/изменённое - три приоритета, а не полный перебор |
| Связка полей | окна совместимы с метками времени, журнал несёт измерения статистики, утверждения структурны - согласовать на проектировании |
| Экономика наблюдения | статистика-часовой постоянно, куча-лупа по нужде - дешёвое всегда, дорогое по запросу |
| Цепочка ошибок | поле ошибки журнала несёт всю цепь - глубина обёртки главы 4 определяет диагностическую ценность |
| Медосмотр/вскрытие | один конвейер, два порядка - до релиза ловим новое, в рантайме диагностируем старое |
| Свидетельство нуля утечек | live=0 при любой точке инъекции - машинное доказательство полного отката пути отказа |
📖 Справочники API в этой главе:memory_debug (19 записей)memory_stats (5 записей)logger (116 записей)error (34 записей)
Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки