From 21dfb337585721c1501f563b730104b3754ed288 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Thu, 7 Nov 2024 12:20:11 +0200 Subject: [PATCH 1/6] Make Remote provider collect wire statistics. New info items to query wire stats counters. --- src/include/firebird/impl/inf_pub.h | 11 ++++ src/include/gen/Firebird.pas | 9 +++ src/remote/client/interface.cpp | 54 +++++++++++++++++ src/remote/inet.cpp | 7 +-- src/remote/os/win32/xnet.cpp | 8 +-- src/remote/protocol.cpp | 3 + src/remote/remote.cpp | 17 +++++- src/remote/remote.h | 94 ++++++++++++++++++++++++++++- 8 files changed, 190 insertions(+), 13 deletions(-) diff --git a/src/include/firebird/impl/inf_pub.h b/src/include/firebird/impl/inf_pub.h index 06afa3c7397..9f77591fdcd 100644 --- a/src/include/firebird/impl/inf_pub.h +++ b/src/include/firebird/impl/inf_pub.h @@ -178,6 +178,17 @@ enum db_info_types fb_info_parallel_workers = 149, + // Wire stats items, implemented by Remote provider only + fb_info_wire_out_packets = 150, + fb_info_wire_in_packets = 151, + fb_info_wire_out_bytes = 152, + fb_info_wire_in_bytes = 153, + fb_info_wire_snd_packets = 154, + fb_info_wire_rcv_packets = 155, + fb_info_wire_snd_bytes = 156, + fb_info_wire_rcv_bytes = 157, + fb_info_wire_roundtrips = 158, + isc_info_db_last_value /* Leave this LAST! */ }; diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 53b3c1475a2..be611bed580 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -4496,6 +4496,15 @@ IProfilerStatsImpl = class(IProfilerStats) fb_info_username = byte(147); fb_info_sqlrole = byte(148); fb_info_parallel_workers = byte(149); + fb_info_wire_out_packets = byte(150); + fb_info_wire_in_packets = byte(151); + fb_info_wire_out_bytes = byte(152); + fb_info_wire_in_bytes = byte(153); + fb_info_wire_snd_packets = byte(154); + fb_info_wire_rcv_packets = byte(155); + fb_info_wire_snd_bytes = byte(156); + fb_info_wire_rcv_bytes = byte(157); + fb_info_wire_roundtrips = byte(158); fb_info_crypt_encrypted = $01; fb_info_crypt_process = $02; fb_feature_multi_statements = byte(1); diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index edd9b410b6c..081b6fbfa39 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -936,6 +936,8 @@ class Attachment final : public RefCntIfacerdb_port; + + UCHAR* ptr = buffer; + const UCHAR* const end = buffer + buffer_length; + + for (auto item = items; item < items + item_length; item++) + { + switch (*item) + { + case fb_info_wire_snd_packets: + case fb_info_wire_rcv_packets: + case fb_info_wire_out_packets: + case fb_info_wire_in_packets: + case fb_info_wire_snd_bytes: + case fb_info_wire_rcv_bytes: + case fb_info_wire_out_bytes: + case fb_info_wire_in_bytes: + case fb_info_wire_roundtrips: + { + const FB_UINT64 value = port->getStatItem(*item); + + if (value <= MAX_SLONG) + ptr = fb_utils::putInfoItemInt(*item, (SLONG) value, ptr, end); + else + ptr = fb_utils::putInfoItemInt(*item, value, ptr, end); + + fb_assert(ptr); + break; + } + + case isc_info_end: + if (ptr < end) + *ptr++ = *item; + + return true; + + default: + // Let someone else handle unknown item + return false; + } + } + + return true; +} + + void Attachment::getInfo(CheckStatusWrapper* status, unsigned int item_length, const unsigned char* items, unsigned int buffer_length, unsigned char* buffer) @@ -1994,6 +2045,9 @@ void Attachment::getInfo(CheckStatusWrapper* status, RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); + if (getWireStatsInfo(item_length, items, buffer_length, buffer)) + return; + UCHAR* temp_buffer = temp.getBuffer(buffer_length); info(status, rdb, op_info_database, rdb->rdb_id, 0, diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index 351ed9d3274..a7feac25a1a 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -3129,8 +3129,7 @@ static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, } // end scope #endif - port->port_rcv_packets++; - port->port_rcv_bytes += n; + port->bumpPhysStats(rem_port::RECEIVE, n); *length = n; @@ -3305,9 +3304,7 @@ static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_leng } // end scope #endif - port->port_snd_packets++; - port->port_snd_bytes += buffer_length; - + port->bumpPhysStats(rem_port::SEND, buffer_length); return true; } diff --git a/src/remote/os/win32/xnet.cpp b/src/remote/os/win32/xnet.cpp index 09cae93858c..37bf6b6736a 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -1991,8 +1991,8 @@ static bool_t xnet_read(RemoteXdr* xdrs) { // Client has written some data for us (server) to read - port->port_rcv_packets++; - port->port_rcv_bytes += xch->xch_length; + port->bumpPhysStats(rem_port::RECEIVE, xch->xch_length); + port->bumpLogBytes(rem_port::RECEIVE, xch->xch_length); // XNET not calls REMOTE_inflate xdrs->x_handy = xch->xch_length; xdrs->x_private = xdrs->x_base; @@ -2048,8 +2048,8 @@ static bool_t xnet_write(RemoteXdr* xdrs) xch->xch_length = xdrs->x_private - xdrs->x_base; if (SetEvent(xcc->xcc_event_send_channel_filled)) { - port->port_snd_packets++; - port->port_snd_bytes += xch->xch_length; + port->bumpPhysStats(rem_port::SEND, xch->xch_length); + port->bumpLogBytes(rem_port::SEND, xch->xch_length); // XNET not calls REMOTE_deflate xdrs->x_private = xdrs->x_base; xdrs->x_handy = xch->xch_size; diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 0bf5a4d1c0c..d072766371d 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -303,6 +303,9 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) const auto port = xdrs->x_public; + if (xdrs->x_op != XDR_FREE) + port->bumpLogPackets(xdrs->x_op == XDR_ENCODE ? rem_port::SEND : rem_port::RECEIVE); + switch (p->p_operation) { case op_reject: diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index 9e92b01642d..2a8da65fef2 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -1493,7 +1493,12 @@ bool REMOTE_inflate(rem_port* port, PacketReceive* packet_receive, UCHAR* buffer { #ifdef WIRE_COMPRESS_SUPPORT if (!port->port_compressed) - return packet_receive(port, buffer, buffer_length, length); + { + const bool ret = packet_receive(port, buffer, buffer_length, length); + if (ret) + port->bumpLogBytes(rem_port::RECEIVE, *length); + return ret; + } z_stream& strm = port->port_recv_stream; strm.avail_out = buffer_length; @@ -1565,16 +1570,22 @@ bool REMOTE_inflate(rem_port* port, PacketReceive* packet_receive, UCHAR* buffer fprintf(stderr, "ZLib buffer %s\n", port->port_z_data ? "has data" : "is empty"); #endif + port->bumpLogBytes(rem_port::RECEIVE, *length); return true; #else - return packet_receive(port, buffer, buffer_length, length); + const bool ret = packet_receive(port, buffer, buffer_length, length); + if (ret) + port->bumpLogBytes(rem_port::RECEIVE, *length); + return ret; #endif } bool REMOTE_deflate(RemoteXdr* xdrs, ProtoWrite* proto_write, PacketSend* packet_send, bool flush) { -#ifdef WIRE_COMPRESS_SUPPORT rem_port* port = xdrs->x_public; + port->bumpLogBytes(rem_port::SEND, xdrs->x_private - xdrs->x_base); + +#ifdef WIRE_COMPRESS_SUPPORT if (!(port->port_compressed && (port->port_flags & PORT_compressed))) return proto_write(xdrs); diff --git a/src/remote/remote.h b/src/remote/remote.h index 2f0fc4e15b4..cf36de9c792 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -1144,10 +1144,100 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted UCharArrayAutoPtr port_buffer; + + enum io_direction_t { + NONE, + SEND, + RECEIVE + }; + +private: + // packets over physical connection FB_UINT64 port_snd_packets; FB_UINT64 port_rcv_packets; + // protocol packets + FB_UINT64 port_out_packets; + FB_UINT64 port_in_packets; + // bytes over physical connection FB_UINT64 port_snd_bytes; FB_UINT64 port_rcv_bytes; + // bytes before/after compression + FB_UINT64 port_out_bytes; + FB_UINT64 port_in_bytes; + FB_UINT64 port_roundtrips; // number of changes of IO direction from SEND to RECEIVE + io_direction_t port_io_direction; // last direction of IO + +public: + void bumpPhysStats(io_direction_t direction, ULONG count) + { + fb_assert(direction != NONE); + + if (direction == SEND) + { + port_snd_packets++; + port_snd_bytes += count; + } + else + { + port_rcv_packets++; + port_rcv_bytes += count; + } + + if (direction != port_io_direction) + { + if (port_io_direction != NONE && direction == RECEIVE) + port_roundtrips++; + port_io_direction = direction; + } + } + + void bumpLogBytes(io_direction_t direction, ULONG count) + { + fb_assert(direction != NONE); + + if (direction == SEND) + port_out_bytes += count; + else + port_in_bytes += count; + } + + void bumpLogPackets(io_direction_t direction) + { + fb_assert(direction != NONE); + + if (direction == SEND) + port_out_packets++; + else + port_in_packets++; + } + + FB_UINT64 getStatItem(UCHAR infoItem) const + { + switch (infoItem) + { + case fb_info_wire_snd_packets: + return port_snd_packets; + case fb_info_wire_rcv_packets: + return port_rcv_packets; + case fb_info_wire_out_packets: + return port_out_packets; + case fb_info_wire_in_packets: + return port_in_packets; + case fb_info_wire_snd_bytes: + return port_snd_bytes; + case fb_info_wire_rcv_bytes: + return port_rcv_bytes; + case fb_info_wire_out_bytes: + return port_out_bytes; + case fb_info_wire_in_bytes: + return port_in_bytes; + case fb_info_wire_roundtrips: + return port_roundtrips; + default: + return 0; + } + } + #ifdef WIRE_COMPRESS_SUPPORT z_stream port_send_stream, port_recv_stream; @@ -1187,7 +1277,9 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted port_known_server_keys(getPool()), port_crypt_plugin(NULL), port_client_crypt_callback(NULL), port_server_crypt_callback(NULL), port_crypt_name(getPool()), port_replicator(NULL), port_buffer(FB_NEW_POOL(getPool()) UCHAR[rpt]), - port_snd_packets(0), port_rcv_packets(0), port_snd_bytes(0), port_rcv_bytes(0) + port_snd_packets(0), port_rcv_packets(0), port_out_packets(0), port_in_packets(0), + port_snd_bytes(0), port_rcv_bytes(0), port_out_bytes(0), port_in_bytes(0), + port_roundtrips(0), port_io_direction(NONE) { addRef(); memset(&port_linger, 0, sizeof port_linger); From 88febf435a693427b4c6bf25fc48235b54ed10ef Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Thu, 7 Nov 2024 12:20:51 +0200 Subject: [PATCH 2/6] New ISQL commands to show wire statistics. --- doc/README.isql_enhancements.txt | 119 ++++++++++++++++++++++++ src/isql/isql.epp | 151 ++++++++++++++++++++++++++++++- src/isql/isql.h | 22 +++++ src/isql/show.epp | 27 +++++- 4 files changed, 315 insertions(+), 4 deletions(-) diff --git a/doc/README.isql_enhancements.txt b/doc/README.isql_enhancements.txt index 9350cf2e574..1476e51f294 100644 --- a/doc/README.isql_enhancements.txt +++ b/doc/README.isql_enhancements.txt @@ -324,3 +324,122 @@ RDB$RELATIONS | 59| | | | -- turn per-table stats off, using shortened name SQL> SET PER_TAB OFF; + + + +12) SET WIRE_STATS option. + +Author: Vladyslav Khorsun + + When set to ON shows wire (network) statistics after query execution. +It is set to OFF by default. The name WIRE_STATS could be shortened up to WIRE. + +The statistics counters shown in two groups: 'logical' and 'physical': + - logical counters show numbers of packets in terms of Firebird wire protocol + and number of bytes send before compression and received after decompression; + - physical counters show number of physical packets and bytes send and + received over the wire, number of bytes could be affected by wire compression, + if present. Also, number of network roundtrips is shown: it is number of + changes of IO direction from 'send' to 'receive'. + + Note, wire statistics is gathered by Remote provider only, i.e. it is always +zero for embedded connections. Also, it is collected by client and IO direction +(send, receive) is shown from client point of view. + +Examples: + +1. INET protocol with wire compression. +Set WireCompression = true in firebird.conf + +>isql inet://employee + +SQL> SET; +Print statistics: OFF +Print per-table stats: OFF +Print wire stats: OFF +... + +SQL> SET WIRE; +SQL> +SQL> SELECT COUNT(*) FROM RDB$RELATIONS; + + COUNT +===================== + 67 + +Wire logical statistics: + send packets = 6 + recv packets = 5 + send bytes = 184 + recv bytes = 224 +Wire physical statistics: + send packets = 3 + recv packets = 2 + send bytes = 123 + recv bytes = 88 + roundtrips = 2 + + Note difference due to wire compression in send/recv bytes for logical and +physical stats. + + +2. XNET protocol (wire compression is not used). + +>isql xnet://employee + +SQL> SET WIRE; +SQL> +SQL> SELECT COUNT(*) FROM RDB$RELATIONS; + + COUNT +===================== + 67 + +Wire logical statistics: + send packets = 5 + recv packets = 6 + send bytes = 176 + recv bytes = 256 +Wire physical statistics: + send packets = 5 + recv packets = 5 + send bytes = 176 + recv bytes = 256 + roundtrips = 5 + + Note, send/recv bytes for logical and physical stats are equal. + + +3. Embedded connection (wire statistics is absent). + +SQL> SET WIRE; +SQL> +SQL> select count(*) from rdb$relations; + + COUNT +===================== + 67 + +Wire logical statistics: + send packets = 0 + recv packets = 0 + send bytes = 0 + recv bytes = 0 +Wire physical statistics: + send packets = 0 + recv packets = 0 + send bytes = 0 + recv bytes = 0 + roundtrips = 0 + + + +13) SHOW WIRE_STATISTICS command. + +Author: Vladyslav Khorsun + + New ISQL command that shows accumulated wire statistics. There is also +shortened alias WIRE_STATS. + + The command show values of wire statistics counters, accumulated since the +connection start time. Format is the same as of SET STATS above. diff --git a/src/isql/isql.epp b/src/isql/isql.epp index e904709ce64..8a1f8587b7c 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -587,6 +587,7 @@ public: KeepTranParams = true; TranParams->assign(DEFAULT_DML_TRANS_SQL); PerTableStats = false; + WireStats = false; } ColList global_Cols; @@ -611,6 +612,7 @@ public: SCHAR ISQL_charset[MAXCHARSET_SIZE]; bool KeepTranParams; bool PerTableStats; + bool WireStats; }; static SetValues setValues; @@ -5381,7 +5383,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms, exec_path_display, sql, warning, sqlCont, heading, bail, bulk_insert, maxrows, stmtTimeout, - keepTranParams, perTableStats, + keepTranParams, perTableStats, wireStats, wrong }; SetOptions(const optionsMap* inmap, size_t insize, int wrongval) @@ -5422,7 +5424,8 @@ static processing_state frontend_set(const char* cmd, const char* const* parms, {SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0}, {SetOptions::sqlCont, "DECFLOAT", 0}, {SetOptions::keepTranParams, "KEEP_TRAN_PARAMS", 9}, - {SetOptions::perTableStats, "PER_TABLE_STATS", 7} + {SetOptions::perTableStats, "PER_TABLE_STATS", 7}, + {SetOptions::wireStats, "WIRE_STATS", 4} }; // Display current set options @@ -5662,6 +5665,10 @@ static processing_state frontend_set(const char* cmd, const char* const* parms, ret = do_set_command(parms[2], &setValues.PerTableStats); break; + case SetOptions::wireStats: + ret = do_set_command(parms[2], &setValues.WireStats); + break; + default: //{ // TEXT msg_string[MSG_LENGTH]; @@ -6452,6 +6459,7 @@ static processing_state print_sets() print_set("Print statistics:", setValues.Stats); print_set("Print per-table stats:", setValues.PerTableStats); + print_set("Print wire stats:", setValues.WireStats); print_set("Echo commands:", setValues.Echo); print_set("List format:", setValues.List); print_set("Show Row Count:", setValues.Docount); @@ -9004,6 +9012,10 @@ static processing_state process_statement(const TEXT* str2) if (setValues.PerTableStats) perTableStats->getStats(DB, true); + IsqlWireStats wireStats(DB); + if (setValues.WireStats) + wireStats.get(true); + // Prepare the dynamic query stored in string. // But put this on the DDL transaction to get maximum visibility of // metadata. @@ -9172,6 +9184,9 @@ static processing_state process_statement(const TEXT* str2) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9191,6 +9206,9 @@ static processing_state process_statement(const TEXT* str2) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9238,6 +9256,9 @@ static processing_state process_statement(const TEXT* str2) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9433,6 +9454,9 @@ static processing_state process_statement(const TEXT* str2) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + if (pad) ISQL_FREE(pad); if (line) @@ -9930,3 +9954,126 @@ unsigned PerTableStats::loadRelNames(Firebird::IAttachment* att) return maxLen; } + +/// class IsqlWireStats + +IsqlWireStats::IsqlWireStats(Firebird::IAttachment* att) : + m_att(att), + m_snd_packets(0), + m_rcv_packets(0), + m_out_packets(0), + m_in_packets(0), + m_snd_bytes(0), + m_rcv_bytes(0), + m_out_bytes(0), + m_in_bytes(0), + m_roundtrips(0) +{ +} + +bool IsqlWireStats::get(bool initial) +{ + if (!m_att) + return false; + + const UCHAR info[] = { + fb_info_wire_snd_packets, fb_info_wire_rcv_packets, + fb_info_wire_out_packets, fb_info_wire_in_packets, + fb_info_wire_snd_bytes, fb_info_wire_rcv_bytes, + fb_info_wire_out_bytes, fb_info_wire_in_bytes, + fb_info_wire_roundtrips, + isc_info_end + }; + + UCHAR buffer[128]; + + m_att->getInfo(fbStatus, sizeof(info), info, sizeof(buffer), buffer); + + if (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS) + return false; + + Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, sizeof(buffer)); + for (; !p.isEof(); p.moveNext()) + { + FB_UINT64* pField = nullptr; + switch (p.getClumpTag()) + { + case fb_info_wire_snd_packets: + pField = &m_snd_packets; + break; + case fb_info_wire_rcv_packets: + pField = &m_rcv_packets; + break; + case fb_info_wire_out_packets: + pField = &m_out_packets; + break; + case fb_info_wire_in_packets: + pField = &m_in_packets; + break; + case fb_info_wire_snd_bytes: + pField = &m_snd_bytes; + break; + case fb_info_wire_rcv_bytes: + pField = &m_rcv_bytes; + break; + case fb_info_wire_out_bytes: + pField = &m_out_bytes; + break; + case fb_info_wire_in_bytes: + pField = &m_in_bytes; + break; + case fb_info_wire_roundtrips: + pField = &m_roundtrips; + break; + case isc_info_end: + break; + case isc_info_error: + // don't return false here, as we not put error into status + return true; + /* uncomment to show error (isc_infunk) instead + { + ISC_STATUS errs[3] = { isc_arg_gds, 0, isc_arg_end }; + auto b = p.getBytes(); + errs[1] = isc_portable_integer(b + 1, p.getClumpLength() - 1); + fbStatus->setErrors(errs); + return false; + }*/ + + default: + fb_assert(false); + break; + } + + if (pField) + { + const FB_UINT64 val = p.getBigInt(); + *pField = initial ? val : val - *pField; + } + } + + return true; +} + +bool IsqlWireStats::print(bool initial) +{ + if (!get(initial)) + { + ISQL_errmsg(fbStatus); + return false; + } + + IUTILS_printf2(Diag, "Wire logical statistics:%s", NEWLINE); + IUTILS_printf2(Diag, " send packets = %8" SQUADFORMAT "%s", m_out_packets, NEWLINE); + IUTILS_printf2(Diag, " recv packets = %8" SQUADFORMAT "%s", m_in_packets, NEWLINE); + IUTILS_printf2(Diag, " send bytes = %8" SQUADFORMAT "%s", m_out_bytes, NEWLINE); + IUTILS_printf2(Diag, " recv bytes = %8" SQUADFORMAT "%s", m_in_bytes, NEWLINE); + + IUTILS_printf2(Diag, "Wire physical statistics:%s", NEWLINE); + IUTILS_printf2(Diag, " send packets = %8" SQUADFORMAT "%s", m_snd_packets, NEWLINE); + IUTILS_printf2(Diag, " recv packets = %8" SQUADFORMAT "%s", m_rcv_packets, NEWLINE); + IUTILS_printf2(Diag, " send bytes = %8" SQUADFORMAT "%s", m_snd_bytes, NEWLINE); + IUTILS_printf2(Diag, " recv bytes = %8" SQUADFORMAT "%s", m_rcv_bytes, NEWLINE); + IUTILS_printf2(Diag, " roundtrips = %8" SQUADFORMAT "%s", m_roundtrips, NEWLINE); + + return true; +} diff --git a/src/isql/isql.h b/src/isql/isql.h index 4c400401380..9028bd80bc5 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -311,4 +311,26 @@ struct IsqlVar TypeMix value; }; +class IsqlWireStats +{ +public: + IsqlWireStats(Firebird::IAttachment* att); + + bool print(bool initial); + bool get(bool initial); + +private: + + Firebird::IAttachment* m_att; + FB_UINT64 m_snd_packets; + FB_UINT64 m_rcv_packets; + FB_UINT64 m_out_packets; + FB_UINT64 m_in_packets; + FB_UINT64 m_snd_bytes; + FB_UINT64 m_rcv_bytes; + FB_UINT64 m_out_bytes; + FB_UINT64 m_in_bytes; + FB_UINT64 m_roundtrips; +}; + #endif // ISQL_ISQL_H diff --git a/src/isql/show.epp b/src/isql/show.epp index f5962fb10b4..cb1225832f0 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -116,6 +116,7 @@ static processing_state show_trigger(const SCHAR*, bool, bool); static processing_state show_users(); static processing_state show_users12(); static void parse_package(const char* procname, MetaString& package, MetaString& procedure); +static processing_state show_wireStats(); const char* const spaces = " "; static TEXT Print_buffer[512]; @@ -2098,7 +2099,8 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) role, table, view, system, index, domain, exception, filter, function, generator, grant, procedure, trigger, check, database, comment, dependency, collation, security_class, - users, package, publication, schema, map, wrong + users, package, publication, schema, map, wireStats, + wrong }; ShowOptions(const optionsMap* inmap, size_t insize, int wrongval) : OptionsBase(inmap, insize, wrongval) @@ -2156,7 +2158,9 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) {ShowOptions::package, "PACKAGES", 4}, {ShowOptions::schema, "SCHEMAS", 4}, {ShowOptions::map, "MAPPING", 3}, - {ShowOptions::publication, "PUBLICATIONS", 3} + {ShowOptions::publication, "PUBLICATIONS", 3}, + {ShowOptions::wireStats, "WIRE_STATISTICS", 9}, + {ShowOptions::wireStats, "WIRE_STATS", 10} }; const ShowOptions showoptions(options, FB_NELEM(options), ShowOptions::wrong); @@ -2813,6 +2817,10 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) return ps_ERR; break; + case ShowOptions::wireStats: + ret = show_wireStats(); + break; + default: return ps_ERR; } // switch @@ -6745,3 +6753,18 @@ static processing_state show_users() return rc; } + +static processing_state show_wireStats() +{ + if (!DB) + { + isqlGlob.printf("No database connection.\n"); + return SKIP; + } + + IsqlWireStats stats(DB); + if (!stats.print(true)) + return ps_ERR; + + return SKIP; +} \ No newline at end of file From 5e2edec6bb349cb5750df5f08e211b13e0e6008d Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 8 Nov 2024 08:10:04 +0200 Subject: [PATCH 3/6] Remove requirement to not mix local and remote handled items in the same info request. --- src/remote/client/interface.cpp | 45 ++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 081b6fbfa39..a9ed65a4e72 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -936,8 +936,11 @@ class Attachment final : public RefCntIfacerdb_port; UCHAR* ptr = buffer; const UCHAR* const end = buffer + buffer_length; - for (auto item = items; item < items + item_length; item++) + for (auto item = info.begin(); item < info.end(); ) { + if (ptr >= end) + return nullptr; + switch (*item) { case fb_info_wire_snd_packets: @@ -1997,23 +2003,28 @@ bool Attachment::getWireStatsInfo(unsigned int item_length, const unsigned char* else ptr = fb_utils::putInfoItemInt(*item, value, ptr, end); - fb_assert(ptr); + if (!ptr) + return nullptr; + + info.remove(item); break; } case isc_info_end: - if (ptr < end) - *ptr++ = *item; + if (info.getCount() > 1) + return ptr; - return true; + *ptr++ = *item; + info.remove(item); + return nullptr; default: - // Let someone else handle unknown item - return false; + item++; + break; } } - return true; + return info.isEmpty() ? nullptr : ptr; } @@ -2045,18 +2056,22 @@ void Attachment::getInfo(CheckStatusWrapper* status, RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); - if (getWireStatsInfo(item_length, items, buffer_length, buffer)) + UCharBuffer tempInfo(items, item_length); + UCHAR* ptr = getWireStatsInfo(tempInfo, buffer_length, buffer); + if (!ptr) return; + buffer_length -= ptr - buffer; + UCHAR* temp_buffer = temp.getBuffer(buffer_length); info(status, rdb, op_info_database, rdb->rdb_id, 0, - item_length, items, 0, 0, buffer_length, temp_buffer); + tempInfo.getCount(), tempInfo.begin(), 0, 0, buffer_length, temp_buffer); string version; port->versionInfo(version); - MERGE_database_info(temp_buffer, buffer, buffer_length, + MERGE_database_info(temp_buffer, ptr, buffer_length, DbImplementation::current.backwardCompatibleImplementation(), 3, 1, reinterpret_cast(version.c_str()), reinterpret_cast(port->port_host->str_data), From cf1bf7db5475c4fd9ba7284a96496a2a3921c203 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 8 Nov 2024 08:11:45 +0200 Subject: [PATCH 4/6] Follow @asfernandes suggestion about class members initialization. --- src/isql/isql.epp | 14 -------------- src/isql/isql.h | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 8a1f8587b7c..9cc3139b795 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -9957,20 +9957,6 @@ unsigned PerTableStats::loadRelNames(Firebird::IAttachment* att) /// class IsqlWireStats -IsqlWireStats::IsqlWireStats(Firebird::IAttachment* att) : - m_att(att), - m_snd_packets(0), - m_rcv_packets(0), - m_out_packets(0), - m_in_packets(0), - m_snd_bytes(0), - m_rcv_bytes(0), - m_out_bytes(0), - m_in_bytes(0), - m_roundtrips(0) -{ -} - bool IsqlWireStats::get(bool initial) { if (!m_att) diff --git a/src/isql/isql.h b/src/isql/isql.h index 9028bd80bc5..aebe00b32d9 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -314,7 +314,9 @@ struct IsqlVar class IsqlWireStats { public: - IsqlWireStats(Firebird::IAttachment* att); + IsqlWireStats(Firebird::IAttachment* att) : + m_att(att) + {} bool print(bool initial); bool get(bool initial); @@ -322,15 +324,15 @@ class IsqlWireStats private: Firebird::IAttachment* m_att; - FB_UINT64 m_snd_packets; - FB_UINT64 m_rcv_packets; - FB_UINT64 m_out_packets; - FB_UINT64 m_in_packets; - FB_UINT64 m_snd_bytes; - FB_UINT64 m_rcv_bytes; - FB_UINT64 m_out_bytes; - FB_UINT64 m_in_bytes; - FB_UINT64 m_roundtrips; + FB_UINT64 m_snd_packets = 0; + FB_UINT64 m_rcv_packets = 0; + FB_UINT64 m_out_packets = 0; + FB_UINT64 m_in_packets = 0; + FB_UINT64 m_snd_bytes = 0; + FB_UINT64 m_rcv_bytes = 0; + FB_UINT64 m_out_bytes = 0; + FB_UINT64 m_in_bytes = 0; + FB_UINT64 m_roundtrips = 0; }; #endif // ISQL_ISQL_H From 49eb920e81874947e22b2592f15714a551af4b85 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 8 Nov 2024 12:07:06 +0200 Subject: [PATCH 5/6] Make ctor explicit, as @sim1984 suggested --- src/isql/isql.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isql/isql.h b/src/isql/isql.h index aebe00b32d9..ccbd8ac3fe4 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -314,7 +314,7 @@ struct IsqlVar class IsqlWireStats { public: - IsqlWireStats(Firebird::IAttachment* att) : + explicit IsqlWireStats(Firebird::IAttachment* att) : m_att(att) {} From 061b561694ddbe210f8eb05e5133509145c917e6 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 8 Nov 2024 12:07:28 +0200 Subject: [PATCH 6/6] Put isc_info_end into response buffer despite of its presence in info items. --- src/remote/client/interface.cpp | 20 +++++++++++--------- src/remote/remote.cpp | 11 ++++++----- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index a9ed65a4e72..2b8725e0a21 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -1984,6 +1984,13 @@ unsigned char* Attachment::getWireStatsInfo(UCharBuffer& info, unsigned int buff if (ptr >= end) return nullptr; + if (*item == isc_info_end) + { + if (info.getCount() == 1) + info.remove(item); + break; + } + switch (*item) { case fb_info_wire_snd_packets: @@ -2010,21 +2017,16 @@ unsigned char* Attachment::getWireStatsInfo(UCharBuffer& info, unsigned int buff break; } - case isc_info_end: - if (info.getCount() > 1) - return ptr; - - *ptr++ = *item; - info.remove(item); - return nullptr; - default: item++; break; } } - return info.isEmpty() ? nullptr : ptr; + if (info.isEmpty() && ptr < end) + *ptr++ = isc_info_end; + + return (info.isEmpty() || (ptr >= end)) ? nullptr : ptr; } diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index 2a8da65fef2..ba71dfe60bf 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -870,6 +870,9 @@ bool RBlobInfo::getLocalInfo(unsigned int itemsLength, const unsigned char* item for (auto item = items; p && (item < items + itemsLength); item++) { + if (*item == isc_info_end) + break; + switch (*item) { case isc_info_blob_num_segments: @@ -888,17 +891,15 @@ bool RBlobInfo::getLocalInfo(unsigned int itemsLength, const unsigned char* item p = fb_utils::putInfoItemInt(*item, blob_type, p, end); break; - case isc_info_end: - if (p < end) - *p++ = isc_info_end; - break; - default: // unknown info item, let remote server handle it return false; } } + if (p < end) + *p++ = isc_info_end; + return true; }