From d2f31aab0ad160efff79eea632dad2e7595b8858 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 3 Apr 2019 14:55:07 +0300 Subject: [PATCH] MXS-2420 Add debug function for decoding response packet When debugging you occasionally want to find out what a packet contains (e.g. delivered to clientReply). Manually looking into the packet works, but is tedious. With this function you when the execution has been stopped in GDB examine a protocol packet. E.g. Thread 3 "maxscale" hit Breakpoint 6, RWSplitSession::clientReply (this=0x7fffe401ed20, writebuf=0x7fffe401e910, backend_dcb=0x7fffe401dbe0) at /home/wikman/MariaDB/MaxScale/server/modules/routing/readwritesplit/rwsplitsession.cc:567 567 DCB* client_dcb = backend_dcb->session->client_dcb; (gdb) p dbg_decode_response(writebuf) $30 = 0x7ffff0d40d54 "Packet no: 1, Payload len: 44, Command : ERR, Code: 1146, Message : Table 'test.blahasdf' doesn't exist" --- server/core/mysql_utils.cc | 104 ++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/server/core/mysql_utils.cc b/server/core/mysql_utils.cc index 2653cc956..6f57d6b6d 100644 --- a/server/core/mysql_utils.cc +++ b/server/core/mysql_utils.cc @@ -380,4 +380,106 @@ std::unique_ptr execute_query(MYSQL* conn, const std::string& return rval; } -} \ No newline at end of file +} + +#if defined(SS_DEBUG) +/** + * Return decoded MySQL response packet. + * + * Intended to be used when debugging with a GDB-based debugger. + * For instance, if GDB has been stopped by a breakpoint in + * clientReply() you can use this function for investigating + * what the response packet contains: + * + * (gdb) p dbg_decode_response(writebuf) + * $30 = 0x7ffff0d40d54 "Packet no: 1, Payload len: 44, Command : ERR, + * Code: 1146, Message : Table 'test.blahasdf' doesn't exist" + * + * @param pPacket A MySQL response packet. + * + * @return The packet decoded into a descriptive string. + */ +char *dbg_decode_response(GWBUF* pPacket) +{ + uint8_t header[MYSQL_HEADER_LEN + 1]; + + gwbuf_copy_data(pPacket, 0, sizeof(header), header); + + uint32_t payload_len = MYSQL_GET_PAYLOAD_LEN(header); + uint32_t packet_no = MYSQL_GET_PACKET_NO(header); + + uint32_t command = std::numeric_limits::max(); + + if (gwbuf_length(pPacket) > MYSQL_HEADER_LEN) + { + command = MYSQL_GET_COMMAND(header); + } + + const int buflen = 1024; // Should be enough. + + thread_local char buffer[buflen]; + + int len = buflen; + char* z = buffer; + + int l = snprintf(z, len, "Packet no: %u, Payload len: %u", packet_no, payload_len); + + z += l; + len -= l; + + switch (command) + { + case 0x00: + snprintf(z, len, ", Command : OK"); + break; + + case 0xff: + { + l = snprintf(z, len, ", Command : ERR"); + z += l; + len -= l; + + uint8_t error[payload_len]; + + gwbuf_copy_data(pPacket, MYSQL_HEADER_LEN, payload_len, error); + + uint32_t error_code = gw_mysql_get_byte2(&error[1]); + + l = snprintf(z, len, ", Code: %u", error_code); + z += l; + len -= l; + + const int message_index = 1 + 2 + 1 + 5; + uint8_t* pMessage = &error[message_index]; + + l = snprintf(z, len, ", Message : "); + + z += l; + len -= l; + + int message_len = payload_len - message_index; + + if (message_len + 1 > len) + { + message_len = len - 1; + } + + strncpy(z, (char*)pMessage, message_len); + z[message_len] = 0; + } + break; + + case 0xfb: + snprintf(z, len, ", Command : GET_MORE_CLIENT_DATA"); + break; + + case std::numeric_limits::max(): + break; + + default: + snprintf(z, len, ", Command : Result Set"); + } + + return buffer; +} +#endif