Перейти до основного вмісту

Діагностика та аналіз конвеєра

ПолеЗначення
КатегоріяГрафи та конвеєри
СкладністьСередній
Орієнтовний час читання<10 minutes
Міткиdiagnostics, debugging, observability

Коли конвеєр працює некоректно, виникає спокуса одразу ж переходити до налагодження на рівні окремих елементів. У цьому розділі ми розглянемо більш простий перший крок: повторюваний процес первинної перевірки, який відповідає на три запитання в певному порядку: Чи є контракт графа дійсним? Чи вдається виконати хоча б один прогін? Що показують діагностичні дані середовища виконання? Він виявляє більшість помилок конфігурації за лічені секунди, перш ніж вони перетворяться на багаточасову сесію, і працює з тим самим мінімальним графом «Вхід → Вихід», який ви вже знаєте з розділу 004.

Наприкінці ви перевірите контракт графа, виконаєте один виміряний прогін і надрукуєте звіт про вимірювання, який покаже, чи працює конвеєр належним чином.

Покроковий огляд

Перевірка контракту

validate() — це перевірка на рівні контракту, яка виконується перед build(). Вона перевіряє порядок вузлів, обмеження та шлях обробки на стороні сервера, не передаючи жодних даних, і повертає звіт, що містить стандартний error_code. Порожній код/ok означає, що структура графа є коректною; у будь-якому іншому випадку помилка класифікується (див. таксономію помилок нижче), щоб ви знали, де шукати. Виконання цієї перевірки на першому етапі означає, що ви ніколи не витрачатимете час на налагодження поведінки в середовищі виконання для графа, який ніколи не буде зібрано.

tutorials/012_diagnose_a_pipeline/diagnose_a_pipeline.cpp
// 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().

tutorials/012_diagnose_a_pipeline/diagnose_a_pipeline.cpp
// 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, описаних у розділі На практиці.

tutorials/012_diagnose_a_pipeline/diagnose_a_pipeline.cpp
// 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.

Відлагодження робочого процесу

  1. Зафіксуйте GraphReport.error_code та спочатку згрупуйте помилки за таксономією.
  2. Зафіксуйте GraphReport.repro_note для конкретного контексту та вбудованої підказки.
  3. Зафіксуйте текст конвеєра: Graph::describe_backend() або last_pipeline().
  4. Зафіксуйте структуровану діагностику: MeasureReport::to_text() або NeatError::report().
  5. Перевірте GraphReport.bus на наявність першого термінального джерела ERROR + деталі.
  6. Якщо середовище виконання зависає/перевищує час очікування, увімкніть зонди для меж/елементів, щоб локалізувати зупинку потоку.

Рекомендований пакет підтримки:

  • error_code
  • repro_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 і структурований рядок для відлагодження. Використовуйте поля, щоб швидко знайти основну причину.

  1. Перегляньте структуровані поля. Знайдіть ключові/значеннєві поля debug у тексті помилки:

    • node: назва елемента, який викликав помилку в конвеєрі
    • config_path: файл конфігурації JSON (якщо застосовно)
    • model_path: шлях до моделі/пакета (якщо застосовно)
    • hint: рекомендації щодо вирішення проблеми
    • detail: додатковий контекст, наприклад відсутні ключі або стан алокатора

    Див. розділ Довідник щодо формату помилок для отримання повного списку.

  2. Перевірте контекст конвеєра. Використовуйте рядок конвеєра з Graph::last_pipeline() або зі звіту про помилку:

    • Переконайтеся, що назва node з’являється в конвеєрі.
    • Переконайтеся, що config_path існує та доступний для читання.
    • У разі помилок, пов’язаних з caps, перевірте попередні елементи, які взаємодіють з проблемним вузлом.
  3. Застосуйте загальні виправлення.

    • Помилки конфігурації: перевірте синтаксис JSON, наявність необхідних ключів і будь-яких шляхів до моделей.
    • Помилки caps: додайте або виправте елементи парсера (наприклад, h264parse), переконайтеся, що caps містять необхідні поля, такі як parsed=true, stream-format=byte-stream, alignment=au.
    • Помилки алокатора: переконайтеся, що попередні елементи використовують необхідний тип алокатора (системний або пам’ять/сегмент simaai).
  4. Зберіть більше даних для діагностики за допомогою наведених вище параметрів налагодження (SIMA_GST_DOT_DIR, SIMA_GST_FLOW_DEBUG, SIMA_GST_ELEMENT_TIMINGS).

Повний початковий код

Показати повні програми
tutorials/012_diagnose_a_pipeline/diagnose_a_pipeline.cpp
// 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;
}
}

Джерело