Skip to content

Commit 4dcf79b

Browse files
committed
spectranext: support for logging under debugger; monitor commands
1 parent 7ff24b0 commit 4dcf79b

8 files changed

Lines changed: 231 additions & 68 deletions

File tree

debugger/gdbserver.c

Lines changed: 134 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "debugger.h"
55
#include "gdbserver.h"
6+
#include "gdbserver_remote_commands.h"
67
#include "packets.h"
78
#include "arch.h"
89
#include "gdbserver_utils.h"
@@ -36,14 +37,12 @@
3637
#include <sys/types.h>
3738
#include <semaphore.h>
3839

39-
typedef uint8_t (*trapped_action_t)(const void* data, void* response);
40-
4140
static int gdbserver_socket;
4241
int gdbserver_client_socket = -1; // Made non-static so packets.c can access it
4342
uint8_t tmpbuf[0x20000]; // Made non-static so vfile_ext.c can access it
4443
static volatile char gdbserver_trapped = 0;
45-
static volatile char gdbserver_do_not_report_trap = 0;
4644
static int gdbserver_port = 0;
45+
static int gdbserver_last_trap_reason = DEBUG_TRAP_REASON_SIGNAL_RECEIVED;
4746

4847
static pthread_t network_thread_id;
4948
static trapped_action_t scheduled_action = NULL;
@@ -54,7 +53,9 @@ static uint8_t scheduled_action_response_delivered = 0;
5453
static pthread_cond_t trapped_cond;
5554
static pthread_cond_t response_cond;
5655
static pthread_mutex_t trap_process_mutex;
57-
static pthread_mutex_t network_mutex;
56+
static pthread_mutex_t network_read_mutex;
57+
static pthread_mutex_t network_write_mutex;
58+
static int gdbserver_reset_event = -1;
5859

5960
/** vSpectranext autoboot: if true at machine_reset, apply ram mount + autoboot once, then clear. */
6061
static bool spectranext_autoboot = false;
@@ -77,7 +78,38 @@ static libspectrum_word* registers[] = {
7778
};
7879

7980
static uint8_t gdbserver_detrap();
80-
static uint8_t gdbserver_execute_on_main_thread(trapped_action_t call, const void* data, void* response);
81+
static void gdbserver_send_stop_reply(int trap_reason);
82+
83+
void gdbserver_lock_network_write(void)
84+
{
85+
pthread_mutex_lock(&network_write_mutex);
86+
}
87+
88+
void gdbserver_unlock_network_write(void)
89+
{
90+
pthread_mutex_unlock(&network_write_mutex);
91+
}
92+
93+
static void gdbserver_send_stop_reply(int trap_reason)
94+
{
95+
char tbuf[64];
96+
int signal;
97+
98+
switch (trap_reason)
99+
{
100+
case DEBUG_TRAP_REASON_BREAKPOINT:
101+
signal = 5; /* SIGTRAP */
102+
break;
103+
case DEBUG_TRAP_REASON_SIGNAL_RECEIVED:
104+
case DEBUG_TRAP_REASON_OTHER:
105+
default:
106+
signal = 2; /* SIGINT */
107+
break;
108+
}
109+
110+
sprintf(tbuf, "T%02xthread:p%02x.%02x;", signal, 1, 1);
111+
packet_send_message((const uint8_t*)tbuf, strlen(tbuf));
112+
}
81113

82114
static uint8_t action_get_registers(const void* arg, void* response);
83115
static uint8_t action_set_registers(const void* arg, void* response);
@@ -88,7 +120,6 @@ static uint8_t action_set_register(const void* arg, void* response);
88120
static uint8_t action_set_breakpoint(const void* arg, void* response);
89121
static uint8_t action_remove_breakpoint(const void* arg, void* response);
90122
static uint8_t action_step_instruction(const void* arg, void* response);
91-
static uint8_t action_reset(const void* arg, void* response);
92123
static uint8_t action_autoboot(const void* arg, void* response);
93124

94125
struct action_mem_args_t {
@@ -113,6 +144,75 @@ struct action_breakpoint_args_t {
113144
size_t maddr, mlen;
114145
};
115146

147+
void gdbserver_send_remote_console_output(const char *text)
148+
{
149+
size_t len = strlen(text);
150+
151+
if (gdbserver_client_socket == -1 || !gdbserver_debugging_enabled)
152+
return;
153+
154+
if (1 + len * 2 >= sizeof(tmpbuf))
155+
len = (sizeof(tmpbuf) - 1) / 2;
156+
157+
tmpbuf[0] = 'O';
158+
mem2hex((const uint8_t *)text, (char *)&tmpbuf[1], (int)len);
159+
packet_send_message(tmpbuf, 1 + len * 2);
160+
}
161+
162+
static int decode_remote_command(const char *hex_command, char *command, size_t command_size)
163+
{
164+
size_t hex_len = strlen(hex_command);
165+
166+
if ((hex_len & 1) != 0)
167+
return 0;
168+
169+
if (hex_len / 2 >= command_size)
170+
return 0;
171+
172+
hex2mem(hex_command, (uint8_t *)command, (int)(hex_len / 2));
173+
command[hex_len / 2] = '\0';
174+
return 1;
175+
}
176+
177+
static void process_remote_command(const char *hex_command)
178+
{
179+
const struct remote_command_entry_t *entry;
180+
char command[256];
181+
182+
if (!decode_remote_command(hex_command, command, sizeof(command))) {
183+
packet_send_message((const uint8_t *)"E01", 3);
184+
return;
185+
}
186+
187+
for (entry = remote_commands; entry->name; entry++) {
188+
if (!strcmp(command, entry->name)) {
189+
if (entry->handler())
190+
packet_send_message((const uint8_t *)"E01", 3);
191+
else
192+
packet_send_message((const uint8_t *)"OK", 2);
193+
return;
194+
}
195+
}
196+
197+
packet_send_message((const uint8_t *)"E04", 3);
198+
}
199+
200+
static void gdbserver_reset_event_fn(libspectrum_dword event_time, int type,
201+
void *user_data)
202+
{
203+
(void)event_time;
204+
(void)type;
205+
(void)user_data;
206+
207+
machine_reset(0);
208+
}
209+
210+
uint8_t gdbserver_reset_via_remote_command(void)
211+
{
212+
gdbserver_schedule_reset();
213+
return 0;
214+
}
215+
116216
static void process_xfer(const char *name, char *args)
117217
{
118218
const char *mode = args;
@@ -132,6 +232,11 @@ static void process_query(char *payload)
132232
const char *name;
133233
char *args;
134234

235+
if (payload == strstr(payload, "Rcmd,")) {
236+
process_remote_command(payload + 5);
237+
return;
238+
}
239+
135240
args = strchr(payload, ':');
136241
if (args)
137242
*args++ = '\0';
@@ -464,13 +569,11 @@ uint8_t process_packet()
464569
{
465570
if (gdbserver_trapped)
466571
{
467-
char tbuf[64];
468-
sprintf(tbuf, "T%02xthread:p%02x.%02x;", 5, 1, 1);
469-
packet_send_message((const uint8_t*)tbuf, strlen(tbuf));
572+
gdbserver_send_stop_reply(gdbserver_last_trap_reason);
470573
}
471574
else
472575
{
473-
packet_send_message((const uint8_t*)"OK", 2);
576+
debugger_mode = DEBUGGER_MODE_HALTED;
474577
}
475578
break;
476579
}
@@ -486,18 +589,18 @@ uint8_t process_packet()
486589

487590
static int process_network(int socket)
488591
{
489-
pthread_mutex_lock(&network_mutex);
592+
pthread_mutex_lock(&network_read_mutex);
490593

491594
// Read raw bytes from socket
492595
int bytes_read = packets_read_socket(socket);
493596
if (bytes_read < 0)
494597
{
495-
pthread_mutex_unlock(&network_mutex);
598+
pthread_mutex_unlock(&network_read_mutex);
496599
return -1; // Error or EOF
497600
}
498601
if (bytes_read == 0)
499602
{
500-
pthread_mutex_unlock(&network_mutex);
603+
pthread_mutex_unlock(&network_read_mutex);
501604
return 0; // No data available
502605
}
503606

@@ -519,7 +622,7 @@ static int process_network(int socket)
519622

520623
// Clear consumed bytes
521624
packets_clear_incoming_raw();
522-
pthread_mutex_unlock(&network_mutex);
625+
pthread_mutex_unlock(&network_read_mutex);
523626
return 0;
524627
}
525628
else if (result == PACKETS_FEED_INTERRUPT)
@@ -535,7 +638,7 @@ static int process_network(int socket)
535638

536639
// All bytes consumed but no complete packet yet
537640
packets_clear_incoming_raw();
538-
pthread_mutex_unlock(&network_mutex);
641+
pthread_mutex_unlock(&network_read_mutex);
539642

540643
return 0;
541644
}
@@ -549,11 +652,11 @@ static void* network_thread(void* arg)
549652
timeout.tv_sec = 0;
550653
timeout.tv_usec = 500000;
551654

552-
pthread_mutex_lock(&network_mutex);
655+
pthread_mutex_lock(&network_read_mutex);
553656

554657
if (gdbserver_socket < 0)
555658
{
556-
pthread_mutex_unlock(&network_mutex);
659+
pthread_mutex_unlock(&network_read_mutex);
557660
break;
558661
}
559662

@@ -563,10 +666,10 @@ static void* network_thread(void* arg)
563666

564667
if (select(gdbserver_socket + 1, &set, NULL, NULL, &timeout) <= 0)
565668
{
566-
pthread_mutex_unlock(&network_mutex);
669+
pthread_mutex_unlock(&network_read_mutex);
567670
continue;
568671
}
569-
pthread_mutex_unlock(&network_mutex);
672+
pthread_mutex_unlock(&network_read_mutex);
570673
}
571674

572675
socklen_t socklen;
@@ -592,17 +695,6 @@ static void* network_thread(void* arg)
592695
// Reset packets subsystem for new connection
593696
packets_reset();
594697
packets_clear_incoming_raw();
595-
gdbserver_do_not_report_trap = 1;
596-
debugger_mode = DEBUGGER_MODE_HALTED;
597-
598-
// property wait for it to trap
599-
pthread_mutex_lock(&trap_process_mutex);
600-
while (!gdbserver_trapped)
601-
{
602-
pthread_cond_wait(&trapped_cond, &trap_process_mutex);
603-
}
604-
pthread_mutex_unlock(&trap_process_mutex);
605-
606698

607699
int ret;
608700
while ((ret = process_network(gdbserver_client_socket)) == 0) ;
@@ -624,10 +716,13 @@ void gdbserver_init()
624716
pthread_cond_init(&trapped_cond, NULL);
625717
pthread_cond_init(&response_cond, NULL);
626718
pthread_mutex_init(&trap_process_mutex, NULL);
627-
pthread_mutex_init(&network_mutex, NULL);
719+
pthread_mutex_init(&network_read_mutex, NULL);
720+
pthread_mutex_init(&network_write_mutex, NULL);
628721

629722
// Initialize packets subsystem
630723
packets_init();
724+
gdbserver_reset_event =
725+
event_register(gdbserver_reset_event_fn, "GDB server reset");
631726

632727
gdbserver_refresh_status();
633728
}
@@ -737,14 +832,14 @@ void gdbserver_stop()
737832
return;
738833
}
739834

740-
pthread_mutex_lock(&network_mutex);
835+
pthread_mutex_lock(&network_read_mutex);
741836
if (gdbserver_socket != -1)
742837
{
743838
compat_socket_close(gdbserver_socket);
744839
gdbserver_socket = -1;
745840
}
746841

747-
pthread_mutex_unlock(&network_mutex);
842+
pthread_mutex_unlock(&network_read_mutex);
748843

749844
gdbserver_debugging_enabled = 0;
750845
pthread_join(network_thread_id, NULL);
@@ -753,7 +848,8 @@ void gdbserver_stop()
753848

754849
void gdbserver_schedule_reset(void)
755850
{
756-
gdbserver_execute_on_main_thread(action_reset, NULL, NULL);
851+
if (gdbserver_reset_event >= 0)
852+
event_add(tstates + 1, gdbserver_reset_event);
757853
}
758854

759855
void gdbserver_schedule_autoboot(void)
@@ -821,7 +917,7 @@ static uint8_t gdbserver_detrap()
821917
// schedule a simple job (call) on the main thread, while it's trapped
822918
// data and response are supposed to be located on the stack of the caller (network) thread,
823919
// as it's going to be stopped until the response is there
824-
static uint8_t gdbserver_execute_on_main_thread(trapped_action_t call, const void* data, void* response)
920+
uint8_t gdbserver_execute_on_main_thread(trapped_action_t call, const void* data, void* response)
825921
{
826922
pthread_mutex_lock(&trap_process_mutex);
827923

@@ -1013,14 +1109,6 @@ static uint8_t action_remove_breakpoint(const void* arg, void* response)
10131109
return 0;
10141110
}
10151111

1016-
static uint8_t action_reset(const void* arg, void* response)
1017-
{
1018-
(void)arg;
1019-
(void)response;
1020-
machine_reset(0);
1021-
return 0;
1022-
}
1023-
10241112
void gdbserver_on_machine_reset(void)
10251113
{
10261114
#ifdef BUILD_SPECTRANET
@@ -1080,31 +1168,11 @@ int gdbserver_activate()
10801168

10811169
int gdbserver_activate_with_reason(int trap_reason)
10821170
{
1083-
// printf("Execution stopped: trapped.\n");
1084-
1085-
if (gdbserver_do_not_report_trap == 0)
1086-
{
1087-
// notify the gdb client that we have trapped
1088-
char tbuf[64];
1089-
int signal;
1090-
switch (trap_reason)
1091-
{
1092-
case DEBUG_TRAP_REASON_BREAKPOINT:
1093-
signal = 5; // SIGTRAP
1094-
break;
1095-
case DEBUG_TRAP_REASON_SIGNAL_RECEIVED:
1096-
case DEBUG_TRAP_REASON_OTHER:
1097-
default:
1098-
signal = 2; // SIGINT
1099-
break;
1100-
}
1101-
sprintf(tbuf, "T%02xthread:p%02x.%02x;", signal, 1, 1);
1102-
packet_send_message((const uint8_t*)tbuf, strlen(tbuf));
1103-
}
1171+
if (gdbserver_client_socket != -1 && gdbserver_debugging_enabled)
1172+
gdbserver_send_stop_reply(trap_reason);
11041173

11051174
pthread_mutex_lock(&trap_process_mutex);
1106-
1107-
gdbserver_do_not_report_trap = 0;
1175+
gdbserver_last_trap_reason = trap_reason;
11081176
gdbserver_trapped = 1;
11091177
pthread_cond_signal(&trapped_cond);
11101178

debugger/gdbserver.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#ifndef FUSE_DEBUGGER_GDBSERVER_H
33
#define FUSE_DEBUGGER_GDBSERVER_H
44

5+
#include <stdint.h>
6+
57
// Trap reasons for GDB RSP protocol
68
#define DEBUG_TRAP_REASON_SIGNAL_RECEIVED 0
79
#define DEBUG_TRAP_REASON_BREAKPOINT 1
@@ -15,6 +17,13 @@ int gdbserver_activate_with_reason(int trap_reason);
1517
void gdbserver_refresh_status();
1618
void gdbserver_schedule_reset(void);
1719
void gdbserver_schedule_autoboot(void);
20+
void gdbserver_send_remote_console_output(const char *text);
21+
uint8_t gdbserver_reset_via_remote_command(void);
22+
void gdbserver_lock_network_write(void);
23+
void gdbserver_unlock_network_write(void);
24+
25+
typedef uint8_t (*trapped_action_t)(const void* data, void* response);
26+
uint8_t gdbserver_execute_on_main_thread(trapped_action_t call, const void* data, void* response);
1827

1928
/** Called from machine_reset() — applies one-shot Spectranext autoboot config or turns autoboot off. */
2029
void gdbserver_on_machine_reset(void);

0 commit comments

Comments
 (0)