Files
rtems/cpukit/libdebugger/rtems-debugger-server.c
Kinsey Moore 1240e8f81b cpukit/libdebugger: Add support for TLS variables
This adds support for the "vGetTLSAddr" GDB query which retrieves the
address of a TLS variable by offset from the beginning of the TLS memory
space for the given thread. This offset does not include the size of the
thread control block which is at the beginning of every TLS area as used
by RTEMS. Notably, the returned address is big-endian rather than the
little-endian typical with other responses.

This functionality does not include retrieval of addresses for TLS
variables hosted in loadable modules, only TLS variables in the host
binary.

Closes #5271
2025-07-24 20:31:40 -05:00

2184 lines
56 KiB
C

/*
* Copyright (c) 2016-2019 Chris Johns <chrisj@rtems.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define RTEMS_DEBUGGER_VERBOSE_LOCK 0
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <rtems/bspIo.h>
#include <rtems/score/smp.h>
#include <rtems/score/tls.h>
#include <rtems/rtems-debugger.h>
#include <rtems/debugger/rtems-debugger-server.h>
#include <rtems/debugger/rtems-debugger-remote.h>
#include "rtems-debugger-target.h"
#include "rtems-debugger-threads.h"
/*
* GDB Debugger Remote Server for RTEMS.
*/
/*
* Command lookup table.
*/
typedef int (*rtems_debugger_command)(uint8_t* buffer, int size);
typedef struct rtems_debugger_packet
{
const char* const label;
rtems_debugger_command command;
} rtems_debugger_packet;
/**
* Common error strings.
*/
static const char* const r_OK = "OK";
static const char* const r_E01 = "E01";
/*
* Global Debugger.
*
* The server instance is allocated on the heap so memory is only used then the
* server is running. A global is used because:
*
* 1. There can only be a single instance at once.
* 2. The backend's need access to the data and holding pointers in the TCB
* for each thread is mess.
* 3. The code is smaller and faster.
*/
rtems_debugger_server* rtems_debugger;
/**
* Print lock ot make the prints sequential. This is to debug the debugger in
* SMP.
*/
RTEMS_INTERRUPT_LOCK_DEFINE(static, printk_lock, "printk_lock")
void
rtems_debugger_printk_lock(rtems_interrupt_lock_context* lock_context)
{
rtems_interrupt_lock_acquire(&printk_lock, lock_context);
}
void
rtems_debugger_printk_unlock(rtems_interrupt_lock_context* lock_context)
{
rtems_interrupt_lock_release(&printk_lock, lock_context);
}
int
rtems_debugger_clean_printf(const char* format, ...)
{
rtems_interrupt_lock_context lock_context;
int len;
va_list ap;
va_start(ap, format);
rtems_debugger_printk_lock(&lock_context);
len = vprintk(format, ap);
rtems_debugger_printk_unlock(&lock_context);
va_end(ap);
return len;
}
int
rtems_debugger_printf(const char* format, ...)
{
rtems_interrupt_lock_context lock_context;
int len;
va_list ap;
va_start(ap, format);
rtems_debugger_printk_lock(&lock_context);
printk("[CPU:%d] ", (int) _SMP_Get_current_processor ());
len = vprintk(format, ap);
rtems_debugger_printk_unlock(&lock_context);
va_end(ap);
return len;
}
bool
rtems_debugger_verbose(void)
{
return rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE);
}
static inline int
hex_decode(uint8_t ch)
{
int i;
if (ch >= '0' && ch <= '9')
i = (int) (ch - '0');
else if (ch >= 'a' && ch <= 'f')
i = (int) (ch - 'a') + 10;
else if (ch >= 'A' && ch <= 'F')
i = (int) (ch - 'A') + 10;
else
i = -1;
return i;
}
static inline uint8_t
hex_encode(int val)
{
return "0123456789abcdef"[val & 0xf];
}
static inline uintptr_t
hex_decode_addr(const uint8_t* data)
{
uintptr_t ui = 0;
size_t i;
if (data[0] == '-') {
if (data[1] == '1')
ui = (uintptr_t) -1;
}
else {
for (i = 0; i < (sizeof(ui) * 2); ++i) {
int v = hex_decode(data[i]);
if (v < 0)
break;
ui = (ui << 4) | v;
}
}
return ui;
}
static inline DB_UINT
hex_decode_uint(const uint8_t* data)
{
DB_UINT ui = 0;
size_t i;
if (data[0] == '-') {
if (data[1] == '1')
ui = (DB_UINT) -1;
}
else {
for (i = 0; i < (sizeof(ui) * 2); ++i) {
int v = hex_decode(data[i]);
if (v < 0)
break;
ui = (ui << 4) | v;
}
}
return ui;
}
static inline int
hex_decode_int(const uint8_t* data)
{
return (int) hex_decode_uint(data);
}
static bool
thread_id_decode(const char* data, DB_UINT* pid, DB_UINT* tid)
{
bool is_extended = false;
if (*data == 'p') {
is_extended = true;
++data;
}
*pid = *tid = hex_decode_uint((const uint8_t*) data);
if (is_extended) {
const char* stop = strchr(data, '.');
if (stop != NULL) {
*tid = hex_decode_uint((const uint8_t*) stop + 1);
}
}
return is_extended;
}
static inline bool
check_pid(DB_UINT pid)
{
return pid == 0 || rtems_debugger->pid == (pid_t) pid;
}
void
rtems_debugger_lock(void)
{
_Mutex_recursive_Acquire(&rtems_debugger->lock);
}
void
rtems_debugger_unlock(void)
{
_Mutex_recursive_Release(&rtems_debugger->lock);
}
static int
rtems_debugger_lock_create(void)
{
_Mutex_recursive_Initialize_named(&rtems_debugger->lock, "DBlock");
return 0;
}
static int
rtems_debugger_lock_destroy(void)
{
return 0;
}
static int
rtems_debugger_task_create(const char* name,
rtems_task_priority priority,
size_t stack_size,
rtems_task_entry entry_point,
rtems_task_argument argument,
rtems_id* id)
{
rtems_name tname;
rtems_status_code sc;
tname = rtems_build_name(name[0], name[1], name[2], name[3]);
sc = rtems_task_create (tname,
priority,
stack_size,
RTEMS_PREEMPT | RTEMS_NO_ASR,
RTEMS_LOCAL | RTEMS_FLOATING_POINT,
id);
if (sc != RTEMS_SUCCESSFUL) {
*id = 0;
rtems_debugger_printf("error: rtems-db: thread create: %s: %s\n",
name, rtems_status_text(sc));
errno = EIO;
return -1;
}
sc = rtems_task_start(*id, entry_point, argument);
if (sc != RTEMS_SUCCESSFUL) {
rtems_debugger_printf("error: rtems-db: thread start: %s: %s\n",
name, rtems_status_text(sc));
rtems_task_delete(*id);
*id = 0;
errno = EIO;
return -1;
}
return 0;
}
static int
rtems_debugger_task_destroy(const char* name,
rtems_id id,
volatile bool* finished,
int timeout)
{
while (timeout) {
bool has_finished;
rtems_debugger_lock();
has_finished = *finished;
rtems_debugger_unlock();
if (has_finished)
break;
usleep(RTEMS_DEBUGGER_POLL_WAIT);
if (timeout < RTEMS_DEBUGGER_POLL_WAIT)
timeout = 0;
else
timeout -= RTEMS_DEBUGGER_POLL_WAIT;
}
if (timeout == 0) {
rtems_debugger_printf("rtems-db: %s not stopping, killing\n", name);
rtems_task_delete(id);
}
return 0;
}
bool
rtems_debugger_server_running(void)
{
bool running;
rtems_debugger_lock();
running = rtems_debugger->server_running;
rtems_debugger_unlock();
return running;
}
rtems_debugger_remote*
rtems_debugger_remote_handle(void)
{
rtems_debugger_remote* remote;
rtems_debugger_lock();
remote = rtems_debugger->remote;
rtems_debugger_unlock();
return remote;
}
bool
rtems_debugger_connected(void)
{
bool isconnected = false;
rtems_debugger_lock();
if (rtems_debugger->remote != NULL)
isconnected = rtems_debugger->remote->isconnected(rtems_debugger->remote);
rtems_debugger_unlock();
return isconnected;
}
bool
rtems_debugger_server_events_running(void)
{
return rtems_debugger->events_running;
}
void
rtems_debugger_server_events_signal(void)
{
_Condition_Signal(&rtems_debugger->server_cond);
}
static void
rtems_debugger_server_events_wait(void)
{
_Condition_Wait_recursive(&rtems_debugger->server_cond, &rtems_debugger->lock);
}
static int
rtems_debugger_remote_connect(void)
{
rtems_debugger_remote* remote = rtems_debugger_remote_handle();
if (remote != NULL) {
if (!remote->isconnected(remote))
return remote->connect(remote);
}
errno = EIO;
return -1;
}
static int
rtems_debugger_remote_disconnect(void)
{
rtems_debugger_remote* remote = rtems_debugger_remote_handle();
if (remote != NULL) {
if (remote->isconnected(remote))
return remote->disconnect(remote);
}
errno = EIO;
return -1;
}
static int
rtems_debugger_remote_receive(uint8_t* buffer, size_t size)
{
rtems_debugger_remote* remote = rtems_debugger_remote_handle();
ssize_t len = remote->read(remote, buffer, size);
if (len < 0 && errno != EAGAIN)
rtems_debugger_printf("rtems-db: read: (%d) %s\n",
errno, strerror(errno));
return (int) len;
}
static int
rtems_debugger_remote_send(void)
{
const uint8_t* buffer = rtems_debugger->output;
ssize_t size = rtems_debugger->output_level;
if (rtems_debugger->output_level > RTEMS_DEBUGGER_BUFFER_SIZE) {
rtems_debugger_printf("rtems-db: write too big: %d\n",
(int) rtems_debugger->output_level);
return -1;
}
if (rtems_debugger->remote_debug) {
size_t i = 0;
rtems_debugger_printf("rtems-db: put:%4zu: ", rtems_debugger->output_level);
while (i < rtems_debugger->output_level)
rtems_debugger_clean_printf("%c", (char) rtems_debugger->output[i++]);
rtems_debugger_clean_printf("\n");
}
while (size) {
rtems_debugger_remote* remote = rtems_debugger_remote_handle();
ssize_t w;
if (remote == NULL) {
errno = EIO;
return -1;
}
w = remote->write(remote, buffer, size);
if (w < 0 && errno != EINTR) {
rtems_debugger_printf("rtems-db: write: (%d) %s\n",
errno, strerror(errno));
break;
}
else {
size -= w;
buffer += w;
}
}
return (int) rtems_debugger->output_level;
}
static int
rtems_debugger_remote_send_ack(void)
{
rtems_debugger->output[0] = '+';
rtems_debugger->output_level = 1;
return rtems_debugger_remote_send();
}
static int
rtems_debugger_remote_send_nack(void)
{
rtems_debugger->output[0] = '-';
rtems_debugger->output_level = 1;
return rtems_debugger_remote_send();
}
static int
rtems_debugger_remote_packet_in(void)
{
uint8_t buf[256];
uint8_t state;
int in = 0;
uint8_t csum = 0;
uint8_t rx_csum = 0;
bool junk = false;
bool escaped = false;
bool remote_debug_header = true;
/*
* States:
* 'H' : Looking for the start character '$', '-' or '+'.
* 'P' : Looking for the checksum character '#' else buffer data.
* '1' : Looking for the first checksum character.
* '2' : Looking for the second checksum character.
* 'F' : Finished.
*/
state = 'H';
while (state != 'F') {
int r;
int i;
rtems_debugger_unlock();
r = rtems_debugger_remote_receive(buf, sizeof(buf));
rtems_debugger_lock();
if (r <= 0) {
/*
* Timeout?
*/
if (r < 0 && errno == EAGAIN) {
if (rtems_debugger->ack_pending) {
rtems_debugger_remote_send();
}
continue;
}
if (r == 0)
rtems_debugger_printf("rtems-db: remote disconnected\n");
return -1;
}
i = 0;
while (i < r) {
uint8_t c = buf[i++];
if (rtems_debugger->remote_debug && remote_debug_header) {
rtems_debugger_printf("rtems-db: get:%4d: ", r);
remote_debug_header = false;
}
if (rtems_debugger->remote_debug)
rtems_debugger_clean_printf("%c", c);
switch (state) {
case 'H':
switch (c) {
case '+':
if (rtems_debugger->remote_debug) {
rtems_debugger_clean_printf(" [[ACK%s]]\n",
rtems_debugger->ack_pending ? "" : "?");
remote_debug_header = true;
}
rtems_debugger->ack_pending = false;
break;
case '-':
if (rtems_debugger->remote_debug) {
rtems_debugger_clean_printf(" [[NACK]]\n");
remote_debug_header = true;
}
/*
* Resend.
*/
rtems_debugger_remote_send();
break;
case '$':
state = 'P';
csum = 0;
in = 0;
if (junk && rtems_debugger->remote_debug) {
rtems_debugger_clean_printf("\b [[junk dropped]]\nrtems-db: get: : $");
remote_debug_header = false;
}
break;
case '\x3':
if (rtems_debugger->remote_debug)
rtems_debugger_clean_printf("^C [[BREAK]]\n");
rtems_debugger->ack_pending = false;
rtems_debugger->input[0] = '^';
rtems_debugger->input[1] = 'C';
rtems_debugger->input[2] = '\0';
return 2;
default:
junk = true;
break;
}
break;
case 'P':
if (c == '{' && !escaped) {
escaped = true;
}
else if (c == '$' && !escaped) {
csum = 0;
in = 0;
if (rtems_debugger->remote_debug) {
rtems_debugger_clean_printf("\n");
remote_debug_header = true;
}
}
else if (c == '#' && !escaped) {
rtems_debugger->input[in] = '\0';
rx_csum = 0;
state = '1';
}
else {
if (in >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
rtems_debugger_printf("rtems-db: input buffer overflow\n");
return -1;
}
csum += c;
rtems_debugger->input[in++] = c;
}
break;
case '1':
rx_csum = (rx_csum << 4) | (uint8_t) hex_decode(c);
state = '2';
break;
case '2':
rx_csum = (rx_csum << 4) | (uint8_t) hex_decode(c);
if (csum == rx_csum) {
state = 'F';
if (rtems_debugger->remote_debug)
rtems_debugger_clean_printf("\n");
rtems_debugger_remote_send_ack();
}
else {
if (rtems_debugger->remote_debug) {
rtems_debugger_clean_printf(" [[invalid checksum]]\n");
remote_debug_header = true;
rtems_debugger_remote_send_nack();
}
state = 'H';
}
break;
case 'F':
if (rtems_debugger->remote_debug)
rtems_debugger_clean_printf(" [[extra data: 0x%02x]]", (int) c);
break;
default:
rtems_debugger_printf("rtems-db: bad state\n");
rtems_debugger_remote_send_nack();
return -1;
}
}
}
return in;
}
static int
rtems_debugger_remote_packet_in_hex(uint8_t* addr,
const char* data,
size_t size)
{
size_t i;
for (i = 0; i < size; ++i) {
*addr = (hex_decode(*data++) << 4);
*addr++ |= hex_decode(*data++);
}
return 0;
}
#if KEEP_INCASE
static void
remote_packet_out_rewind(size_t size)
{
size_t i = 0;
while (rtems_debugger->output_level > 0 && i < size) {
if (rtems_debugger->output_level > 1) {
if (rtems_debugger->output[rtems_debugger->output_level - 1] == '}') {
--rtems_debugger->output_level;
}
}
--rtems_debugger->output_level;
--i;
}
}
#endif
static int
remote_packet_out_append_buffer(const char* buffer, size_t size)
{
size_t ol = rtems_debugger->output_level;
size_t i = 0;
while (i < size) {
char c = buffer[i++];
if (c == '#' || c == '$') {
if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
rtems_debugger->output_level = ol;
rtems_debugger_printf("rtems-db: output overflow\n");
return -1;
}
rtems_debugger->output[rtems_debugger->output_level++] = '}';
c ^= 0x20;
}
if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 1)) {
rtems_debugger->output_level = ol;
rtems_debugger_printf("rtems-db: output overflow\n");
return -1;
}
rtems_debugger->output[rtems_debugger->output_level++] = c;
}
return 0;
}
static int
remote_packet_out_append_hex(const uint8_t* data, size_t size)
{
size_t ol = rtems_debugger->output_level;
size_t i = 0;
while (i < size) {
uint8_t byte = data[i++];
if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 2)) {
rtems_debugger->output_level = ol;
rtems_debugger_printf("rtems-db: output overflow\n");
return -1;
}
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte >> 4);
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte);
}
return 0;
}
/* output data in big-endian instead of little-endian */
static int
remote_packet_out_append_hex_be(const uint8_t* data, size_t size)
{
size_t ol = rtems_debugger->output_level;
size_t i = size;
while (i > 0) {
uint8_t byte = data[--i];
if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 2)) {
rtems_debugger->output_level = ol;
rtems_debugger_printf("rtems-db: output overflow\n");
return -1;
}
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte >> 4);
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(byte);
}
return 0;
}
static int
remote_packet_out_append_str(const char* str)
{
return remote_packet_out_append_buffer(str, strlen(str));
}
static int
remote_packet_out_append_vprintf(const char* fmt, va_list ap)
{
int len;
char buffer[64];
len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
return remote_packet_out_append_buffer(buffer, len);
}
static int
remote_packet_out_append(const char* fmt, ...)
{
va_list ap;
int r;
va_start(ap, fmt);
r = remote_packet_out_append_vprintf(fmt, ap);
va_end(ap);
return r;
}
static void
remote_packet_out_reset(void)
{
rtems_debugger->output_level = 1;
rtems_debugger->output[0] = '$';
}
static int
remote_packet_out_buffer(const char* buffer, size_t size)
{
remote_packet_out_reset();
return remote_packet_out_append_buffer(buffer, size);
}
static int
remote_packet_out_str(const char* str)
{
remote_packet_out_reset();
return remote_packet_out_append_buffer(str, strlen(str));
}
static int
remote_packet_out(const char* fmt, ...)
{
va_list ap;
int r;
va_start(ap, fmt);
remote_packet_out_reset();
r = remote_packet_out_append_vprintf(fmt, ap);
va_end(ap);
return r;
}
static int
remote_packet_out_send(void)
{
uint8_t csum = 0;
size_t i = 1;
if (rtems_debugger->output_level >= (RTEMS_DEBUGGER_BUFFER_SIZE - 3)) {
rtems_debugger_printf("rtems-db: output overflow\n");
return -1;
}
while (i < rtems_debugger->output_level) {
csum += rtems_debugger->output[i++];
}
rtems_debugger->output[rtems_debugger->output_level++] = '#';
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode((csum >> 4) & 0xf);
rtems_debugger->output[rtems_debugger->output_level++] = hex_encode(csum & 0xf);
rtems_debugger->ack_pending = true;;
return rtems_debugger_remote_send();
}
static int
remote_packet_dispatch(const rtems_debugger_packet* packet,
size_t packets,
uint8_t* buffer,
int size)
{
const rtems_debugger_packet* p;
size_t i;
int r = -1;
for (i = 0, p = &packet[0]; i < packets; ++i, ++p) {
if (strncmp(p->label,
(const char*) &buffer[0],
strlen(p->label)) == 0) {
if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE_CMDS))
rtems_debugger_printf("rtems-db: cmd: %s [%d] '%s'\n",
p->label, size, (const char*) buffer);
r = p->command(buffer, size);
break;
}
}
if (r < 0) {
remote_packet_out_buffer("", 0);
remote_packet_out_send();
}
return 0;
}
static int
remote_detach(uint8_t* buffer, int size)
{
remote_packet_out_str(r_OK);
remote_packet_out_send();
rtems_debugger_remote_disconnect();
return 0;
}
static int
remote_ut_features(uint8_t* buffer, int size)
{
return -1;
}
static int
remote_ut_osdata(uint8_t* buffer, int size)
{
return -1;
}
static const rtems_debugger_packet uninterpreted_transfer[] = {
{ .label = "qXfer:features",
.command = remote_ut_features },
{ .label = "qXfer:osdata",
.command = remote_ut_osdata },
};
#define REMOTE_UNINTERPRETED_TRANSFERS \
RTEMS_DEBUGGER_NUMOF(uninterpreted_transfer)
static int
remote_gq_uninterpreted_transfer(uint8_t* buffer, int size)
{
return remote_packet_dispatch(uninterpreted_transfer,
REMOTE_UNINTERPRETED_TRANSFERS,
buffer, size);
}
static int
remote_gq_thread_info_subsequent(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
if (threads->next >= threads->current.level)
remote_packet_out_str("l");
else {
rtems_debugger_thread* current;
const char* format = "p%d.%08lx";
current = rtems_debugger_thread_current(threads);
remote_packet_out_str("m");
while (threads->next < threads->current.level) {
int r;
r = remote_packet_out_append(format,
rtems_debugger->pid,
current[threads->next].id);
if (r < 0)
break;
format = ",p%d.%08lx";
++threads->next;
}
}
remote_packet_out_send();
return 0;
}
static int
remote_gq_thread_info_first(uint8_t* buffer, int size)
{
rtems_debugger->threads->next = 0;
return remote_gq_thread_info_subsequent(buffer, size);
}
static int
remote_gq_thread_extra_info(uint8_t* buffer, int size)
{
const char* comma;
remote_packet_out_reset();
comma = strchr((const char*) buffer, ',');
if (comma != NULL) {
DB_UINT pid = 0;
DB_UINT tid = 0;
bool extended;
extended = thread_id_decode(comma + 1, &pid, &tid);
if (extended || check_pid(pid)) {
int r;
r = rtems_debugger_thread_find_index(tid);
if (r >= 0) {
rtems_debugger_threads* threads = rtems_debugger->threads;
rtems_debugger_thread* current;
rtems_debugger_thread* thread;
char buf[128];
char str[32];
size_t l;
current = rtems_debugger_thread_current(threads);
thread = &current[r];
l = snprintf(buf, sizeof(buf),
"%4s (%08" PRIx32 "), ", thread->name, thread->id);
remote_packet_out_append_hex((const uint8_t*) buf, l);
l = snprintf(buf, sizeof(buf),
"priority(c:%3d r:%3d), ",
rtems_debugger_thread_current_priority(thread),
rtems_debugger_thread_real_priority(thread));
remote_packet_out_append_hex((const uint8_t*) buf, l);
l = snprintf(buf, sizeof(buf),
"stack(s:%6lu a:%p), ",
rtems_debugger_thread_stack_size(thread),
rtems_debugger_thread_stack_area(thread));
remote_packet_out_append_hex((const uint8_t*) buf, l);
rtems_debugger_thread_state_str(thread, str, sizeof(str));
l = snprintf(buf, sizeof(buf), "state(%s)", str);
remote_packet_out_append_hex((const uint8_t*) buf, l);
}
}
}
remote_packet_out_send();
return 0;
}
static int
remote_gq_supported(uint8_t* buffer, int size)
{
uint32_t capabilities = rtems_debugger_target_capabilities();
const char* p;
bool swbreak = false;
bool hwbreak = false;
bool vCont = false;
bool no_resumed = false;
bool multiprocess = false;
remote_packet_out("qSupported:PacketSize=%d;QNonStop-",
RTEMS_DEBUGGER_BUFFER_SIZE);
p = strchr((const char*) buffer, ':');
if (p != NULL)
++p;
while (p != NULL && *p != '\0') {
bool echo = false;
char* sc;
sc = strchr(p, ';');
if (sc != NULL) {
*sc++ = '\0';
}
if (strcmp(p, "swbreak+") == 0 &&
!swbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
swbreak = true;
echo = true;
}
if (strcmp(p, "hwbreak+") == 0 &&
!hwbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWBREAK) != 0) {
hwbreak = true;
echo = true;
}
if (!vCont && strcmp(p, "vContSupported+") == 0) {
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_VCONT;
vCont = true;
echo = true;
}
if (!no_resumed && strcmp(p, "no-resumed+") == 0) {
no_resumed = true;
echo = true;
}
if (!multiprocess && strcmp(p, "multiprocess+") == 0) {
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_MULTIPROCESS;
multiprocess = true;
echo = true;
}
if (echo) {
remote_packet_out_append_str(";");
remote_packet_out_append_str(p);
}
else if (strncmp(p, "xmlRegisters", sizeof("xmlRegisters") - 1) == 0) {
/* ignore */
}
else {
remote_packet_out_append_str(";");
remote_packet_out_append_buffer(p, strlen(p) - 1);
remote_packet_out_append_str("-");
}
p = sc;
}
if (!swbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
remote_packet_out_append_str("swbreak+;");
}
if (!hwbreak && (capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWBREAK) != 0) {
remote_packet_out_append_str("hwbreak+;");
}
if (!vCont) {
remote_packet_out_append_str("vContSupported+;");
}
if (!no_resumed) {
remote_packet_out_append_str("no-resumed+;");
}
if (!multiprocess) {
remote_packet_out_append_str("multiprocess+;");
}
remote_packet_out_send();
return 0;
}
static int
remote_gq_attached(uint8_t* buffer, int size)
{
const char* response = "1";
const char* colon = strchr((const char*) buffer, ':');
if (colon != NULL) {
DB_UINT pid = hex_decode_uint((const uint8_t*) colon + 1);
if ((pid_t) pid != rtems_debugger->pid)
response = r_E01;
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static rtems_debugger_thread* get_debugger_thread_from_id(const char* thread_id)
{
int r;
DB_UINT pid = 0;
DB_UINT tid = 0;
bool extended;
rtems_debugger_threads* threads = rtems_debugger->threads;
rtems_debugger_thread* current;
extended = thread_id_decode(thread_id, &pid, &tid);
if (!extended && !check_pid(pid)) {
return NULL;
}
r = rtems_debugger_thread_find_index(tid);
if (r < 0) {
return NULL;
}
current = rtems_debugger_thread_current(threads);
return &current[r];
}
static int
parse_get_tls_addr(
uint8_t* buffer,
int size,
const char** thread_id_str,
const char** offset_str,
const char** lm_str
)
{
if (thread_id_str == NULL || offset_str == NULL || lm_str == NULL) {
return 1;
}
*thread_id_str = strchr((const char*) buffer, ':') + 1;
if (*thread_id_str == NULL || *thread_id_str - (char*)buffer > size) {
/* malformed packet */
return 1;
}
*offset_str = strchr(*thread_id_str, ',') + 1;
if (*offset_str == NULL || *offset_str - (char*)buffer > size) {
/* malformed packet */
return 1;
}
*lm_str = strchr(*offset_str, ',') + 1;
if (*lm_str == NULL || *lm_str - (char*)buffer > size) {
/* malformed packet */
return 1;
}
return 0;
}
static int
remote_gq_get_tls_addr(uint8_t* buffer, int size)
{
const char* thread_id_str;
const char* offset_str;
const char* lm_str;
uint64_t target_address;
int r;
rtems_debugger_thread* thread;
DB_UINT offset;
DB_UINT lm;
if (parse_get_tls_addr(buffer, size, &thread_id_str, &offset_str, &lm_str)) {
/* malformed packet */
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
offset = hex_decode_uint((const uint8_t*) offset_str);
lm = hex_decode_uint((const uint8_t*) lm_str);
if (lm != 0) {
/*
* TODO(kmoore) lm is the load module identifier. It is ignored and expected
* to be 0 until TLS support for dynamically loaded modules is added.
*/
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
thread = get_debugger_thread_from_id(thread_id_str);
if (thread == NULL) {
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
target_address = thread->tcb->Registers.thread_id;
target_address += sizeof(TLS_Thread_control_block) + offset;
remote_packet_out_reset();
r = remote_packet_out_append_hex_be((const uint8_t*) &target_address,
sizeof(target_address));
if (r < 0) {
remote_packet_out_str(r_E01);
}
remote_packet_out_send();
return 0;
}
static const rtems_debugger_packet general_query[] = {
{ .label = "qfThreadInfo",
.command = remote_gq_thread_info_first },
{ .label = "qsThreadInfo",
.command = remote_gq_thread_info_subsequent },
{ .label = "qThreadExtraInfo",
.command = remote_gq_thread_extra_info },
{ .label = "qSupported",
.command = remote_gq_supported },
{ .label = "qAttached",
.command = remote_gq_attached },
{ .label = "qXfer",
.command = remote_gq_uninterpreted_transfer },
{ .label = "qGetTLSAddr",
.command = remote_gq_get_tls_addr },
};
#define REMOTE_GENERAL_QUERIES RTEMS_DEBUGGER_NUMOF(general_query)
static int
remote_general_query(uint8_t* buffer, int size)
{
return remote_packet_dispatch(general_query, REMOTE_GENERAL_QUERIES,
buffer, size);
}
static int
remote_gs_non_stop(uint8_t* buffer, int size)
{
const char* response = r_E01;
char* p = strchr((char*) buffer, ':');
if (p != NULL) {
++p;
response = r_OK;
if (*p == '0') {
rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_NON_STOP;
}
else if (*p == '1') {
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_NON_STOP;
}
else
response = r_E01;
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static const rtems_debugger_packet general_set[] = {
{ .label = "QNonStop",
.command = remote_gs_non_stop },
};
#define REMOTE_GENERAL_SETS RTEMS_DEBUGGER_NUMOF(general_set)
static int
remote_general_set(uint8_t* buffer, int size)
{
return remote_packet_dispatch(general_set, REMOTE_GENERAL_SETS,
buffer, size);
}
static int
remote_v_stopped(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
if (threads->next >= threads->stopped.level)
remote_packet_out_str(r_OK);
else {
rtems_id* stopped;
remote_packet_out("T%02x", rtems_debugger->signal);
stopped = rtems_debugger_thread_stopped(threads);
while (threads->next < threads->stopped.level) {
int r;
r = remote_packet_out_append("thread:p%d.%08lx;",
rtems_debugger->pid,
stopped[threads->next]);
if (r < 0)
break;
++threads->next;
}
}
remote_packet_out_send();
return 0;
}
static int
remote_stop_reason(uint8_t* buffer, int size)
{
rtems_debugger->threads->next = 0;
return remote_v_stopped(buffer, size);
}
static int
remote_v_continue(uint8_t* buffer, int size)
{
buffer += 5;
if (buffer[0] == '?') {
/*
* You need to supply 'c' and 'C' or GDB says vCont is not supported. As
* Sammy-J says "Silly GDB".
*/
remote_packet_out_str("vCont;c;C;s;r;");
}
else {
const char* semi = (const char*) &buffer[0];
bool resume = false;
bool ok = true;
while (ok && semi != NULL) {
const char* colon = strchr(semi + 1, ':');
const char action = *(semi + 1);
DB_UINT pid = 0;
DB_UINT tid = 0;
bool extended;
if (colon != NULL) {
int r = -1;
extended = thread_id_decode(colon + 1, &pid, &tid);
if (extended || check_pid(pid)) {
rtems_debugger_threads* threads = rtems_debugger->threads;
rtems_debugger_thread* thread = NULL;
int index = 0;
if (tid != (DB_UINT) -1) {
rtems_debugger_thread* current;
current = rtems_debugger_thread_current(threads);
index = rtems_debugger_thread_find_index(tid);
if (index >= 0)
thread = &current[index];
}
switch (action) {
case 'c':
case 'C':
if (tid == (DB_UINT) -1) {
r = rtems_debugger_thread_continue_all();
}
else if (thread != NULL) {
r = rtems_debugger_thread_continue(thread);
}
if (r == 0)
resume = true;
break;
case 'S':
case 's':
if (thread != NULL) {
r = rtems_debugger_thread_step(thread);
if (r == 0)
resume = true;
}
break;
case 'r':
/*
* Range to step around inside: `r start,end`.
*/
if (thread != NULL) {
const char* comma;
comma = strchr(semi + 2, ',');
if (comma != NULL) {
DB_UINT start;
DB_UINT end;
start = hex_decode_uint((const uint8_t*) semi + 2);
end = hex_decode_uint((const uint8_t*) comma + 1);
r = rtems_debugger_thread_stepping(thread, start, end);
if (r == 0)
resume = true;
}
else {
ok = false;
}
}
break;
default:
rtems_debugger_printf("rtems-db: vCont: unkown action: %c\n", action);
ok = false;
break;
}
if (r < 0)
ok = false;
}
}
else {
rtems_debugger_printf("rtems-db: vCont: no colon\n");
ok = false;
}
semi = strchr(semi + 1, ';');
}
if (ok)
remote_packet_out_str(r_OK);
else
remote_packet_out_str(r_E01);
if (resume)
rtems_debugger_thread_system_resume(false);
}
remote_packet_out_send();
return 0;
}
static int
remote_v_kill(uint8_t* buffer, int size)
{
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_RESET;
return remote_detach(buffer, size);
}
static const rtems_debugger_packet v_packets[] = {
{ .label = "vCont",
.command = remote_v_continue },
{ .label = "vStopped",
.command = remote_v_stopped },
{ .label = "vKill",
.command = remote_v_kill },
};
#define REMOTE_V_PACKETS RTEMS_DEBUGGER_NUMOF(v_packets)
static int
remote_v_packets(uint8_t* buffer, int size)
{
return remote_packet_dispatch(v_packets, REMOTE_V_PACKETS,
buffer, size);
}
static int
remote_thread_select(uint8_t* buffer, int size)
{
const char* response = r_OK;
int* index = NULL;
if (buffer[1] == 'g')
index = &rtems_debugger->threads->selector_gen;
else if (buffer[1] == 'c')
index = &rtems_debugger->threads->selector_cont;
else
response = r_E01;
if (index != NULL) {
DB_UINT pid = 0;
DB_UINT tid = 0;
bool extended;
extended = thread_id_decode((const char*) &buffer[2], &pid, &tid);
if (extended && !check_pid(pid)) {
response = r_E01;
}
else {
if (tid == 0 || tid == (DB_UINT) -1)
*index = (int) tid;
else {
int r;
r = rtems_debugger_thread_find_index(tid);
if (r < 0) {
response = r_E01;
*index = -1;
}
else
*index = r;
}
}
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static int
remote_thread_alive(uint8_t* buffer, int size)
{
const char* response = r_E01;
DB_UINT pid = 0;
DB_UINT tid = 0;
bool extended;
extended = thread_id_decode((const char*) &buffer[1], &pid, &tid);
if (!extended || (extended && check_pid(pid))) {
int r;
r = rtems_debugger_thread_find_index(tid);
if (r >= 0)
response = r_OK;
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static int
remote_argc_argv(uint8_t* buffer, int size)
{
return -1;
}
static int
remote_continue_at(uint8_t* buffer, int size)
{
if (!rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VCONT)) {
char* vCont_c = "vCont;c:p1.-1";
return remote_v_continue((uint8_t*) vCont_c, strlen(vCont_c));
}
return -1;
}
static int
remote_read_general_regs(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
bool ok = false;
int r;
if (threads->selector_gen >= 0 &&
threads->selector_gen < (int) threads->current.level) {
rtems_debugger_thread* current;
rtems_debugger_thread* thread;
current = rtems_debugger_thread_current(threads);
thread = &current[threads->selector_gen];
r = rtems_debugger_target_read_regs(thread);
if (r >= 0) {
remote_packet_out_reset();
r = remote_packet_out_append_hex((const uint8_t*) &thread->registers[0],
rtems_debugger_target_reg_table_size());
if (r >= 0)
ok = true;
}
}
if (!ok)
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
static int
remote_write_general_regs(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
size_t reg_table_size = rtems_debugger_target_reg_table_size();
bool ok = false;
int r;
if (threads->selector_gen >= 0 &&
threads->selector_gen < (int) threads->current.level &&
((size - 1) / 2) == (int) reg_table_size) {
rtems_debugger_thread* current;
rtems_debugger_thread* thread;
current = rtems_debugger_thread_current(threads);
thread = &current[threads->selector_gen];
r = rtems_debugger_target_read_regs(thread);
if (r >= 0) {
r = rtems_debugger_remote_packet_in_hex((uint8_t*) &thread->registers[0],
(const char*) &buffer[1],
reg_table_size);
if (r >= 0) {
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
ok = true;
}
}
}
if (!ok)
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
static int
remote_read_reg(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
bool ok = false;
int r;
if (threads->selector_gen >= 0
&& threads->selector_gen < (int) threads->current.level) {
size_t reg = hex_decode_int(&buffer[1]);
if (reg < rtems_debugger_target_reg_num()) {
rtems_debugger_thread* current;
rtems_debugger_thread* thread;
current = rtems_debugger_thread_current(threads);
thread = &current[threads->selector_gen];
r = rtems_debugger_target_read_regs(thread);
if (r >= 0) {
const size_t reg_size = rtems_debugger_target_reg_size(reg);
const size_t reg_offset = rtems_debugger_target_reg_offset(reg);
const uint8_t* addr = &thread->registers[reg_offset];
remote_packet_out_reset();
r = remote_packet_out_append_hex(addr, reg_size);
if (r >= 0)
ok = true;
}
}
}
if (!ok)
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
static int
remote_write_reg(uint8_t* buffer, int size)
{
rtems_debugger_threads* threads = rtems_debugger->threads;
const char* response = r_E01;
if (threads->selector_gen >= 0
&& threads->selector_gen < (int) threads->current.level) {
const char* equals;
equals = strchr((const char*) buffer, '=');
if (equals != NULL) {
size_t reg = hex_decode_int(&buffer[1]);
if (reg < rtems_debugger_target_reg_num()) {
rtems_debugger_thread* current;
rtems_debugger_thread* thread;
int r;
current = rtems_debugger_thread_current(threads);
thread = &current[threads->selector_gen];
r = rtems_debugger_target_read_regs(thread);
if (r >= 0) {
const size_t reg_size = rtems_debugger_target_reg_size(reg);
const size_t reg_offset = rtems_debugger_target_reg_offset(reg);
uint8_t* addr = &thread->registers[reg_offset];
r = rtems_debugger_remote_packet_in_hex(addr, equals + 1, reg_size);
if (r == 0) {
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
response = r_OK;
}
}
}
}
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static int
remote_read_memory(uint8_t* buffer, int size)
{
const char* comma;
comma = strchr((const char*) buffer, ',');
if (comma == NULL)
remote_packet_out_str(r_E01);
else {
uintptr_t addr;
DB_UINT length;
int r;
addr = hex_decode_addr(&buffer[1]);
length = hex_decode_uint((const uint8_t*) comma + 1);
remote_packet_out_reset();
r = rtems_debugger_target_start_memory_access();
if (r == 0) {
/*
* There should be specific target access for 8, 16, 32 and 64 bit reads.
*/
r = remote_packet_out_append_hex((const uint8_t*) addr, length);
}
rtems_debugger_target_end_memory_access();
if (r < 0)
remote_packet_out_str(r_E01);
}
remote_packet_out_send();
return 0;
}
static int
remote_write_memory(uint8_t* buffer, int size)
{
const char* response = r_E01;
const char* comma;
const char* colon;
comma = strchr((const char*) buffer, ',');
colon = strchr((const char*) buffer, ':');
if (comma != NULL && colon != NULL) {
uintptr_t addr;
DB_UINT length;
int r;
addr = hex_decode_addr(&buffer[1]);
length = hex_decode_uint((const uint8_t*) comma + 1);
r = rtems_debugger_target_start_memory_access();
if (r == 0) {
r = rtems_debugger_remote_packet_in_hex((uint8_t*) addr,
colon + 1,
length);
}
rtems_debugger_target_end_memory_access();
if (r == 0)
response = r_OK;
}
remote_packet_out_str(response);
remote_packet_out_send();
return 0;
}
static int
remote_single_step(uint8_t* buffer, int size)
{
if (!rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VCONT)) {
rtems_debugger_threads* threads = rtems_debugger->threads;
if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
rtems_debugger_thread* current;
char vCont_s[32];
current = rtems_debugger_thread_current(threads);
snprintf(vCont_s, sizeof(vCont_s), "vCont;s:p1.%08" PRIx32 ";c:p1.-1",
current[threads->selector_cont].id);
return remote_v_continue((uint8_t*) vCont_s, strlen(vCont_s));
}
remote_packet_out_str(r_E01);
remote_packet_out_send();
return 0;
}
return -1;
}
static int
remote_breakpoints(bool insert, uint8_t* buffer, int size)
{
const char* comma1;
int r = -1;
comma1 = strchr((const char*) buffer, ',');
if (comma1 != NULL) {
const char* comma2;
comma2 = strchr(comma1 + 1, ',');
if (comma2 != NULL) {
uint32_t capabilities;
uintptr_t addr;
DB_UINT kind;
addr = hex_decode_addr((const uint8_t*) comma1 + 1);
kind = hex_decode_uint((const uint8_t*) comma2 + 1);
capabilities = rtems_debugger_target_capabilities();
switch (buffer[1]) {
case '0':
if ((capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) != 0) {
r = rtems_debugger_target_swbreak_control(insert, addr, kind);
}
break;
case '1': /* execute */
case '2': /* write */
case '3': /* read */
case '4': /* access */
if ((capabilities & RTEMS_DEBUGGER_TARGET_CAP_HWWATCH) != 0) {
rtems_debugger_target_watchpoint type;
switch (buffer[1]) {
case '1':
type = rtems_debugger_target_hw_execute;
break;
case '2':
type = rtems_debugger_target_hw_write;
break;
case '3':
type = rtems_debugger_target_hw_read;
break;
case '4':
default:
type = rtems_debugger_target_hw_read_write;
break;
}
r = rtems_debugger_target_hwbreak_control(type, insert, addr, kind);
}
break;
default:
break;
}
}
}
remote_packet_out_str(r < 0 ? r_E01 : r_OK);
remote_packet_out_send();
return 0;
}
static int
remote_insert_breakpoint(uint8_t* buffer, int size)
{
return remote_breakpoints(true, buffer, size);
}
static int
remote_remove_breakpoint(uint8_t* buffer, int size)
{
return remote_breakpoints(false, buffer, size);
}
static int
remote_break(uint8_t* buffer, int size)
{
int r;
r = rtems_debugger_thread_system_suspend();
if (r < 0) {
rtems_debugger_printf("error: rtems-db: suspend all on break\n");
}
return remote_stop_reason(buffer, size);
}
static const rtems_debugger_packet packets[] = {
{ .label = "q",
.command = remote_general_query },
{ .label = "Q",
.command = remote_general_set },
{ .label = "v",
.command = remote_v_packets },
{ .label = "H",
.command = remote_thread_select },
{ .label = "T",
.command = remote_thread_alive },
{ .label = "?",
.command = remote_stop_reason },
{ .label = "A",
.command = remote_argc_argv },
{ .label = "c",
.command = remote_continue_at },
{ .label = "g",
.command = remote_read_general_regs },
{ .label = "G",
.command = remote_write_general_regs },
{ .label = "p",
.command = remote_read_reg },
{ .label = "P",
.command = remote_write_reg },
{ .label = "m",
.command = remote_read_memory },
{ .label = "M",
.command = remote_write_memory },
{ .label = "s",
.command = remote_single_step },
{ .label = "Z",
.command = remote_insert_breakpoint },
{ .label = "z",
.command = remote_remove_breakpoint },
{ .label = "D",
.command = remote_detach },
{ .label = "k",
.command = remote_v_kill },
{ .label = "r",
.command = remote_v_kill },
{ .label = "R",
.command = remote_v_kill },
{ .label = "^C",
.command = remote_break },
};
#define REMOTE_PACKETS RTEMS_DEBUGGER_NUMOF(packets)
static int
remote_packets(uint8_t* buffer, size_t size)
{
return remote_packet_dispatch(packets, REMOTE_PACKETS,
buffer, size);
}
static void
rtems_debugger_events(rtems_task_argument arg)
{
int r = 0;
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: events running\n");
/*
* Hold the lock until the thread blocks waiting for an event.
*/
rtems_debugger_lock();
rtems_debugger_target_enable();
if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_BREAK_WAITER)) {
rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_BREAK_WAITER;
r = rtems_debugger_thread_system_suspend();
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: break waiter\n");
rtems_debugger_server_events_signal();
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: break waiter: signalled\n");
}
if (r == 0) {
while (rtems_debugger_server_events_running()) {
rtems_debugger_server_events_wait();
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: event woken\n");
if (!rtems_debugger_server_events_running())
break;
r = rtems_debugger_thread_system_suspend();
if (r < 0)
break;
r = remote_stop_reason(NULL, 0);
if (r < 0)
break;
}
}
if (r < 0)
rtems_debugger_printf("rtems-db: error in events\n");
rtems_debugger_target_disable();
rtems_debugger->events_running = false;
rtems_debugger->events_finished = true;
rtems_debugger_unlock();
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: events finishing\n");
rtems_task_exit();
}
static int
rtems_debugger_session(void)
{
int r;
int rr;
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: remote running\n");
/*
* Hold the lock until the thread blocks on the remote input.
*/
rtems_debugger_lock();
r = rtems_debugger_target_create();
if (r < 0) {
rtems_debugger_unlock();
return r;
}
r = rtems_debugger_thread_create();
if (r < 0) {
rtems_debugger_target_destroy();
rtems_debugger_unlock();
return r;
}
rtems_debugger->events_running = true;
rtems_debugger->events_finished = false;
r = rtems_debugger_task_create("DBSe",
rtems_debugger->priority,
RTEMS_DEBUGGER_STACKSIZE,
rtems_debugger_events,
0,
&rtems_debugger->events_task);
if (r < 0) {
rtems_debugger_thread_destroy();
rtems_debugger_target_destroy();
rtems_debugger_unlock();
return r;
}
while (rtems_debugger_server_running() &&
rtems_debugger_connected()) {
r = rtems_debugger_remote_packet_in();
if (r < 0)
break;
if (r > 0) {
remote_packets(&rtems_debugger->input[0], r);
}
}
rtems_debugger->events_running = false;
rtems_debugger_server_events_signal();
rtems_debugger_unlock();
rr = rtems_debugger_task_destroy("DBSe",
rtems_debugger->events_task,
&rtems_debugger->events_finished,
RTEMS_DEBUGGER_TIMEOUT_STOP);
if (rr < 0 && r == 0)
r = rr;
rtems_debugger_lock();
rr = rtems_debugger_target_destroy();
if (rr < 0 && r == 0)
r = rr;
rr = rtems_debugger_thread_destroy();
if (rr < 0 && r == 0)
r = rr;
if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_RESET)) {
rtems_debugger_printf("rtems-db: shutdown\n");
/*
* Wait a moment to let any disconnection protocol a transport has
* complete. The legacy stack needs this to close the connection
* to GDB and clean up.
*/
sleep(2);
/*
* No special exit code, the user will asked GDB to kill the
* target
*/
rtems_fatal_error_occurred(1122);
}
rtems_debugger->flags = 0;
rtems_debugger->ack_pending = false;
rtems_debugger_unlock();
if (rtems_debugger_verbose())
rtems_debugger_printf("rtems-db: remote finishing\n");
return r;
}
static int
rtems_debugger_create(const char* remote,
const char* device,
rtems_task_priority priority,
int timeout,
const rtems_printer* printer)
{
int r;
if (rtems_debugger != NULL) {
rtems_printf(printer, "error: rtems-db: create: already active\n");
errno = EEXIST;
return -1;
}
rtems_debugger = malloc(sizeof(rtems_debugger_server));
if (rtems_debugger == NULL) {
rtems_printf(printer, "error: rtems-db: create: no memory\n");
errno = ENOMEM;
return -1;
}
memset(rtems_debugger, 0, sizeof(rtems_debugger_server));
/*
* These do not change with a session.
*/
rtems_debugger->priority = priority;
rtems_debugger->timeout = timeout;
rtems_debugger->printer = *printer;
rtems_debugger->pid = getpid();
rtems_debugger->remote_debug = false;
rtems_chain_initialize_empty(&rtems_debugger->exception_threads);
rtems_debugger->remote = rtems_debugger_remote_find(remote);
if (rtems_debugger->remote== NULL) {
rtems_printf(printer, "error: rtems-db: remote not found: %s\n", remote);
free(rtems_debugger);
rtems_debugger = NULL;
return -1;
}
r = rtems_debugger->remote->begin(rtems_debugger->remote, device);
if (r < 0) {
rtems_printf(printer, "error: rtems-db: remote begin: %s: %s\n",
rtems_debugger->remote->name, strerror(errno));
free(rtems_debugger);
rtems_debugger = NULL;
return -1;
}
/*
* Reset at the end of the session.
*/
rtems_debugger->flags = 0;
rtems_debugger->ack_pending = false;
r = rtems_debugger_lock_create();
if (r < 0) {
free(rtems_debugger);
rtems_debugger = NULL;
return -1;
}
return 0;
}
static int
rtems_debugger_destroy(void)
{
int r;
int rr;
rtems_debugger_lock();
rtems_debugger->server_running = false;
rtems_debugger_unlock();
r = rtems_debugger_remote_disconnect();
rr = rtems_debugger->remote->end(rtems_debugger->remote);
if (rr < 0 && r == 0)
r = rr;
rr = rtems_debugger_task_destroy("DBSs",
rtems_debugger->server_task,
&rtems_debugger->server_finished,
RTEMS_DEBUGGER_TIMEOUT_STOP);
if (rr < 0 && r == 0)
r = rr;
rr = rtems_debugger_lock_destroy();
if (rr < 0 && r == 0)
r = rr;
free(rtems_debugger);
rtems_debugger = NULL;
return r;
}
static void
rtems_debugger_main(rtems_task_argument arg)
{
int r;
rtems_debugger_lock();
rtems_debugger->server_running = true;
rtems_debugger->server_finished = false;
rtems_debugger_unlock();
rtems_debugger_printf("rtems-db: remote running\n");
while (rtems_debugger_server_running()) {
r = rtems_debugger_remote_connect();
if (r < 0)
break;
rtems_debugger_session();
rtems_debugger_remote_disconnect();
}
rtems_debugger_printf("rtems-db: remote finishing\n");
rtems_debugger_lock();
rtems_debugger->server_running = false;
rtems_debugger->server_finished = true;
rtems_debugger_unlock();
rtems_task_exit();
}
int
rtems_debugger_start(const char* remote,
const char* device,
int timeout,
rtems_task_priority priority,
const rtems_printer* printer)
{
int r;
r = rtems_debugger_create(remote, device, priority, timeout, printer);
if (r < 0)
return -1;
rtems_debugger_lock();
rtems_debugger->server_running = false;
rtems_debugger->server_finished = true;
_Condition_Initialize_named(&rtems_debugger->server_cond, "DBserver");
rtems_debugger_unlock();
r = rtems_debugger_task_create("DBSs",
priority,
RTEMS_DEBUGGER_STACKSIZE,
rtems_debugger_main,
0,
&rtems_debugger->server_task);
if (r < 0) {
rtems_debugger_destroy();
return -1;
}
return 0;
}
void
rtems_debugger_server_crash(void)
{
rtems_debugger_lock();
rtems_debugger->server_running = false;
rtems_debugger_unlock();
rtems_debugger->remote->end(rtems_debugger->remote);
}
int
rtems_debugger_break(bool wait)
{
int r = 0;
if (!rtems_debugger_running()) {
errno = EIO;
r = -1;
} else {
rtems_debugger_lock();
if (rtems_debugger_server_events_running()) {
rtems_debugger_server_events_signal();
} else if (
wait && !rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_BREAK_WAITER)) {
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_BREAK_WAITER;
rtems_debugger_server_events_wait();
} else {
errno = EIO;
r = -1;
}
rtems_debugger_unlock();
}
return r;
}
int
rtems_debugger_stop(void)
{
return rtems_debugger_destroy();
}
bool
rtems_debugger_running(void)
{
return rtems_debugger != NULL;
}
void
rtems_debugger_set_verbose(bool on)
{
if (rtems_debugger_running()) {
if (on)
rtems_debugger->flags |= RTEMS_DEBUGGER_FLAG_VERBOSE;
else
rtems_debugger->flags &= ~RTEMS_DEBUGGER_FLAG_VERBOSE;
}
}
int
rtems_debugger_remote_debug(bool state)
{
if (rtems_debugger_running()) {
rtems_debugger_lock();
rtems_debugger->remote_debug = state;
rtems_debugger_printf("rtems-db: remote-debug is %s\n",
rtems_debugger->remote_debug ? "on" : "off");
rtems_debugger_unlock();
} else {
rtems_debugger_printf("rtems-db: debug server not running\n");
}
return 0;
}