Діагностика та аналіз конвеєра
| Поле | Значення |
|---|---|
| Категорія | Графи та конвеєри |
| Складність | Середній |
| Орієнтовний час читання | <10 minutes |
| Мітки | diagnostics, debugging, observability |
Коли конвеєр працює некоректно, виникає спокуса одразу ж переходити до налагодження на рівні окремих елементів. У цьому розділі ми розглянемо більш простий перший крок: повторюваний процес первинної перевірки, який відповідає на три запитання в певному порядку: Чи є контракт графа дійсним? Чи вдається виконати хоча б один прогін? Що показують діагностичні дані середовища виконання? Він виявляє більшість помилок конфігурації за лічені секунди, перш ніж вони перетворяться на багаточасову сесію, і працює з тим самим мінімальним графом «Вхід → Вихід», який ви вже знаєте з розділу 004.
Наприкінці ви перевірите контракт графа, виконаєте один виміряний прогін і надрукуєте звіт про вимірювання, який покаже, чи працює конвеєр належним чином.
Покроковий огляд
Перевірка контракту
validate() — це перевірка на рівні контракту, яка виконується перед build(). Вона перевіряє порядок вузлів, обмеження та шлях обробки на стороні сервера, не передаючи жодних даних, і повертає звіт, що містить стандартний error_code. Порожній код/ok означає, що структура графа є коректною; у будь-якому іншому випадку помилка класифікується (див. таксономію помилок нижче), щоб ви знали, де шукати. Виконання цієї перевірки на першому етапі означає, що ви ніколи не витрачатимете час на налагодження поведінки в середовищі виконання для графа, який ніколи не буде зібрано.
// validate() checks the Graph before build() and prints any caps problems.
auto report = graph.validate();
std::cout << "validate.error_code=" << report.error_code << "\n";
Запустіть один виміряний кадр.
Далі створіть і запустіть один детермінований кадр усередині вікна start_measurement(). output_memory = Owned запитує власні вихідні буфери, щоб результат залишався дійсним після виклику. Одного кадру достатньо: якщо операція завершилася успішно, конвеєр працює; якщо виникла помилка, виняток містить структурований звіт, який можна обробити так само, як і validate().
// Build a reusable runner and measure the caller-owned workload.
simaai::neat::RunOptions run_opt;
run_opt.output_memory = simaai::neat::OutputMemory::Owned;
auto run = graph.build(std::vector<cv::Mat>{rgb}, run_opt);
simaai::neat::MeasureOptions measure_opt;
measure_opt.title = "tutorial 011 diagnosis";
auto scope = run.start_measurement(measure_opt);
simaai::neat::TensorList out = run.run(std::vector<cv::Mat>{rgb}, /*timeout_ms=*/1000);
if (out.empty())
throw std::runtime_error("missing output tensor");
const simaai::neat::MeasureReport measured = scope.stop();
Перегляньте діагностичну інформацію середовища виконання
Маючи в активі лише один забіг, MeasureReport узагальнює стан конвеєра: лічильники inputs_enqueued, outputs_pulled, кількість втрачених пакетів, затримка від кінця до кінця, показники вузла, час роботи плагіна/ядра, час роботи на периферійному пристрої та опціональне живлення. MeasureReport::to_text() є базовим показником, який ви фіксуєте перед переходом до більш детальних досліджень і графіків DOT, описаних у розділі На практиці.
// Post-run diagnostics come from the measurement report.
std::cout << "measure.inputs_enqueued=" << measured.counters.inputs_enqueued
<< " outputs_pulled=" << measured.counters.outputs_pulled << "\n";
std::cout << "measure.text_size=" << measured.to_text().size() << "\n";
Запуск
Запустіть його, і ви повинні побачити код валідації та звіт про вимірювання, виведені в стандартний потік виводу (stdout). Запустіть команди Python і C++ (попередньо скомпільовані) з Neat встановити в кореневу директорію **(директорію, яка містить) share/ і lib/); виконайте команди build from source з кореневої директорії репозиторію. У цьому розділі не потрібен архів моделі.
C++ (prebuilt):
./lib/sima-neat/tutorials/tutorial_012_diagnose_a_pipeline
C++ (build from source):
./build.sh --target tutorial_012_diagnose_a_pipeline
./build/tutorials-standalone/tutorial_012_diagnose_a_pipeline
Очікуваний результат (значення лічильників і підсумковий рядок можуть відрізнятися в залежності від запуску):
validate.error_code=
measure.inputs_enqueued=1 outputs_pulled=1
measure.text_size=...
[OK] 012_diagnose_a_pipeline
(Під час збірки Python виводяться validate_error_code=, inputs_enqueued=... outputs_pulled=... і measure_text_size=...). Щоб інтегрувати вихідний код C++ з цього розділу у власний проєкт за допомогою спец іального файлу CMakeLists.txt (додаткова тека не потрібна), див. розділ Як запускати навчальні матеріали на головній сторінці.
На практиці
Структурована діагностика, таксономія помилок, параметри налагодження та робочий процес обробки збоїв плагінів, які ви використовуєте, коли validate() / start_measurement() / MeasureReport вказують на проблему.
GraphReport
GraphReport фіксує структуровану діагностику:
- рядок конвеєра (для відтворення)
- канонічний
error_code(для автоматизованої обробки) repro_note(зведена інформація для людини + підказка)- звіти про вузли та імена відповідних елементів
- повідомлення шини та деталі помилок
- необов’язкові лічильники потоку/часу
У разі виникнення помилки, NeatError містить GraphReport, який можна записати в журнал або серіалізувати.
Таксономія помилок
Помилки фреймворку використовують стабільні кодові групи:
| Код помилки | Опис | Типове рішення |
|---|---|---|
misconfig.pipeline_shape | Порушення контракту щодо порядку/форми вузлів | Переконайтеся, що Input() розміщено першим для конвеєрів, що передають дані, і Output() – останнім для конвеєрів, що отримують дані |
misconfig.caps | Несумісність перевантаження параметрів або суміжних контрактів вузлів | Узгодьте caps_override та оголошені контракти вузлів |
misconfig.media_caps | Несумісність параметрів медіа GStreamer під час виконання | Узгодьте формат, роздільну здатність і частоту кадрів або вставте конвертер |
misconfig.input_shape | Несумісність форми/розташування вхідного тензора/кадру/зразка | Перевірте ширину/висоту/глибину, розташування, тип даних і сховище |
build.plugin_missing | Відсутній необхідний елемент або кодек GStreamer | Встановіть/замініть його та перевірте за допомогою gst-inspect-1.0 |
build.property_invalid | Недійсне ім’я або значення властивості GStreamer | Перевірте за допомогою gst-inspect-1.0 <element> |
build.pipeline_syntax | У фрагменті GStreamer є синтаксична помилка | Виправте її та перевірте за допомогою gst-launch-1.0 |
runtime.pull | Операція отримання даних завершилася невдало без більш конкретної причини | Перегляньте прикріплений звіт і першу помилку у вихідному потоці |
io.parse | Помилка під час розбору/перевірки схеми JSON збереженого графа | Перевірте JSON і необхідні поля вузлів |
io.open | Помилка під час відкриття/читання/запису файлу збереженого графа | Перевірте існування шляху, дозволи та стан сховища |
PullError.code використовує ту саму таксономію (а не лише шляхи обробки винятків).
Це короткий перелік для первинної діагностики. Перегляньте повний каталог кодів помилок, зокрема інформацію про перехід від попередніх загальних кодів середовища виконання та збірки.
Програмна обробка
#include "pipeline/ErrorCodes.h"
#include "pipeline/NeatError.h"
try {
auto run = graph.build(input);
simaai::neat::Sample out;
simaai::neat::PullError perr;
const auto st = run.pull(500, out, &perr);
if (st == simaai::neat::PullStatus::Error) {
if (perr.code == simaai::neat::error_codes::kMediaCaps) {
// Fix the incompatible upstream/downstream media contract.
} else {
// Handle another specific code, including future codes, or report it.
}
}
} catch (const simaai::neat::NeatError& e) {
if (e.report().error_code == simaai::neat::error_codes::kPluginMissing) {
// Install or replace the missing GStreamer component.
}
}
Параметри налагодження (середовище)
Ключові змінні середовища (детальніше див. у розділі Архітекту ра):
SIMA_GST_DOT_DIR: запис DOT-графів для випадків помилокSIMA_GST_BOUNDARY_PROBES: лічильники потоку на межахSIMA_GST_ELEMENT_TIMINGS: часові мітки для кожного елементаSIMA_GST_FLOW_DEBUG: лічильники потоку для кожного елементаSIMA_GST_ENFORCE_NAMES: забезпечення дотримання угоди про іменування
Для короткого запуску з додаванням відредагованого необробленого контексту GStreamer до повідомлення про помилку, використовуйте:
SIMA_NEAT_VERBOSE_LEVEL=2 \
SIMA_NEAT_VERBOSE_TOPICS=gstreamer \
./your-neat-application
NEAT_LOG_LEVEL=debug не є налаштуванням Neat Library.
Відлагодження робочого процесу
- Зафіксуйте
GraphReport.error_codeта спочатку згрупуйте помилки за таксономією. - Зафіксуйте
GraphReport.repro_noteдля конкретного контексту та вбудованої підказки. - Зафіксуйте текст конвеєра:
Graph::describe_backend()абоlast_pipeline(). - Зафіксуйте структуровану діагностику:
MeasureReport::to_text()абоNeatError::report(). - Перевірте
GraphReport.busна наявність першого термінального джерелаERROR+ деталі. - Якщо середовище виконання зависає/перевищує час очікування, увімкніть зонди для меж/елементів, щоб локалізувати зупинку потоку.
Рекомендований пакет підтримки:
error_coderepro_note- повний
pipeline_string - перші 3-5 термінальних помилок шини (
GraphReport.bus) - перевантаження середовища, що використовуються під час запуску/валідації
Типові помилки → способи їх усунення
| Симптом | Ймовірна причина | Вирішення |
|---|---|---|
missing ... plugin | Не знайдено плагін GStreamer | Перевірте GST_PLUGIN_PATH, запустіть gst-inspect-1.0 <plugin> |
appsink 'mysink' not found | Відсутній термінальний елемент Output() | Переконайтеся, що Output є останнім елементом у конвеєрах під час виконання/збірки |
caps_override is set; renegotiation disabled | Зафіксовано властивості (caps) | Видаліть caps_override або зафіксуйте вхідні властивості (caps) |
tensor caps change not supported | Зміна форми/типу тензора під час виконання | Зберігайте стабільну форму/тип тензора (без повторного узгодження) |
Відлагодження помилок плагінів
Коли плагін не працює, Neat генерує NeatError, пов ідомлення якого містить помилку GStreamer і структурований рядок для відлагодження. Використовуйте поля, щоб швидко знайти основну причину.
-
Перегляньте структуровані поля. Знайдіть ключові/значеннєві поля
debugу тексті помилки:node: назва елемента, який викликав помилку в конвеєріconfig_path: файл конфігурації JSON (якщо застосовно)model_path: шлях до моделі/пакета (якщо застосовно)hint: рекомендації щодо вирішення проблемиdetail: додатковий контекст, наприклад відсутні ключі або стан алокатора
Див. розділ Довідник щодо формату помилок для отримання повного списку.
-
Перевірте контекст конвеєра. Використовуйте рядок конвеєра з
Graph::last_pipeline()або зі звіту про помилку:- Переконайтеся, що назва
nodeз’являється в конвеєрі. - Переконайтеся, що
config_pathіснує та доступний для читання. - У разі помилок, пов’язаних з caps, перевірте попередні елементи, які взаємодіють з проблемним вузлом.
- Переконайтеся, що назва
-
Засто суйте загальні виправлення.
- Помилки конфігурації: перевірте синтаксис JSON, наявність необхідних ключів і будь-яких шляхів до моделей.
- Помилки caps: додайте або виправте елементи парсера (наприклад,
h264parse), переконайтеся, що caps містять необхідні поля, такі якparsed=true,stream-format=byte-stream,alignment=au. - Помилки алокатора: переконайтеся, що попередні елементи використовують необхідний тип алокатора (системний або пам’ять/сегмент simaai).
-
Зберіть більше даних для діагностики за допомогою наведених вище параметрів налагодження (
SIMA_GST_DOT_DIR,SIMA_GST_FLOW_DEBUG,SIMA_GST_ELEMENT_TIMINGS).
Повний початковий код
Показати повні програми
// Two diagnostic commands: Graph::validate and Run::start_measurement.
//
// Usage:
// tutorial_012_diagnose_a_pipeline
#include "neat.h"
#include <opencv2/core.hpp>
#include <iostream>
#include <stdexcept>
int main() {
try {
cv::Mat rgb(96, 128, CV_8UC3, cv::Scalar(22, 44, 66));
if (!rgb.isContinuous())
rgb = rgb.clone();
simaai::neat::Graph graph;
simaai::neat::InputOptions in;
in.format = "RGB";
in.width = rgb.cols;
in.height = rgb.rows;
in.depth = rgb.channels();
graph.add(simaai::neat::nodes::Input(in));
graph.add(simaai::neat::nodes::Output());
// CORE LOGIC
// validate() checks the Graph before build() and prints any caps problems.
auto report = graph.validate();
std::cout << "validate.error_code=" << report.error_code << "\n";
// Build a reusable runner and measure the caller-owned workload.
simaai::neat::RunOptions run_opt;
run_opt.output_memory = simaai::neat::OutputMemory::Owned;
auto run = graph.build(std::vector<cv::Mat>{rgb}, run_opt);
simaai::neat::MeasureOptions measure_opt;
measure_opt.title = "tutorial 011 diagnosis";
auto scope = run.start_measurement(measure_opt);
simaai::neat::TensorList out = run.run(std::vector<cv::Mat>{rgb}, /*timeout_ms=*/1000);
if (out.empty())
throw std::runtime_error("missing output tensor");
const simaai::neat::MeasureReport measured = scope.stop();
// Post-run diagnostics come from the measurement report.
std::cout << "measure.inputs_enqueued=" << measured.counters.inputs_enqueued
<< " outputs_pulled=" << measured.counters.outputs_pulled << "\n";
std::cout << "measure.text_size=" << measured.to_text().size() << "\n";
std::cout << "[OK] 012_diagnose_a_pipeline\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}