all repos — mgba @ b3c7bd8227dcb77222bd596c0cff474919bd5b99

mGBA Game Boy Advance Emulator

src/debugger/gdb-stub.c (view raw)

  1/* Copyright (c) 2013-2014 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gdb-stub.h"
  7
  8#include "arm/isa-inlines.h"
  9#include "core/core.h"
 10#include "gba/memory.h"
 11
 12#include <signal.h>
 13
 14#ifndef SIGTRAP
 15#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
 16#endif
 17
 18#define SOCKET_TIMEOUT 50
 19
 20enum GDBError {
 21	GDB_NO_ERROR = 0x00,
 22	GDB_BAD_ARGUMENTS = 0x06,
 23	GDB_UNSUPPORTED_COMMAND = 0x07
 24};
 25
 26enum {
 27	MACH_O_ARM = 12,
 28	MACH_O_ARM_V4T = 5
 29};
 30
 31static void _sendMessage(struct GDBStub* stub);
 32
 33static void _gdbStubDeinit(struct mDebugger* debugger) {
 34	struct GDBStub* stub = (struct GDBStub*) debugger;
 35	if (!SOCKET_FAILED(stub->socket)) {
 36		GDBStubShutdown(stub);
 37	}
 38}
 39
 40static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
 41	struct GDBStub* stub = (struct GDBStub*) debugger;
 42	switch (reason) {
 43	case DEBUGGER_ENTER_MANUAL:
 44		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
 45		break;
 46	case DEBUGGER_ENTER_BREAKPOINT:
 47		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); // TODO: Use hwbreak/swbreak if gdb supports it
 48		break;
 49	case DEBUGGER_ENTER_WATCHPOINT:
 50		if (info) {
 51			const char* type = 0;
 52			switch (info->watchType) {
 53			case WATCHPOINT_WRITE:
 54				if (info->newValue == info->oldValue) {
 55					if (stub->d.state == DEBUGGER_PAUSED) {
 56						stub->d.state = DEBUGGER_RUNNING;
 57					}
 58					return;
 59				}
 60				type = "watch";
 61				break;
 62			case WATCHPOINT_READ:
 63				type = "rwatch";
 64				break;
 65			case WATCHPOINT_RW:
 66				type = "awatch";
 67				break;
 68			}
 69			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08x;", SIGTRAP, type, info->address);
 70		} else {
 71			snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
 72		}
 73		break;
 74	case DEBUGGER_ENTER_ILLEGAL_OP:
 75		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGILL);
 76		break;
 77	case DEBUGGER_ENTER_ATTACHED:
 78		return;
 79	}
 80	_sendMessage(stub);
 81}
 82
 83static void _gdbStubPoll(struct mDebugger* debugger) {
 84	struct GDBStub* stub = (struct GDBStub*) debugger;
 85	--stub->untilPoll;
 86	if (stub->untilPoll > 0) {
 87		return;
 88	}
 89	stub->untilPoll = GDB_STUB_INTERVAL;
 90	stub->shouldBlock = false;
 91	GDBStubUpdate(stub);
 92}
 93
 94static void _gdbStubWait(struct mDebugger* debugger) {
 95	struct GDBStub* stub = (struct GDBStub*) debugger;
 96	stub->shouldBlock = true;
 97	GDBStubUpdate(stub);
 98}
 99
100static void _ack(struct GDBStub* stub) {
101	char ack = '+';
102	SocketSend(stub->connection, &ack, 1);
103}
104
105static void _nak(struct GDBStub* stub) {
106	char nak = '-';
107	mLOG(DEBUGGER, WARN, "Packet error");
108	SocketSend(stub->connection, &nak, 1);
109}
110
111static uint32_t _hex2int(const char* hex, int maxDigits) {
112	uint32_t value = 0;
113	uint8_t letter;
114
115	while (maxDigits--) {
116		letter = *hex - '0';
117		if (letter > 9) {
118			letter = *hex - 'a';
119			if (letter > 5) {
120				break;
121			}
122			value *= 0x10;
123			value += letter + 10;
124		} else {
125			value *= 0x10;
126			value += letter;
127		}
128		++hex;
129	}
130	return value;
131}
132
133static void _int2hex8(uint8_t value, char* out) {
134	static const char language[] = "0123456789abcdef";
135	out[0] = language[value >> 4];
136	out[1] = language[value & 0xF];
137}
138
139static void _int2hex32(uint32_t value, char* out) {
140	static const char language[] = "0123456789abcdef";
141	out[6] = language[value >> 28];
142	out[7] = language[(value >> 24) & 0xF];
143	out[4] = language[(value >> 20) & 0xF];
144	out[5] = language[(value >> 16) & 0xF];
145	out[2] = language[(value >> 12) & 0xF];
146	out[3] = language[(value >> 8) & 0xF];
147	out[0] = language[(value >> 4) & 0xF];
148	out[1] = language[value & 0xF];
149}
150
151static uint32_t _readHex(const char* in, unsigned* out) {
152	unsigned i;
153	for (i = 0; i < 8; ++i) {
154		if (in[i] == ',' || in[i] == ':' || in[i] == '=') {
155			break;
156		}
157	}
158	*out += i;
159	return _hex2int(in, i);
160}
161
162static void _sendMessage(struct GDBStub* stub) {
163	if (stub->lineAck != GDB_ACK_OFF) {
164		stub->lineAck = GDB_ACK_PENDING;
165	}
166	uint8_t checksum = 0;
167	int i = 1;
168	char buffer = stub->outgoing[0];
169	char swap;
170	stub->outgoing[0] = '$';
171	if (buffer) {
172		for (; i < GDB_STUB_MAX_LINE - 5; ++i) {
173			checksum += buffer;
174			swap = stub->outgoing[i];
175			stub->outgoing[i] = buffer;
176			buffer = swap;
177			if (!buffer) {
178				++i;
179				break;
180			}
181		}
182	}
183	stub->outgoing[i] = '#';
184	_int2hex8(checksum, &stub->outgoing[i + 1]);
185	stub->outgoing[i + 3] = 0;
186	mLOG(DEBUGGER, DEBUG, "> %s", stub->outgoing);
187	SocketSend(stub->connection, stub->outgoing, i + 3);
188}
189
190static void _error(struct GDBStub* stub, enum GDBError error) {
191	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "E%02x", error);
192	_sendMessage(stub);
193}
194
195static void _writeHostInfo(struct GDBStub* stub) {
196	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "cputype:%u;cpusubtype:%u:ostype:none;vendor:none;endian:little;ptrsize:4;", MACH_O_ARM, MACH_O_ARM_V4T);
197	_sendMessage(stub);
198}
199
200static void _continue(struct GDBStub* stub, const char* message) {
201	stub->d.state = DEBUGGER_CUSTOM;
202	stub->untilPoll = GDB_STUB_INTERVAL;
203	// TODO: parse message
204	UNUSED(message);
205}
206
207static void _step(struct GDBStub* stub, const char* message) {
208	stub->d.core->step(stub->d.core);
209	snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
210	_sendMessage(stub);
211	// TODO: parse message
212	UNUSED(message);
213}
214
215static void _writeMemoryBinary(struct GDBStub* stub, const char* message) {
216	const char* readAddress = message;
217	unsigned i = 0;
218	uint32_t address = _readHex(readAddress, &i);
219	readAddress += i + 1;
220
221	i = 0;
222	uint32_t size = _readHex(readAddress, &i);
223	readAddress += i + 1;
224
225	if (size > 512) {
226		_error(stub, GDB_BAD_ARGUMENTS);
227		return;
228	}
229
230	struct ARMCore* cpu = stub->d.core->cpu;
231	for (i = 0; i < size; i++) {
232		uint8_t byte = *readAddress;
233		++readAddress;
234
235		// Parse escape char
236		if (byte == 0x7D) {
237			byte = *readAddress ^ 0x20;
238			++readAddress;
239		}
240
241		GBAPatch8(cpu, address + i, byte, 0);
242	}
243
244	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
245	_sendMessage(stub);
246}
247
248
249static void _writeMemory(struct GDBStub* stub, const char* message) {
250	const char* readAddress = message;
251	unsigned i = 0;
252	uint32_t address = _readHex(readAddress, &i);
253	readAddress += i + 1;
254
255	i = 0;
256	uint32_t size = _readHex(readAddress, &i);
257	readAddress += i + 1;
258
259	if (size > 512) {
260		_error(stub, GDB_BAD_ARGUMENTS);
261		return;
262	}
263
264	struct ARMCore* cpu = stub->d.core->cpu;
265	for (i = 0; i < size; ++i, readAddress += 2) {
266		uint8_t byte = _hex2int(readAddress, 2);
267		GBAPatch8(cpu, address + i, byte, 0);
268	}
269
270	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
271	_sendMessage(stub);
272}
273
274static void _readMemory(struct GDBStub* stub, const char* message) {
275	const char* readAddress = message;
276	unsigned i = 0;
277	uint32_t address = _readHex(readAddress, &i);
278	readAddress += i + 1;
279	uint32_t size = _readHex(readAddress, &i);
280	if (size > 512) {
281		_error(stub, GDB_BAD_ARGUMENTS);
282		return;
283	}
284	struct ARMCore* cpu = stub->d.core->cpu;
285	int writeAddress = 0;
286	for (i = 0; i < size; ++i, writeAddress += 2) {
287		uint8_t byte = cpu->memory.load8(cpu, address + i, 0);
288		_int2hex8(byte, &stub->outgoing[writeAddress]);
289	}
290	stub->outgoing[writeAddress] = 0;
291	_sendMessage(stub);
292}
293
294static void _writeGPRs(struct GDBStub* stub, const char* message) {
295	struct ARMCore* cpu = stub->d.core->cpu;
296	const char* readAddress = message;
297
298	int r;
299	for (r = 0; r <= ARM_PC; ++r) {
300		cpu->gprs[r] = _hex2int(readAddress, 8);
301		readAddress += 8;
302	}
303	int32_t currentCycles = 0;
304	if (cpu->executionMode == MODE_ARM) {
305		ARM_WRITE_PC;
306	} else {
307		THUMB_WRITE_PC;
308	}
309
310	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
311	_sendMessage(stub);
312}
313
314static void _readGPRs(struct GDBStub* stub, const char* message) {
315	struct ARMCore* cpu = stub->d.core->cpu;
316	UNUSED(message);
317	int r;
318	int i = 0;
319	for (r = 0; r < ARM_PC; ++r) {
320		_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
321		i += 8;
322	}
323	_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
324	i += 8;
325
326	stub->outgoing[i] = 0;
327	_sendMessage(stub);
328}
329
330static void _writeRegister(struct GDBStub* stub, const char* message) {
331	struct ARMCore* cpu = stub->d.core->cpu;
332	const char* readAddress = message;
333
334	unsigned i = 0;
335	uint32_t reg = _readHex(readAddress, &i);
336	readAddress += i + 1;
337
338	uint32_t value = _readHex(readAddress, &i);
339
340#ifdef _MSC_VER
341	value = _byteswap_ulong(value);
342#else
343	value = __builtin_bswap32(value);
344#endif
345
346	if (reg <= ARM_PC) {
347		cpu->gprs[reg] = value;
348		if (reg == ARM_PC) {
349			int32_t currentCycles = 0;
350			if (cpu->executionMode == MODE_ARM) {
351				ARM_WRITE_PC;
352			} else {
353				THUMB_WRITE_PC;
354			}
355		}
356	} else if (reg == 0x19) {
357		cpu->cpsr.packed = value;
358	} else {
359		stub->outgoing[0] = '\0';
360		_sendMessage(stub);
361		return;
362	}
363
364	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
365	_sendMessage(stub);
366}
367
368static void _readRegister(struct GDBStub* stub, const char* message) {
369	struct ARMCore* cpu = stub->d.core->cpu;
370	const char* readAddress = message;
371	unsigned i = 0;
372	uint32_t reg = _readHex(readAddress, &i);
373	uint32_t value;
374	if (reg < 0x10) {
375		value = cpu->gprs[reg];
376	} else if (reg == 0x19) {
377		value = cpu->cpsr.packed;
378	} else {
379		stub->outgoing[0] = '\0';
380		_sendMessage(stub);
381		return;
382	}
383	_int2hex32(value, stub->outgoing);
384	stub->outgoing[8] = '\0';
385	_sendMessage(stub);
386}
387
388static void _processQReadCommand(struct GDBStub* stub, const char* message) {
389	stub->outgoing[0] = '\0';
390	if (!strncmp("HostInfo#", message, 9)) {
391		_writeHostInfo(stub);
392		return;
393	}
394	if (!strncmp("Attached#", message, 9)) {
395		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
396	} else if (!strncmp("VAttachOrWaitSupported#", message, 23)) {
397		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
398	} else if (!strncmp("C#", message, 2)) {
399		strncpy(stub->outgoing, "QC1", GDB_STUB_MAX_LINE - 4);
400	} else if (!strncmp("fThreadInfo#", message, 12)) {
401		strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
402	} else if (!strncmp("sThreadInfo#", message, 12)) {
403		strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
404	}
405	_sendMessage(stub);
406}
407
408static void _processQWriteCommand(struct GDBStub* stub, const char* message) {
409	stub->outgoing[0] = '\0';
410	if (!strncmp("StartNoAckMode#", message, 16)) {
411		stub->lineAck = GDB_ACK_OFF;
412		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
413	}
414	_sendMessage(stub);
415}
416
417static void _processVWriteCommand(struct GDBStub* stub, const char* message) {
418	UNUSED(message);
419	stub->outgoing[0] = '\0';
420	_sendMessage(stub);
421}
422
423static void _processVReadCommand(struct GDBStub* stub, const char* message) {
424	stub->outgoing[0] = '\0';
425	if (!strncmp("Attach", message, 6)) {
426		strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
427		mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
428	}
429	_sendMessage(stub);
430}
431
432static void _setBreakpoint(struct GDBStub* stub, const char* message) {
433	const char* readAddress = &message[2];
434	unsigned i = 0;
435	uint32_t address = _readHex(readAddress, &i);
436	readAddress += i + 1;
437	uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
438	UNUSED(kind);
439
440	switch (message[0]) {
441	case '0': // Memory breakpoints are not currently supported
442	case '1':
443		stub->d.platform->setBreakpoint(stub->d.platform, address);
444		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
445		_sendMessage(stub);
446		break;
447	case '2':
448		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
449		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
450		_sendMessage(stub);
451		break;
452	case '3':
453		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
454		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
455		_sendMessage(stub);
456		break;
457	case '4':
458		stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
459		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
460		_sendMessage(stub);
461		break;
462	default:
463		stub->outgoing[0] = '\0';
464		_sendMessage(stub);
465		break;
466	}
467}
468
469static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
470	const char* readAddress = &message[2];
471	unsigned i = 0;
472	uint32_t address = _readHex(readAddress, &i);
473	switch (message[0]) {
474	case '0': // Memory breakpoints are not currently supported
475	case '1':
476		stub->d.platform->clearBreakpoint(stub->d.platform, address);
477		break;
478	case '2':
479	case '3':
480	case '4':
481		stub->d.platform->clearWatchpoint(stub->d.platform, address);
482		break;
483	default:
484		break;
485	}
486	strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
487	_sendMessage(stub);
488}
489
490size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
491	uint8_t checksum = 0;
492	int parsed = 1;
493	switch (*message) {
494	case '+':
495		stub->lineAck = GDB_ACK_RECEIVED;
496		return parsed;
497	case '-':
498		stub->lineAck = GDB_NAK_RECEIVED;
499		return parsed;
500	case '$':
501		++message;
502		break;
503	case '\x03':
504		mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
505		return parsed;
506	default:
507		_nak(stub);
508		return parsed;
509	}
510
511	int i;
512	char messageType = message[0];
513	for (i = 0; message[i] != '#'; ++i, ++parsed) {
514		checksum += message[i];
515	}
516	if (!message[i]) {
517		_nak(stub);
518		return parsed;
519	}
520	++i;
521	++parsed;
522	if (!message[i]) {
523		_nak(stub);
524		return parsed;
525	} else if (!message[i + 1]) {
526		++parsed;
527		_nak(stub);
528		return parsed;
529	}
530	parsed += 2;
531	int networkChecksum = _hex2int(&message[i], 2);
532	if (networkChecksum != checksum) {
533		mLOG(DEBUGGER, WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum);
534		_nak(stub);
535		return parsed;
536	}
537
538	_ack(stub);
539	++message;
540	switch (messageType) {
541	case '?':
542		snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
543		_sendMessage(stub);
544		break;
545	case 'c':
546		_continue(stub, message);
547		break;
548	case 'G':
549		_writeGPRs(stub, message);
550		break;
551	case 'g':
552		_readGPRs(stub, message);
553		break;
554	case 'H':
555		// This is faked because we only have one thread
556		strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
557		_sendMessage(stub);
558		break;
559	case 'M':
560		_writeMemory(stub, message);
561		break;
562	case 'm':
563		_readMemory(stub, message);
564		break;
565	case 'P':
566		_writeRegister(stub, message);
567		break;
568	case 'p':
569		_readRegister(stub, message);
570		break;
571	case 'Q':
572		_processQWriteCommand(stub, message);
573		break;
574	case 'q':
575		_processQReadCommand(stub, message);
576		break;
577	case 's':
578		_step(stub, message);
579		break;
580	case 'V':
581		_processVWriteCommand(stub, message);
582		break;
583	case 'v':
584		_processVReadCommand(stub, message);
585		break;
586	case 'X':
587		_writeMemoryBinary(stub, message);
588                break;
589	case 'Z':
590		_setBreakpoint(stub, message);
591		break;
592	case 'z':
593		_clearBreakpoint(stub, message);
594		break;
595	default:
596		_error(stub, GDB_UNSUPPORTED_COMMAND);
597		break;
598	}
599	return parsed;
600}
601
602void GDBStubCreate(struct GDBStub* stub) {
603	stub->socket = INVALID_SOCKET;
604	stub->connection = INVALID_SOCKET;
605	stub->d.init = 0;
606	stub->d.deinit = _gdbStubDeinit;
607	stub->d.paused = _gdbStubWait;
608	stub->d.entered = _gdbStubEntered;
609	stub->d.custom = _gdbStubPoll;
610	stub->untilPoll = GDB_STUB_INTERVAL;
611	stub->lineAck = GDB_ACK_PENDING;
612	stub->shouldBlock = false;
613}
614
615bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
616	if (!SOCKET_FAILED(stub->socket)) {
617		GDBStubShutdown(stub);
618	}
619	stub->socket = SocketOpenTCP(port, bindAddress);
620	if (SOCKET_FAILED(stub->socket)) {
621		mLOG(DEBUGGER, ERROR, "Couldn't open socket");
622		return false;
623	}
624	if (!SocketSetBlocking(stub->socket, false)) {
625		goto cleanup;
626	}
627	int err = SocketListen(stub->socket, 1);
628	if (err) {
629		goto cleanup;
630	}
631
632	return true;
633
634cleanup:
635	mLOG(DEBUGGER, ERROR, "Couldn't listen on port");
636	SocketClose(stub->socket);
637	stub->socket = INVALID_SOCKET;
638	return false;
639}
640
641void GDBStubHangup(struct GDBStub* stub) {
642	if (!SOCKET_FAILED(stub->connection)) {
643		SocketClose(stub->connection);
644		stub->connection = INVALID_SOCKET;
645	}
646	if (stub->d.state == DEBUGGER_PAUSED) {
647		stub->d.state = DEBUGGER_RUNNING;
648	}
649}
650
651void GDBStubShutdown(struct GDBStub* stub) {
652	GDBStubHangup(stub);
653	if (!SOCKET_FAILED(stub->socket)) {
654		SocketClose(stub->socket);
655		stub->socket = INVALID_SOCKET;
656	}
657}
658
659void GDBStubUpdate(struct GDBStub* stub) {
660	if (stub->socket == INVALID_SOCKET) {
661		if (stub->d.state == DEBUGGER_PAUSED) {
662			stub->d.state = DEBUGGER_RUNNING;
663		}
664		return;
665	}
666	if (stub->connection == INVALID_SOCKET) {
667		if (stub->shouldBlock) {
668			Socket reads = stub->socket;
669			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
670		}
671		stub->connection = SocketAccept(stub->socket, 0);
672		if (!SOCKET_FAILED(stub->connection)) {
673			if (!SocketSetBlocking(stub->connection, false)) {
674				goto connectionLost;
675			}
676			mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
677		} else if (SocketWouldBlock()) {
678			return;
679		} else {
680			goto connectionLost;
681		}
682	}
683	while (true) {
684		if (stub->shouldBlock) {
685			Socket reads = stub->connection;
686			SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
687		}
688		ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
689		if (messageLen == 0) {
690			goto connectionLost;
691		}
692		if (messageLen == -1) {
693			if (SocketWouldBlock()) {
694				return;
695			}
696			goto connectionLost;
697		}
698		stub->line[messageLen] = '\0';
699		mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
700		ssize_t position = 0;
701		while (position < messageLen) {
702			position += _parseGDBMessage(stub, &stub->line[position]);
703		}
704	}
705
706connectionLost:
707	mLOG(DEBUGGER, WARN, "Connection lost");
708	GDBStubHangup(stub);
709}