diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index 8d18aa05..1da212fb 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -32,6 +32,8 @@ static void OnDisconnect(struct etcpmon_app* app); static void OnConnectionSelect(struct etcpmon_app* app); static void OnTimer(struct etcpmon_app* app); static void UpdateUIState(struct etcpmon_app* app); +static void AddTooltip(HWND hTip, HWND hCtrl, const char* text); +static void CreateTooltips(struct etcpmon_app* app); /* Callback functions for client events */ static void on_connected(void* user_data); static void on_disconnected(void* user_data); @@ -139,7 +141,8 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) { } CreateControls(app); - + CreateTooltips(app); + for (int i = 0; i < GRAPH_METRICS_COUNT; i++) { app->hGraphPens[i] = CreatePen(PS_SOLID, 2, graph_colors[i]); } @@ -695,6 +698,205 @@ static void CreateControls(struct etcpmon_app* app) { } } +static void AddTooltip(HWND hTip, HWND hCtrl, const char* text) +{ + if (!hTip || !hCtrl || !text) return; + TOOLINFOA ti; + memset(&ti, 0, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + ti.hwnd = GetParent(hCtrl); + ti.uId = (UINT_PTR)hCtrl; + ti.lpszText = (LPSTR)text; + SendMessageA(hTip, TTM_ADDTOOL, 0, (LPARAM)&ti); +} +static void CreateTooltips(struct etcpmon_app* app) +{ + if (!app || !app->hWndMain) return; + + app->hToolTip = CreateWindowExA( + WS_EX_TOPMOST, TOOLTIPS_CLASSA, NULL, + WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + app->hWndMain, NULL, app->hInstance, NULL + ); + + if (!app->hToolTip) return; + + SendMessageA(app->hToolTip, TTM_SETMAXTIPWIDTH, 0, 400); + + /* ETCP Metrics */ + AddTooltip(app->hToolTip, app->hEditEtcpRttLast, + "etcp.rtt_last / ETCP Connection\r\nВремя приема-передачи последнего пакета (RTT), единицы 0.1 мс"); + AddTooltip(app->hToolTip, app->hEditEtcpRttAvg10, + "etcp.rtt_avg_10 / ETCP Connection\r\nСредний RTT за последние 10 пакетов, единицы 0.1 мс"); + AddTooltip(app->hToolTip, app->hEditEtcpRttAvg100, + "etcp.rtt_avg_100 / ETCP Connection\r\nСредний RTT за последние 100 пакетов, единицы 0.1 мс"); + AddTooltip(app->hToolTip, app->hEditEtcpJitter, + "etcp.jitter / ETCP Connection\r\nРазброс RTT (макс - мин), единицы 0.1 мс"); + AddTooltip(app->hToolTip, app->hEditEtcpBytesSent, + "etcp.bytes_sent_total / ETCP Connection\r\nОбщий объем отправленных байт за все время"); + AddTooltip(app->hToolTip, app->hEditEtcpRetrans, + "etcp.retrans_count / ETCP Connection\r\nКоличество ретрансляций (повторных отправок пакетов)"); + AddTooltip(app->hToolTip, app->hEditEtcpAcks, + "etcp.ack_count / ETCP Connection\r\nКоличество полученных ACK-пакетов (подтверждений)"); + AddTooltip(app->hToolTip, app->hEditEtcpInflight, + "etcp.unacked_bytes / ETCP Connection\r\nБайт в полете: отправлено, но еще не подтверждено"); + AddTooltip(app->hToolTip, app->hEditEtcpLinks, + "etcp.links_count / ETCP Connection\r\nКоличество активных линков (физических соединений)"); + + /* TUN Metrics */ + AddTooltip(app->hToolTip, app->hEditTunReadBytes, + "tun.bytes_read / TUN Interface\r\nВсего байт прочитано из TUN-интерфейса"); + AddTooltip(app->hToolTip, app->hEditTunWriteBytes, + "tun.bytes_written / TUN Interface\r\nВсего байт записано в TUN-интерфейс"); + AddTooltip(app->hToolTip, app->hEditTunReadPkts, + "tun.packets_read / TUN Interface\r\nВсего пакетов прочитано из TUN-интерфейса"); + AddTooltip(app->hToolTip, app->hEditTunWritePkts, + "tun.packets_written / TUN Interface\r\nВсего пакетов записано в TUN-интерфейс"); + AddTooltip(app->hToolTip, app->hEditTunReadErrs, + "tun.read_errors / TUN Interface\r\nОшибок чтения из TUN-интерфейса"); + AddTooltip(app->hToolTip, app->hEditTunWriteErrs, + "tun.write_errors / TUN Interface\r\nОшибок записи в TUN-интерфейс"); + + /* Routing Metrics */ + AddTooltip(app->hToolTip, app->hEditRtRouted, + "tun.routed_packets / Routing\r\nКоличество успешно маршрутизированных пакетов"); + AddTooltip(app->hToolTip, app->hEditRtDropped, + "tun.dropped_packets / Routing\r\nКоличество отброшенных (не маршрутизированных) пакетов"); + AddTooltip(app->hToolTip, app->hEditRtTunInQPkts, + "tun.tun_in_q_packets / Routing\r\nПакетов во входной очереди TUN"); + AddTooltip(app->hToolTip, app->hEditRtTunInQBytes, + "tun.tun_in_q_bytes / Routing\r\nБайт во входной очереди TUN"); + AddTooltip(app->hToolTip, app->hEditRtTunOutQPkts, + "tun.tun_out_q_packets / Routing\r\nПакетов в выходной очереди TUN"); + AddTooltip(app->hToolTip, app->hEditRtTunOutQBytes, + "tun.tun_out_q_bytes / Routing\r\nБайт в выходной очереди TUN"); + AddTooltip(app->hToolTip, app->hEditRtCount, + "tun.rt_count / Routing\r\nОбщее количество записей в таблице маршрутизации"); + AddTooltip(app->hToolTip, app->hEditRtLocal, + "tun.rt_local / Routing\r\nЛокальных (статических) маршрутов"); + AddTooltip(app->hToolTip, app->hEditRtLearned, + "tun.rt_learned / Routing\r\nИзученных (динамических/BGP) маршрутов"); + AddTooltip(app->hToolTip, app->hEditRtBgpSenders, + "tun.rt_bgp_senders / Routing BGP\r\nКоличество BGP-отправителей в списке"); + AddTooltip(app->hToolTip, app->hEditRtBgpNodes, + "tun.rt_bgp_nodes / Routing BGP\r\nКоличество BGP-узлов"); + + /* Queue Metrics */ + AddTooltip(app->hToolTip, app->hEditQInQBytes, + "etcp.input_queue_bytes / ETCP Queue\r\nБайт во входной очереди (принято от TUN, ожидает отправки)"); + AddTooltip(app->hToolTip, app->hEditQInQPkts, + "etcp.input_queue_packets / ETCP Queue\r\nПакетов во входной очереди"); + AddTooltip(app->hToolTip, app->hEditQInSendBytes, + "etcp.input_send_q_bytes / ETCP Queue\r\nБайт в очереди отправки (готовы к отправке по линкам)"); + AddTooltip(app->hToolTip, app->hEditQInSendPkts, + "etcp.input_send_q_packets / ETCP Queue\r\nПакетов в очереди отправки"); + AddTooltip(app->hToolTip, app->hEditQWaitAckBytes, + "etcp.input_wait_ack_bytes / ETCP Queue\r\nБайт в очереди ожидания ACK (отправлено, ждем подтверждения)"); + AddTooltip(app->hToolTip, app->hEditQWaitAckPkts, + "etcp.input_wait_ack_packets / ETCP Queue\r\nПакетов в очереди ожидания ACK"); + AddTooltip(app->hToolTip, app->hEditQWaitAckOptInfl, + "etcp.optimal_inflight / ETCP Congestion Control\r\nЦелевое (оптимальное) окно перегрузки в байтах"); + AddTooltip(app->hToolTip, app->hEditQAckBytes, + "etcp.ack_q_bytes / ETCP Queue\r\nБайт в очереди ACK (подтверждения, готовы к отправке)"); + AddTooltip(app->hToolTip, app->hEditQAckPkts, + "etcp.ack_q_packets / ETCP Queue\r\nПакетов в очереди ACK"); + AddTooltip(app->hToolTip, app->hEditQRecvBytes, + "etcp.recv_q_bytes / ETCP Queue\r\nБайт в очереди приема (принято, ожидает доставки в TUN)"); + AddTooltip(app->hToolTip, app->hEditQRecvPkts, + "etcp.recv_q_packets / ETCP Queue\r\nПакетов в очереди приема"); + AddTooltip(app->hToolTip, app->hEditQOutBytes, + "etcp.output_queue_bytes / ETCP Queue\r\nБайт в выходной очереди (готовы к записи в TUN)"); + AddTooltip(app->hToolTip, app->hEditQOutPkts, + "etcp.output_queue_packets / ETCP Queue\r\nПакетов в выходной очереди"); + + /* Error Counters */ + AddTooltip(app->hToolTip, app->hEditErrReinit, + "etcp.reinit_count / ETCP Errors\r\nСчетчик реинициализаций соединения"); + AddTooltip(app->hToolTip, app->hEditErrReset, + "etcp.reset_count / ETCP Errors\r\nСчетчик сбросов (reset) соединения"); + AddTooltip(app->hToolTip, app->hEditErrPktFmt, + "etcp.pkt_format_errors / ETCP Errors\r\nОшибок формата пакета (невалидные заголовки)"); + + /* Timer Flags */ + AddTooltip(app->hToolTip, app->hEditTimerRetrans, + "etcp.retrans_timer_active / ETCP Timers\r\nАктивен ли таймер ретрансляции (ON/OFF)"); + AddTooltip(app->hToolTip, app->hEditTimerAckResp, + "etcp.ack_resp_timer_active / ETCP Timers\r\nАктивен ли таймер отложенных ACK"); + + /* WaitAck queue state */ + AddTooltip(app->hToolTip, app->hEditWaitAckSuspend, + "etcp.wait_ack_cb_suspended / ETCP WaitAck\r\nCallback приостановлен (ожидание ACK блокирует передачу)"); + AddTooltip(app->hToolTip, app->hEditWaitAckCb, + "etcp.wait_ack_cb_set / ETCP WaitAck\r\nCallback установлен (есть обработчик ожидания ACK)"); + AddTooltip(app->hToolTip, app->hEditWaitAckTimer, + "etcp.wait_ack_resume_timeout / ETCP WaitAck\r\nТаймер возобновления передачи активен"); + + /* ID Metrics */ + AddTooltip(app->hToolTip, app->hEditIdNextTx, + "etcp.next_tx_id / ETCP Sequence\r\nСледующий ID для передачи (Sequence Number TX)"); + AddTooltip(app->hToolTip, app->hEditIdLastRx, + "etcp.last_rx_id / ETCP Sequence\r\nПоследний принятый ID (Sequence Number RX)"); + AddTooltip(app->hToolTip, app->hEditIdLastDel, + "etcp.last_delivered_id / ETCP Sequence\r\nПоследний доставленный ID (в TUN)"); + AddTooltip(app->hToolTip, app->hEditIdRxAckTill, + "etcp.rx_ack_till / ETCP Sequence\r\nДо какого ID подтвержден прием (ACK от получателя)"); + + /* Normalizer */ + AddTooltip(app->hToolTip, app->hEditNormInPkts, + "etcp.norm_input_pkts / Packet Normalizer\r\nПакетов на входе нормализатора (текущие)"); + AddTooltip(app->hToolTip, app->hEditNormInBytes, + "etcp.norm_input_bytes / Packet Normalizer\r\nБайт на входе нормализатора (текущие)"); + AddTooltip(app->hToolTip, app->hEditNormOutPkts, + "etcp.norm_output_pkts / Packet Normalizer\r\nПакетов на выходе нормализатора (текущие)"); + AddTooltip(app->hToolTip, app->hEditNormOutBytes, + "etcp.norm_output_bytes / Packet Normalizer\r\nБайт на выходе нормализатора (текущие)"); + AddTooltip(app->hToolTip, app->hEditNormAllocErr, + "etcp.norm_alloc_errors / Packet Normalizer\r\nОшибок выделения памяти в нормализаторе"); + AddTooltip(app->hToolTip, app->hEditNormLogicErr, + "etcp.norm_logic_errors / Packet Normalizer\r\nЛогических ошибок в нормализаторе"); + AddTooltip(app->hToolTip, app->hEditNormFragSize, + "etcp.norm_frag_size / Packet Normalizer\r\nРазмер текущего фрагмента"); + AddTooltip(app->hToolTip, app->hEditNormDataPtr, + "etcp.norm_data_ptr / Packet Normalizer\r\nУказатель на данные в буфере"); + AddTooltip(app->hToolTip, app->hEditNormDataSize, + "etcp.norm_data_size / Packet Normalizer\r\nРазмер данных в буфере"); + AddTooltip(app->hToolTip, app->hEditNormInTotPkts, + "etcp.norm_in_total_pkts / Packet Normalizer\r\nВсего пакетов обработано на входе (накопительно)"); + AddTooltip(app->hToolTip, app->hEditNormInTotBytes, + "etcp.norm_in_total_bytes / Packet Normalizer\r\nВсего байт обработано на входе (накопительно)"); + AddTooltip(app->hToolTip, app->hEditNormOutTotPkts, + "etcp.norm_out_total_pkts / Packet Normalizer\r\nВсего пакетов обработано на выходе (накопительно)"); + AddTooltip(app->hToolTip, app->hEditNormOutTotBytes, + "etcp.norm_out_total_bytes / Packet Normalizer\r\nВсего байт обработано на выходе (накопительно)"); + + /* ACK Debug counters */ + AddTooltip(app->hToolTip, app->hEditAckHitInf, + "etcp.cnt_ack_hit_inf / ETCP ACK Debug\r\nACK попал в inflight-окно (ожидаемый ACK)"); + AddTooltip(app->hToolTip, app->hEditAckHitSndq, + "etcp.cnt_ack_hit_sndq / ETCP ACK Debug\r\nACK попал в send-очередь"); + AddTooltip(app->hToolTip, app->hEditAckMiss, + "etcp.cnt_ack_miss / ETCP ACK Debug\r\nACK не найден (неожиданный ACK)"); + AddTooltip(app->hToolTip, app->hEditCntLinkWait, + "etcp.cnt_link_wait / ETCP ACK Debug\r\nСчетчик ожидания линка"); + AddTooltip(app->hToolTip, app->hEditTxState, + "etcp.tx_state / ETCP ACK Debug\r\nСостояние передачи (битовая маска)"); + + /* Debug counters */ + for (int i = 0; i < 8; i++) { + char buf[256]; + snprintf(buf, sizeof(buf), + "etcp.debug[%d] / ETCP Debug\r\nОтладочное поле (зависит от версии)", i); + AddTooltip(app->hToolTip, app->hEditDebug[i], buf); + } + + /* System resources */ + AddTooltip(app->hToolTip, app->hEditActiveTimeouts, + "etcp.active_timeouts / System Resources\r\nАктивных таймаутов в системе (async)"); + AddTooltip(app->hToolTip, app->hEditBusyMemory, + "etcp.busy_memory_blocks / System Resources\r\nЗанятых блоков памяти (memory pool)"); +} static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { struct etcpmon_app* app = NULL; @@ -918,10 +1120,15 @@ int etcpmon_gui_run(struct etcpmon_app* app) { } void etcpmon_gui_cleanup(struct etcpmon_app* app) { if (!app) return; - + + if (app->hToolTip) { + DestroyWindow(app->hToolTip); + app->hToolTip = NULL; + } + for (int i = 0; i < GRAPH_METRICS_COUNT; i++) { if (app->hGraphPens[i]) DeleteObject(app->hGraphPens[i]); - } + } if (app->client) { etcpmon_client_cleanup(app->client); free(app->client); diff --git a/tools/etcpmon/etcpmon_gui.h b/tools/etcpmon/etcpmon_gui.h index 0eb3d679..3f7dc6b7 100644 --- a/tools/etcpmon/etcpmon_gui.h +++ b/tools/etcpmon/etcpmon_gui.h @@ -184,6 +184,7 @@ struct etcpmon_app { HINSTANCE hInstance; HWND hWndMain; HWND hWndStatus; + HWND hToolTip; HPEN hGraphPens[GRAPH_METRICS_COUNT];