mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-08-20 03:44:15 +08:00
libdebugger: Fix corruption of thread registers with 32 or more threads
The block logic uses realloc to resize memory used by libdebugger when creating a snapshot of threads. Thread registers take a reference to the block and if a realloc changes the base address any references are invalid. Add rebase handler support to blocks that is called when the block base changes and add a handler to thread register blocks to rebase a thread's registers. Exclude threads using both types of names they can have. This lets all names be reported in extra thread info packets. Closes #5662
This commit is contained in:
committed by
Vijay Banerjee
parent
5c074eb03c
commit
e2859cf1c5
@@ -29,20 +29,20 @@
|
||||
|
||||
#include "rtems-debugger-block.h"
|
||||
|
||||
int
|
||||
rtems_debugger_block_create(rtems_debugger_block* block,
|
||||
size_t step,
|
||||
size_t size)
|
||||
{
|
||||
int r = 0;
|
||||
int rtems_debugger_block_create(rtems_debugger_block* block, size_t step,
|
||||
size_t size,
|
||||
rtems_debugger_block_rebase rebaser) {
|
||||
block->level = 0;
|
||||
block->step = step;
|
||||
block->count = step;
|
||||
block->size = size;
|
||||
block->rebaser = rebaser;
|
||||
block->block = calloc(block->count, block->size);
|
||||
if (block->block == NULL)
|
||||
if (block->block == NULL) {
|
||||
errno = ENOMEM;
|
||||
return r;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -56,19 +56,28 @@ rtems_debugger_block_destroy(rtems_debugger_block* block)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_block_resize(rtems_debugger_block* block)
|
||||
{
|
||||
int r = 0;
|
||||
int rtems_debugger_block_resize(rtems_debugger_block* block) {
|
||||
if (block->level >= block->count) {
|
||||
block->count += block->step;
|
||||
block->block = realloc(block->block, block->count * block->size);
|
||||
if (block->block == NULL) {
|
||||
void* new_block = realloc(block->block, block->count * block->size);
|
||||
if (new_block == NULL) {
|
||||
block->block = NULL;
|
||||
block->level = 0;
|
||||
block->count = 0;
|
||||
errno = ENOMEM;
|
||||
r = -1;
|
||||
return -1;
|
||||
}
|
||||
if (block->block != new_block && block->rebaser != NULL) {
|
||||
block->rebaser(block->block, new_block);
|
||||
}
|
||||
block->block = new_block;
|
||||
}
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* rtems_debugger_block_transform(void* previous, void* new, void* addr) {
|
||||
uintptr_t previous_u = (uintptr_t)previous;
|
||||
uintptr_t new_u = (uintptr_t)new;
|
||||
uintptr_t addr_u = (uintptr_t)addr;
|
||||
return (void*)((addr_u - previous_u) + new_u);
|
||||
}
|
||||
|
||||
@@ -37,26 +37,34 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Block resize base change handler. This is called when a block resize
|
||||
* moves the base address of the block. The handler is called with the
|
||||
* previous and new values and you can call transform to relocate your
|
||||
* pointer to the new base.
|
||||
*/
|
||||
typedef void (*rtems_debugger_block_rebase)(void* previous, void* new);
|
||||
|
||||
/**
|
||||
* DB server block manages a block of re-sizable memory. The block only
|
||||
* grows. As more threads enter the system the block becomes the peak and then
|
||||
* sits at that level.
|
||||
*/
|
||||
typedef struct rtems_debugger_block
|
||||
{
|
||||
void* block; /**< The block of memory. */
|
||||
size_t step; /**< The step size the block is increased by. */
|
||||
size_t size; /**< The size of the elements in the block. */
|
||||
size_t count; /**< The number of elements in the block. */
|
||||
size_t level; /**< The usage level in the block. */
|
||||
typedef struct rtems_debugger_block {
|
||||
void* block; /**< The block of memory. */
|
||||
size_t step; /**< The step size the block is increased by. */
|
||||
size_t size; /**< The size of the elements in the block. */
|
||||
size_t count; /**< The number of elements in the block. */
|
||||
size_t level; /**< The usage level in the block. */
|
||||
rtems_debugger_block_rebase rebaser; /**< Rebase handler if set */
|
||||
} rtems_debugger_block;
|
||||
|
||||
/**
|
||||
* Create a block.
|
||||
*/
|
||||
extern int rtems_debugger_block_create(rtems_debugger_block* block,
|
||||
size_t step,
|
||||
size_t size);
|
||||
extern int rtems_debugger_block_create(rtems_debugger_block* block, size_t step,
|
||||
size_t size,
|
||||
rtems_debugger_block_rebase rebaser);
|
||||
|
||||
/**
|
||||
* Destroy a block.
|
||||
@@ -68,6 +76,12 @@ extern int rtems_debugger_block_destroy(rtems_debugger_block* block);
|
||||
*/
|
||||
extern int rtems_debugger_block_resize(rtems_debugger_block* block);
|
||||
|
||||
/**
|
||||
* Transform a pointer to a new base. Using when rebasing.
|
||||
*/
|
||||
extern void* rtems_debugger_block_transform(void* previous, void* new,
|
||||
void* addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,9 +94,9 @@ rtems_debugger_target_create(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = rtems_debugger_block_create(&target->swbreaks,
|
||||
RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
|
||||
sizeof(rtems_debugger_target_swbreak));
|
||||
r = rtems_debugger_block_create(
|
||||
&target->swbreaks, RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
|
||||
sizeof(rtems_debugger_target_swbreak), NULL);
|
||||
if (r < 0) {
|
||||
free(target);
|
||||
return -1;
|
||||
|
||||
@@ -59,9 +59,31 @@ rtems_debugger_thread_free(rtems_debugger_threads* threads)
|
||||
threads->next = 0;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_thread_create(void)
|
||||
{
|
||||
static void rebase_registers(void* previous, void* new) {
|
||||
rtems_debugger_threads* threads = rtems_debugger->threads;
|
||||
rtems_debugger_thread* current;
|
||||
current = rtems_debugger_thread_current(threads);
|
||||
if (current != NULL) {
|
||||
size_t i;
|
||||
for (i = 0; i < threads->current.level; ++i) {
|
||||
rtems_debugger_thread* thread = ¤t[i];
|
||||
thread->registers =
|
||||
rtems_debugger_block_transform(previous, new, thread->registers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool rtems_debugger_thread_excluded(const char* name) {
|
||||
size_t i;
|
||||
for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
|
||||
if (strcmp(excludes_defaults[i], name) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int rtems_debugger_thread_create(void) {
|
||||
rtems_debugger_threads* threads;
|
||||
int r;
|
||||
|
||||
@@ -75,7 +97,7 @@ rtems_debugger_thread_create(void)
|
||||
|
||||
r = rtems_debugger_block_create(&threads->current,
|
||||
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
sizeof(rtems_debugger_thread));
|
||||
sizeof(rtems_debugger_thread), NULL);
|
||||
if (r < 0) {
|
||||
rtems_debugger_thread_free(threads);
|
||||
free(threads);
|
||||
@@ -84,9 +106,9 @@ rtems_debugger_thread_create(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = rtems_debugger_block_create(&threads->registers,
|
||||
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
rtems_debugger_target_reg_table_size());
|
||||
r = rtems_debugger_block_create(
|
||||
&threads->registers, RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
rtems_debugger_target_reg_table_size(), rebase_registers);
|
||||
if (r < 0) {
|
||||
rtems_debugger_thread_free(threads);
|
||||
free(threads);
|
||||
@@ -97,18 +119,18 @@ rtems_debugger_thread_create(void)
|
||||
|
||||
r = rtems_debugger_block_create(&threads->excludes,
|
||||
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
sizeof(rtems_id));
|
||||
sizeof(rtems_id), NULL);
|
||||
if (r < 0) {
|
||||
rtems_debugger_thread_free(threads);
|
||||
free(threads);
|
||||
rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
|
||||
rtems_debugger_printf("error: rtems-db: thread: excludes alloc: (%d) %s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = rtems_debugger_block_create(&threads->stopped,
|
||||
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
sizeof(rtems_id));
|
||||
sizeof(rtems_id), NULL);
|
||||
if (r < 0) {
|
||||
rtems_debugger_thread_free(threads);
|
||||
free(threads);
|
||||
@@ -119,7 +141,7 @@ rtems_debugger_thread_create(void)
|
||||
|
||||
r = rtems_debugger_block_create(&threads->steppers,
|
||||
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
|
||||
sizeof(rtems_debugger_thread_stepper));
|
||||
sizeof(rtems_debugger_thread_stepper), NULL);
|
||||
if (r < 0) {
|
||||
rtems_debugger_thread_free(threads);
|
||||
free(threads);
|
||||
@@ -162,15 +184,14 @@ rtems_debugger_thread_find_index(rtems_id id)
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool
|
||||
snapshot_thread(rtems_tcb* tcb, void* arg)
|
||||
{
|
||||
static bool snapshot_thread(rtems_tcb* tcb, void* arg) {
|
||||
rtems_debugger_threads* threads = rtems_debugger->threads;
|
||||
rtems_id id = tcb->Object.id;
|
||||
char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
|
||||
bool exclude = false;
|
||||
size_t i;
|
||||
int sc;
|
||||
rtems_id id = tcb->Object.id;
|
||||
char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
|
||||
bool exclude = false;
|
||||
int sc;
|
||||
|
||||
(void)arg;
|
||||
|
||||
/*
|
||||
* The only time the threads pointer is NULL is a realloc error so we stop
|
||||
@@ -188,12 +209,24 @@ snapshot_thread(rtems_tcb* tcb, void* arg)
|
||||
exclude = true;
|
||||
break;
|
||||
default:
|
||||
rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
|
||||
for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
|
||||
if (strcmp(excludes_defaults[i], name) == 0) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* There are object names that can be a string or the classic 4
|
||||
* byte name and threads that are based on object can optionally
|
||||
* hold a name in it's Join_queue that points to the last section
|
||||
* of the TCB. The API to get an object name has no ability to see
|
||||
* the thread name extension. The POSIX np calls to set the name
|
||||
* only set the thread extension name. The score is not uniform and
|
||||
* as a result the APIs are not aligned.
|
||||
*
|
||||
* To entrench this state of affairs LibBSD uses the object name
|
||||
* and thread name and they are not the same. This makes detecting
|
||||
* exclusions harder. We need to deal with both names.
|
||||
*/
|
||||
rtems_object_get_name(id, sizeof(name), (char*)&name[0]);
|
||||
exclude = rtems_debugger_thread_excluded(name);
|
||||
if (!exclude) {
|
||||
_Thread_Get_name(tcb, &name[0], sizeof(name));
|
||||
exclude = rtems_debugger_thread_excluded(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ extern "C" {
|
||||
/**
|
||||
* Debugger thread name size, fixed size. ASCIIZ format.
|
||||
*/
|
||||
#define RTEMS_DEBUGGER_THREAD_NAME_SIZE (5)
|
||||
#define RTEMS_DEBUGGER_THREAD_NAME_SIZE (16)
|
||||
|
||||
/**
|
||||
* Debugger thread allocation block size.
|
||||
|
||||
Reference in New Issue
Block a user