lwIP: вывод отладочных сообщений Печать
Добавил(а) microsin   

Есть несколько разных методов, обычно используемых для отладки lwIP, стиля printf и с использованием внешнего отладчика.

[Внешний отладчик]

Возможно самый очевидный метод поиска проблем, связанных с сетевым стеком - с помощью отладчика / точки останова начать пошаговую отладку выполняемого кода. Однако поскольку lwIP обычно используется для встраиваемых платформ (микроконтроллеров) с ограниченным объемом ресурсов, то в случае включенной оптимизации запустить отладку по исходному коду может быть затруднительно. Кроме того, должен быть специально реализован способ соединения отладчика с отлаживаемой системой. Часто это решается через сетевое соединение, когда работает сетевой стек и сервер GDB, или какой-то другой демон, позволяющий отладчику общаться с целевой системой.

Поскольку отлаживается сам сетевой стек, то очевидно, что отладчик не сможет работать через то же самое сетевое соединение, и нужен какой-то другой способ обмена данными. Для этого есть несколько решений. Для самой лучшей производительности хорошей идеей будет запустить другой стабильный экземпляр lwIP, или другой сетевой стек, рядом с отлаживаемым стеком lwIP. Это может быть довольно замысловатым способом отладки, в зависимости от определенной архитектуры целевой системы, однако вполне выполнимо и полезно для устранения проблем, возникающих с начальным портированием. Когда стек запущен и заработал, отладчик становится гораздо менее полезен, поскольку большинство проблем создают уже внешние компоненты (чаще всего это компонент, блокирующий выполнение на слишком большое время). Здесь уже поможет старый, традиционный метод вывода отладочных printf-сообщений.

[Встроенная отладка в стиле printf]

Стек lwIP разработан с хорошей поддержкой вывода различных отладочных сообщений, с богатыми опциями настройки этого вывода. Опции отладки разрешаются во время сборки. Это различные xxx_DEBUG опции, которые перечислены в файле opt.h. Измените настройки по умолчанию (в которых отладка выключена) файла opts.h путем добавления в файл lwipopts.h:

#define LWIP_DEBUG 1
 
#define xxx_DEBUG LWIP_DBG_ON
...

Здесь xxx обозначает отлаживаемую подсистему стека - например, IP_DEBUG, DHCP_DEBUG и т. д.

Пример вывода отлдочных сообщений с включенной опцией NETIF_DEBUG: 

netif: netmask of interface netif: GW address of interface
   netif_set_ipaddr: netif address being changed
netif: IP address of interface netif: added interface st
   IP addr 192.168.0.208 netmask 255.255.255.0 gw 192.168.0.1
netif: setting default interface st 

В следующей таблице перечислены все отлаживаемые подсистемы стека, краткая информация по этим опциям приведена также в файле opt.h.

Опция Какие отладочные сообщения разрешает
ETHARP_DEBUG Протокол ARP (модуль etharp.c).
NETIF_DEBUG Работа с сетевым интерфейсом (модуль netif.c).
PBUF_DEBUG Буферы стека (модуль pbuf.c).
API_LIB_DEBUG Вызовы библиотечных функций (модуль api_lib.c).
API_MSG_DEBUG API сообщений (модуль api_msg.c).
SOCKETS_DEBUG Работа с сетевыми сокетами (модуль sockets.c).
ICMP_DEBUG Протокол ICMP, поддержка ping (модуль icmp.c).
IGMP_DEBUG Протокол IGMP (модуль igmp.c).
INET_DEBUG Функции, общие для всех модулей TCP/IPv4, такие как функции обеспечения нужного порядка следования байт (модуль inet.c).
IP_DEBUG Протокол IP.
IP_REASS_DEBUG Разрешение отладки ip_frag.c для фрагментов и пересборки пакетов.
RAW_DEBUG Низкоуровневая реализация PCB (Protocol Control Blocks, модуль raw.c).
MEM_DEBUG Облегченная реализация malloc (модуль mem.c).
MEMP_DEBUG Менеджер динамических пулов памяти (модуль memp.c).
SYS_DEBUG Слой абстракции операционной системы (модуль sys.c).
TIMERS_DEBUG Таймеры стека (модуль timers.c).
TCP_DEBUG Протокол TCP.
TCP_INPUT_DEBUG Протокол TCP, обработка входящих пакетов (модуль tcp_in.c).
TCP_FR_DEBUG Протокол TCP, быстрая повторная передача пакетов (модуль tcp_in.c).
TCP_RTO_DEBUG Протокол TCP, повторная передача пакетов.
TCP_CWND_DEBUG Управление стратегией окна перегрузки TCP (congestion window).
TCP_WND_DEBUG Протокол TCP, обновление окна (модуль tcp_in.c).
TCP_OUTPUT_DEBUG Протокол TCP, функции вывода (модуль tcp_out.c).
TCP_RST_DEBUG Протокол TCP, сообщения RST.
TCP_QLEN_DEBUG Протокол TCP, длины очередей.
UDP_DEBUG Протокол UDP.
TCPIP_DEBUG Протокол TCP/IP (модуль tcpip.c).
SLIP_DEBUG Интерфейс SLIP (модуль slipif.c).
DHCP_DEBUG Протокол DHCP (модуль dhcp.c).
AUTOIP_DEBUG Автоматическое назначение IP-адреса при недоступности DHCP (модуль autoip.c).
DNS_DEBUG Протокол преобразования доменных имен в адреса IP.
IP6_DEBUG Протокол IPv6.

Чтобы разрешить отладочный код в LWIP, нужно определить флаг (макрос) LWIP_DEBUG в момент компиляции кода LWIP. Флаги компиляции обычно передаются с опцией "-D" как один из аргументов командной строки компилятора.

[Keil]

Например, при компиляции средствами разработки Keil можно добавить целевые опции компилятора на закладке "define C/C++" целевых опций проекта.

Чтобы разрешить определенные отладочные сообщения LWIP, просто установите соответствующее значение для макросов *_DEBUG в " LWIP_DBG_ON". Выполните копию необходимых для отладки опций, и вставьте их в файл lwipopts.h. Например:

// Строки, добавленные в lwipopts.h:
#define TCP_DEBUG                       LWIP_DBG_ON
#define ETHARP_DEBUG                    LWIP_DBG_ON
#define PBUF_DEBUG                      LWIP_DBG_ON

[IAR]

Зайдите в свойства проекта, раздел C/C++ Compiler, и на закладке Preprocessor в поле ввода "Defined symbols: (one per line)" добавьте строку LWIP_DEBUG:

IAR add LWIP DEBUG

После этого в файл lwipopts.h включите опции xxx_DEBUG, необходимые для отладки.

[Конфигурирование типов отладочных сообщений]

Стек lwIP поддерживает разные типы отладочных сообщений. Символическая константа LWIP_DBG_TYPES_ON (находится в файле lwipopts.h) может быть изменена, чтобы разрешить или запретить некоторые виды сообщений. Следующие 4 значения могут быть объединены операцией ИЛИ, и назначены для константы LWIP_DBG_TYPES_ON:

/* Флаг для LWIP_DEBUGF, показывающий сообщение трассировки
 * (для выполняющихся операций программы): */
#define LWIP_DBG_TRACE         0x40U
 
/* Флаг для LWIP_DEBUGF, показывающий состояние отладочного сообщения
 * (для отслеживания состояний модуля): */
#define LWIP_DBG_STATE         0x20U
 
/* Флаг для LWIP_DEBUGF, показывающий только что добавленный код,
 * это пока еще тщательно не протестировано: */
#define LWIP_DBG_FRESH         0x10U
 
/* Флаг для LWIP_DEBUGF, вызывающий остановку выполнения после
 * вывода на печать этого отладочного сообщения: */
#define LWIP_DBG_HALT          0x08U

[Реализация LWIP_DEBUGF]

Код, который выводит отладочные сообщения, реализован макросом LWIP_DEBUGF. По умолчанию он использует стандартный вывод printf:

#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
 
#define LWIP_DEBUGF(debug, message) do { \
                               if ( \
                                   ((debug) & LWIP_DBG_ON) && \
                                   ((debug) & LWIP_DBG_TYPES_ON) && \
                                   ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
                                 LWIP_PLATFORM_DIAG(message); \
                                 if ((debug) & LWIP_DBG_HALT) { \
                                   while(1); \
                                 } \
                               } \
                             } while(0)

Вы можете переопределить макрос LWIP_PLATFORM_DIAG, заменив вызов printf на любую функцию, поддерживающую аргументы __VA_ARGS__.

Ниже приведен кусок кода, где реализован вывод отладочных сообщений в USART1 микроконтроллера STM32F429 (плата 32F429IDISCAVERY). Функцию uprintf можно использовать вместо printf в макросе LWIP_PLATFORM_DIAG(x):

//#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#define LWIP_PLATFORM_DIAG(x) do {uprintf x;} while(0)

Важный момент: аргумент x в вызове функции uprintf макроса LWIP_PLATFORM_DIAG не надо заключать в скобки, иначе компилятор будет выдавать ошибку "... argument of type "u16_t" is incompatible with parameter of type 'char const *'".

#include < stdio.h>
#include < stdarg.h>
#include < string.h>
#include "bufcheck.h"
#include "UsartIO.h"
#include "USARTconsole.h"
#include "settings.h"
#include "pins.h"
#include "errors.h"
#include "cmsis_os.h"
 
char usartbuftx[USART_TXBUFSIZE];
static char strbuf[UPRINTF_BUF_SIZE];
static USART_HandleTypeDef husart1;
uint16_t usartinTX = 0;
uint16_t usartoutTX = 0;
SemaphoreHandle_t usarttxSemaphore;
 
void MX_USART1_Init(void)
{
   husart1.Instance = USART1;
   husart1.Init.BaudRate = 115200;
   husart1.Init.WordLength = USART_WORDLENGTH_8B;
   husart1.Init.StopBits = USART_STOPBITS_1;
   husart1.Init.Parity = USART_PARITY_NONE;
   husart1.Init.Mode = USART_MODE_TX_RX;
   if (HAL_USART_Init(&husart1) != HAL_OK)
   {
      HALerrorHandler();
   }
}
 
void StartUSART1txtask(void const * argument)
{
   for(;;)
   {
      //////////////////////////////////////////////
      // Обработка передачи
      while(usartinTX != usartoutTX)
      {
         while (0 == __HAL_USART_GET_FLAG(&husart1, USART_FLAG_TXE));
         husart1.Instance->DR = usartbuftx[usartoutTX];
         usartoutTX++;
         usartoutTX &= USART_TXBUFMASK;
      }
      xSemaphoreTake(usarttxSemaphore, portMAX_DELAY);
   }
}
 
void usartputc(char c)
{
   usartbuftx[usartinTX++] = c;
   usartinTX &= USART_TXBUFMASK;
}
 
//Замена \n (LF 0A) на \r\n (CRLF 0D0A),
// \r во входной строке пропускается.
static void usartputs(const char *c)
{
   while (*c)
   {
      if (*c == '\n')
         usartputc('\r'); // 0x0D = insert carriage return
      usartputc(*c);
      c++;
   }
}
 
int16_t uprintf (const char *fmt, ...)
{
   int16_t outsize;
   va_list ap;
 
   // Вернет ошибку, если формат является указателем NULL:
   if (!fmt) { return -1; }
   // Вернет ошибку, если строка превышает размер буфера, с учетом
   // необходимых дополнительных 2 символов: CR и нулевой терминатор ASCIIZ:
   if (UPRINTF_BUF_SIZE-2 < strlen(fmt)) { return -1; }
 
   va_start (ap,fmt);
   outsize = vsprintf(strbuf,fmt,ap);
   strbuf[outsize+1] = 0;
   va_end (ap);
 
   usartputs(strbuf);
   xSemaphoreGive(usarttxSemaphore);
   return outsize;
}

Весь проект целиком можно скачать по ссылке [3].

[Ссылки]

1. Enabling debug output in LWIP site:community.nxp.com.
2. Debugging lwIP site:lwip.fandom.com.
3. 191113TestUSART1-tx-USART1-with-semaphore.zip - проект IAR 8.30, где демонстрируется отладочный вывод в USART1 под управлением FreeRTOS.