Логи и инструменты диагностики

Логирование, профилирование производительности и диагностика памяти устройства.

15. Log - логирование

Лёгкий логгер ядра. Уровень проверяется до форматирования (отключённый уровень не тратит такты на разбор строки формата). Вывод идёт в платформенный бэкенд плюс опциональный приёмник строк.

enum class log::Level : uint8_t
{
    Verbose = 0, Debug = 1, Info = 2, Warning = 3, Error = 4, Off = 5
};

using log::Sink = void (*)(void *user, Level level, const char *line) noexcept;
Функция Описание
void log::setLevel(Level level) Установить порог уровня в рантайме
Level log::level() Текущий порог
bool log::enabled(Level level) Пройдёт ли сообщение такого уровня через фильтр - полезно, чтобы не готовить дорогие аргументы
void log::setSink(Sink sink, void *user) Дополнительный приёмник отформатированных строк (nullptr - отключить)
void log::print(Level level, const char *fmt, ...) Вывод с printf-форматированием
void log::vprint(Level level, const char *fmt, std::va_list args) То же с va_list
log::verbose/debug/info/warning/error(const char *fmt, ...) Сокращения для соответствующего уровня
  • Стартовый уровень - PIPCORE_LOG_LEVEL (по умолчанию Info). Сообщения ниже порога не форматируются вообще.
  • На ESP32 вывод идёт через логгер IDF (метка pipcore, с меткой времени и уровнем) - виден в idf.py monitor; в симуляторе - в консоль.
  • setSink() передаёт каждую уже отформатированную строку вашему колбэку. Так симулятор показывает логи в своей консоли; так же логи можно пересылать куда угодно (по сети, в файл).
log::info("heap: internal %u KB", unsigned(plat->freeHeapInternal() / 1024));
log::error("display: %s", plat->lastErrorText());

if (log::enabled(log::Level::Debug))
    log::debug("state dump: %s", buildExpensiveDump());

16. Debug - профайлер и трекер аллокаций

Включается PIPCORE_ENABLE_DEBUG. В production-сборке (выключено) макросы профайлера превращаются в пустышки - нулевая цена.

Профайлер

void hotPath()
{
    PIP_PROFILE_FUNCTION();        // зона на всю функцию (имя из __PRETTY_FUNCTION__)
    PIP_PROFILE_ZONE("parse");     // именованная зона в любом scope
    // ...
}

Макросы строят дерево узлов: суммарное и собственное время (в тактах CPU), число вызовов, максимальная длительность зоны. Точность измерения - до такта (debug::profileCycles()). Каждая зона - одна строка кода: не ставьте две PIP_PROFILE_ZONE на одной строке.

Данные читает PipCore Inspector (GET_PROFILE) или вы вручную:

auto &prof = debug::Profiler::instance();
prof.calculateSelfCycles();                 // пересчёт собственного времени
for (debug::ProfileNode *n = prof._head; n; n = n->next)
    log::info("%s: %u cycles x%u", n->name, unsigned(n->totalCycles), unsigned(n->callCount));
prof.clear();                               // обнулить счётчики

Трекер аллокаций

Все глобальные operator new/delete идут через трекер: он ведёт текущие и пиковые байты, теги точек выделения и полный список живых аллокаций (виден в Inspector).

debug::AllocStats st = debug::allocStats();
log::info("heap now %u, peak %u", unsigned(st.currentBytes), unsigned(st.peakBytes));

debug::allocStats() и AllocStats доступны только при PIPCORE_ENABLE_DEBUG, поэтому при использовании оберните вызовы в #if PIPCORE_ENABLE_DEBUG.

Отладочная консоль (PipCore Inspector)

PIPCORE_DEBUG_CONSOLE поднимает ASCII-консоль (таск PipCoreConsole, транспорт - USB-Serial/JTAG или UART0) для десктопного инспектора.

Команда Действие
GET_ALLOCS Список живых аллокаций
GET_FLASH Информация о флеш-памяти
GET_NVS Содержимое NVS
GET_CPU Загрузка CPU
GET_PROFILE / RESET_PROFILE Данные профайлера / сброс
GET_FS Содержимое файловой системы
GET_FILE:<path> Скачать файл
WRITE_START:<path> → WRITE_CHUNK:<hex> → WRITE_END Загрузить файл по частям
DELETE_FILE:<path> Удалить файл
GET_PARTITIONS Таблица разделов
READ_FLASH:<offset>,<size> Прочитать флеш (до 1 КБ за запрос)
GET_BOOT_SECURITY Состояние защиты загрузки
Важно: консоль без аутентификации

Любой, у кого есть доступ к порту, может прочитать всю флеш-память и NVS, а также скачать, перезаписать и удалить файлы в разделе хранилища. Используйте только при разработке. Ядро не соберётся, пока не включён PIPCORE_DEBUG_CONSOLE_ACCEPT_RISK. Никогда не включайте консоль в релизной прошивке.