Skip to content

Commit 11f4e1e

Browse files
extra error info in case of failed parsing
1 parent fe67267 commit 11f4e1e

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

protocol.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,59 @@ void abstract_protocol::set_keep_value(bool flag)
5656
m_keep_value = flag;
5757
}
5858

59+
void abstract_protocol::capture_buffer_preview()
60+
{
61+
if (!m_read_buf) {
62+
m_parse_error.raw_buffer_len = 0;
63+
m_parse_error.raw_buffer_preview[0] = '\0';
64+
return;
65+
}
66+
67+
m_parse_error.raw_buffer_len = evbuffer_get_length(m_read_buf);
68+
69+
if (m_parse_error.raw_buffer_len == 0) {
70+
m_parse_error.raw_buffer_preview[0] = '\0';
71+
return;
72+
}
73+
74+
// Get a preview of the buffer (up to 100 bytes to leave room for escaping)
75+
size_t preview_len = m_parse_error.raw_buffer_len;
76+
if (preview_len > 100) preview_len = 100;
77+
78+
unsigned char *buf_data = evbuffer_pullup(m_read_buf, preview_len);
79+
if (!buf_data) {
80+
m_parse_error.raw_buffer_preview[0] = '\0';
81+
return;
82+
}
83+
84+
// Escape non-printable characters
85+
char *out = m_parse_error.raw_buffer_preview;
86+
char *out_end = m_parse_error.raw_buffer_preview + sizeof(m_parse_error.raw_buffer_preview) - 5;
87+
88+
for (size_t i = 0; i < preview_len && out < out_end; i++) {
89+
unsigned char c = buf_data[i];
90+
if (c >= 32 && c <= 126 && c != '\\') {
91+
*out++ = c;
92+
} else if (c == '\r') {
93+
*out++ = '\\'; *out++ = 'r';
94+
} else if (c == '\n') {
95+
*out++ = '\\'; *out++ = 'n';
96+
} else if (c == '\t') {
97+
*out++ = '\\'; *out++ = 't';
98+
} else if (c == '\\') {
99+
*out++ = '\\'; *out++ = '\\';
100+
} else {
101+
out += snprintf(out, 5, "\\x%02x", c);
102+
}
103+
}
104+
105+
if (m_parse_error.raw_buffer_len > preview_len) {
106+
snprintf(out, 5, "...");
107+
out += 3;
108+
}
109+
*out = '\0';
110+
}
111+
59112
/////////////////////////////////////////////////////////////////////////
60113

61114
protocol_response::protocol_response()
@@ -174,6 +227,23 @@ class redis_protocol : public abstract_protocol {
174227
bool single_type(char c);
175228
bool response_ended();
176229

230+
const char* response_state_name() const {
231+
switch (m_response_state) {
232+
case rs_initial: return "initial";
233+
case rs_read_bulk: return "read_bulk";
234+
case rs_read_line: return "read_line";
235+
case rs_end_bulk: return "end_bulk";
236+
default: return "unknown";
237+
}
238+
}
239+
240+
void set_parse_error(const char* msg) {
241+
m_parse_error.error_message = msg;
242+
m_parse_error.response_state_name = response_state_name();
243+
m_parse_error.bytes_parsed = m_response_len;
244+
capture_buffer_preview();
245+
}
246+
177247
public:
178248
redis_protocol() : m_response_state(rs_initial), m_bulk_len(0), m_response_len(0), m_total_bulks_count(0), m_current_mbulk(NULL), m_resp3(false), m_attribute(false) { }
179249
virtual redis_protocol* clone(void) { return new redis_protocol(); }
@@ -625,6 +695,7 @@ int redis_protocol::parse_response(void)
625695
}
626696
} else {
627697
benchmark_debug_log("unsupported response: '%s'.\n", line);
698+
set_parse_error("unsupported response type");
628699
free(line);
629700
return -1;
630701
}
@@ -707,10 +778,12 @@ int redis_protocol::parse_response(void)
707778
}
708779
break;
709780
default:
781+
set_parse_error("invalid response state");
710782
return -1;
711783
}
712784
}
713785

786+
set_parse_error("unexpected end of parse loop");
714787
return -1;
715788
}
716789

@@ -785,6 +858,24 @@ class memcache_text_protocol : public abstract_protocol {
785858
response_state m_response_state;
786859
unsigned int m_value_len;
787860
size_t m_response_len;
861+
862+
const char* response_state_name() const {
863+
switch (m_response_state) {
864+
case rs_initial: return "initial";
865+
case rs_read_section: return "read_section";
866+
case rs_read_value: return "read_value";
867+
case rs_read_end: return "read_end";
868+
default: return "unknown";
869+
}
870+
}
871+
872+
void set_parse_error(const char* msg) {
873+
m_parse_error.error_message = msg;
874+
m_parse_error.response_state_name = response_state_name();
875+
m_parse_error.bytes_parsed = m_response_len;
876+
capture_buffer_preview();
877+
}
878+
788879
public:
789880
memcache_text_protocol() : m_response_state(rs_initial), m_value_len(0), m_response_len(0) { }
790881
virtual memcache_text_protocol* clone(void) { return new memcache_text_protocol(); }
@@ -927,6 +1018,7 @@ int memcache_text_protocol::parse_response(void)
9271018
int res = sscanf(line, "%s %s %u %u %u", prefix, key, &flags, &m_value_len, &cas);
9281019
if (res < 4|| res > 5) {
9291020
benchmark_debug_log("unexpected VALUE response: %s\n", line);
1021+
set_parse_error("unexpected VALUE response format");
9301022
if (m_last_response.get_status() != line)
9311023
free(line);
9321024
return -1;
@@ -943,6 +1035,7 @@ int memcache_text_protocol::parse_response(void)
9431035
} else {
9441036
m_last_response.set_error();
9451037
benchmark_debug_log("unknown response: %s\n", line);
1038+
set_parse_error("unknown response type");
9461039
return -1;
9471040
}
9481041
break;
@@ -978,10 +1071,12 @@ int memcache_text_protocol::parse_response(void)
9781071

9791072
default:
9801073
benchmark_debug_log("unknown response state %d.\n", m_response_state);
1074+
set_parse_error("invalid response state");
9811075
return -1;
9821076
}
9831077
}
9841078

1079+
set_parse_error("unexpected end of parse loop");
9851080
return -1;
9861081
}
9871082

@@ -1007,6 +1102,22 @@ class memcache_binary_protocol : public abstract_protocol {
10071102
size_t m_response_len;
10081103

10091104
const char* status_text(void);
1105+
1106+
const char* response_state_name() const {
1107+
switch (m_response_state) {
1108+
case rs_initial: return "initial";
1109+
case rs_read_body: return "read_body";
1110+
default: return "unknown";
1111+
}
1112+
}
1113+
1114+
void set_parse_error(const char* msg) {
1115+
m_parse_error.error_message = msg;
1116+
m_parse_error.response_state_name = response_state_name();
1117+
m_parse_error.bytes_parsed = m_response_len;
1118+
capture_buffer_preview();
1119+
}
1120+
10101121
public:
10111122
memcache_binary_protocol() : m_response_state(rs_initial), m_response_len(0) { }
10121123
virtual memcache_binary_protocol* clone(void) { return new memcache_binary_protocol(); }
@@ -1190,6 +1301,7 @@ int memcache_binary_protocol::parse_response(void)
11901301

11911302
if (m_response_hdr.message.header.response.magic != PROTOCOL_BINARY_RES) {
11921303
benchmark_error_log("error: invalid memcache response header magic.\n");
1304+
set_parse_error("invalid memcache response header magic");
11931305
return -1;
11941306
}
11951307

@@ -1253,10 +1365,12 @@ int memcache_binary_protocol::parse_response(void)
12531365
break;
12541366
default:
12551367
benchmark_debug_log("unknown response state.\n");
1368+
set_parse_error("invalid response state");
12561369
return -1;
12571370
}
12581371
}
12591372

1373+
set_parse_error("unexpected end of parse loop");
12601374
return -1;
12611375
}
12621376

protocol.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,32 @@ class keylist {
168168
void clear(void);
169169
};
170170

171+
// Structure to hold detailed parse error information
172+
struct parse_error_info {
173+
const char* error_message; // Description of what failed
174+
const char* response_state_name; // Name of the response state when error occurred
175+
char raw_buffer_preview[256]; // Preview of raw buffer content (escaped)
176+
size_t raw_buffer_len; // Length of data in read buffer
177+
size_t bytes_parsed; // How many bytes were parsed before error
178+
179+
parse_error_info() : error_message("unknown error"), response_state_name("unknown"),
180+
raw_buffer_len(0), bytes_parsed(0) {
181+
raw_buffer_preview[0] = '\0';
182+
}
183+
};
184+
171185
class abstract_protocol {
172186
protected:
173187
struct evbuffer* m_read_buf;
174188
struct evbuffer* m_write_buf;
175189

176190
bool m_keep_value;
177191
struct protocol_response m_last_response;
192+
struct parse_error_info m_parse_error;
193+
194+
// Helper to capture raw buffer preview for error reporting
195+
void capture_buffer_preview();
196+
178197
public:
179198
abstract_protocol();
180199
virtual ~abstract_protocol();
@@ -198,6 +217,9 @@ class abstract_protocol {
198217
virtual int write_arbitrary_command(const char *val, int val_len) = 0;
199218

200219
struct protocol_response* get_response(void) { return &m_last_response; }
220+
221+
// Get detailed error information when parse_response() returns -1
222+
const parse_error_info* get_parse_error(void) const { return &m_parse_error; }
201223
};
202224

203225
class abstract_protocol *protocol_factory(enum PROTOCOL_TYPE type);

shard_connection.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,11 +522,13 @@ void shard_connection::process_response(void)
522522
{
523523
int ret;
524524
bool responses_handled = false;
525+
unsigned int responses_parsed = 0;
525526

526527
struct timeval now;
527528
gettimeofday(&now, NULL);
528529

529530
while ((ret = m_protocol->parse_response()) > 0) {
531+
responses_parsed++;
530532
bool error = false;
531533
protocol_response *r = m_protocol->get_response();
532534

@@ -592,7 +594,18 @@ void shard_connection::process_response(void)
592594
}
593595

594596
if (ret == -1) {
597+
const parse_error_info* err = m_protocol->get_parse_error();
595598
benchmark_error_log("error: response parsing failed.\n");
599+
benchmark_error_log(" server: %s\n", get_readable_id());
600+
benchmark_error_log(" pipeline position: %u (of %zu pending)\n",
601+
responses_parsed + 1, responses_parsed + m_pipeline->size() + 1);
602+
benchmark_error_log(" error: %s\n", err->error_message);
603+
benchmark_error_log(" parser state: %s\n", err->response_state_name);
604+
benchmark_error_log(" buffer length: %zu bytes, parsed: %zu bytes\n",
605+
err->raw_buffer_len, err->bytes_parsed);
606+
if (err->raw_buffer_preview[0] != '\0') {
607+
benchmark_error_log(" raw data: %s\n", err->raw_buffer_preview);
608+
}
596609
}
597610

598611
if (m_config->reconnect_interval > 0 && responses_handled) {

0 commit comments

Comments
 (0)