From df885d5935122cd8392365e5e473a71279eeb9ae Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Tue, 12 Nov 2024 13:00:25 +0200 Subject: [PATCH] Collect network statistics and make it available for the user applications. (#8310) * Make Remote provider collect wire statistics. New info items to query wire stats counters. * New ISQL commands to show wire statistics. * Remove requirement to not mix local and remote handled items in the same info request. * Follow @asfernandes suggestion about class members initialization. * Make ctor explicit, as @sim1984 suggested * Put isc_info_end into response buffer despite of its presence in info items. --- doc/README.isql_enhancements.txt | 123 ++++++++++++++++++++++++- src/include/firebird/impl/inf_pub.h | 11 +++ src/include/gen/Firebird.pas | 9 ++ src/isql/isql.epp | 137 +++++++++++++++++++++++++++- src/isql/isql.h | 24 +++++ src/isql/show.epp | 27 +++++- src/remote/client/interface.cpp | 75 ++++++++++++++- 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 ++++++++++++++++++- 12 files changed, 514 insertions(+), 21 deletions(-) diff --git a/doc/README.isql_enhancements.txt b/doc/README.isql_enhancements.txt index 181f43f5ca..b37997cdf7 100644 --- a/doc/README.isql_enhancements.txt +++ b/doc/README.isql_enhancements.txt @@ -327,10 +327,129 @@ 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. + + + Isql enhancements in Firebird v6. --------------------------------- -12) EXPLAIN statement. +14) EXPLAIN statement. Author: Adriano dos Santos Fernandes @@ -355,7 +474,7 @@ SQL> SQL> set term ;! -13) SET AUTOTERM ON/OFF +15) SET AUTOTERM ON/OFF Author: Adriano dos Santos Fernandes diff --git a/src/include/firebird/impl/inf_pub.h b/src/include/firebird/impl/inf_pub.h index 06afa3c739..9f77591fdc 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 ccca8f1943..9c1be4d79a 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -4507,6 +4507,15 @@ const 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/isql/isql.epp b/src/isql/isql.epp index 266aa0f3e1..771e3e2e69 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -577,6 +577,7 @@ public: KeepTranParams = true; TranParams->assign(DEFAULT_DML_TRANS_SQL); PerTableStats = false; + WireStats = false; ExplainCommand = false; } @@ -603,6 +604,7 @@ public: SCHAR ISQL_charset[MAXCHARSET_SIZE]; bool KeepTranParams; bool PerTableStats; + bool WireStats; bool ExplainCommand; }; @@ -5453,7 +5455,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) @@ -5495,7 +5497,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 @@ -5746,6 +5749,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]; @@ -6372,6 +6379,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); @@ -8950,6 +8958,10 @@ static processing_state process_statement(const std::string& str) 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. @@ -9137,6 +9149,9 @@ static processing_state process_statement(const std::string& str) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9156,6 +9171,9 @@ static processing_state process_statement(const std::string& str) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9203,6 +9221,9 @@ static processing_state process_statement(const std::string& str) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + return ret; } @@ -9398,6 +9419,9 @@ static processing_state process_statement(const std::string& str) if (setValues.PerTableStats) perTableStats->getStats(DB, false); + if (setValues.WireStats) + wireStats.print(false); + if (pad) ISQL_FREE(pad); if (line) @@ -9895,3 +9919,112 @@ unsigned PerTableStats::loadRelNames(Firebird::IAttachment* att) return maxLen; } + +/// class IsqlWireStats + +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 6051d968a0..8c97ec42a8 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -312,4 +312,28 @@ struct IsqlVar TypeMix value; }; +class IsqlWireStats +{ +public: + explicit IsqlWireStats(Firebird::IAttachment* att) : + m_att(att) + {} + + bool print(bool initial); + bool get(bool initial); + +private: + + Firebird::IAttachment* m_att; + 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 diff --git a/src/isql/show.epp b/src/isql/show.epp index 8c73486c9e..c15a22b422 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]; @@ -2090,7 +2091,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) @@ -2148,7 +2150,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); @@ -2805,6 +2809,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 @@ -6687,3 +6695,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 diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index a05adbf514..d175c6a9ba 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -937,6 +937,11 @@ private: void internalDropDatabase(CheckStatusWrapper* status); SLONG getSingleInfo(CheckStatusWrapper* status, UCHAR infoItem); + // Returns nullptr if all items was handled or if user buffer is full, else + // returns pointer into unused buffer space. Handled info items are removed. + unsigned char* getWireStatsInfo(UCharBuffer& info, unsigned int buffer_length, + unsigned char* buffer); + Rdb* rdb; const PathName dbPath; }; @@ -1966,6 +1971,65 @@ IAttachment* Loopback::createDatabase(CheckStatusWrapper* status, const char* fi } +unsigned char* Attachment::getWireStatsInfo(UCharBuffer& info, unsigned int buffer_length, + unsigned char* buffer) +{ + const rem_port* const port = rdb->rdb_port; + + UCHAR* ptr = buffer; + const UCHAR* const end = buffer + buffer_length; + + for (auto item = info.begin(); item < info.end(); ) + { + 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: + 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); + + if (!ptr) + return nullptr; + + info.remove(item); + break; + } + + default: + item++; + break; + } + } + + if (info.isEmpty() && ptr < end) + *ptr++ = isc_info_end; + + return (info.isEmpty() || (ptr >= end)) ? nullptr : ptr; +} + + void Attachment::getInfo(CheckStatusWrapper* status, unsigned int item_length, const unsigned char* items, unsigned int buffer_length, unsigned char* buffer) @@ -1994,15 +2058,22 @@ void Attachment::getInfo(CheckStatusWrapper* status, RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); + 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), diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index 161e614a43..552afde30e 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -3130,8 +3130,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; @@ -3306,9 +3305,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 768e8a29a7..acd2bfb7d5 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -1992,8 +1992,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; @@ -2049,8 +2049,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 8f5bf38882..2564d978ce 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 afbd7d450a..609dd6eeb0 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -1494,7 +1494,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; @@ -1566,16 +1571,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 14f7c9a78d..e61cf071f9 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -1200,10 +1200,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; @@ -1243,7 +1333,9 @@ public: 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);