Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,3 @@ This will most likely break the usage of other plugins until the backup is resto
* Other Arm cores than Cortex-M33: This plugin has only been tested initially on nrf9160dk, and no work has been done to verify differences between Arm architectures on Zephyr.
* Other than Arm cores: Zero effort has been used to other architectures, and the plugin does not even check the core parameter, so it might probably crash or show garbage values.
* Distributing the built .DLL file in Windows requires user to install [Visual C++ Redistributable](https://support.microsoft.com/en-us/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0) on a target machine.


## Known problems in JLinkGDBServer

* When `RTOS_GetNumThreads()` returns zero, GDB server still calls `RTOS_GetThreadRegList()` with thread id zero and generates a new dummy thread for GDB. This breaks debugging of the board startup.
* There is no easy way to tell GDBServer that CPU is not yet running in Thread mode, and therefore
plugin should be ignored.
71 changes: 69 additions & 2 deletions unittests/plugin_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
Expand All @@ -14,6 +15,7 @@

#include "acutest.h"

#define GDB_NO_THREAD 0xDEAD

/*************************
* Dummy data
Expand Down Expand Up @@ -76,17 +78,21 @@ struct stack {
struct dummy_data {
uint32_t padding[1]; // Cannot start from offset zero.
struct my_kernel kernel;
bool post_kernel;
uint32_t debug_offsets[LEN_DEBUG_OFFSETS];
struct my_thread thread1;
struct my_thread thread2;
struct stack stack;
uint8_t extra_mem[2048];
} dummy_data = {
};

struct dummy_data dummy_data = {
.kernel = {
.version = 1,
.current = offsetof(struct dummy_data, thread1),
.threads = offsetof(struct dummy_data, thread1)
},
.post_kernel = true,
.thread1 = {
.name = "First Thread",
.saved = {.psp = offsetof(struct dummy_data, stack) },
Expand Down Expand Up @@ -115,7 +121,33 @@ struct dummy_data {
}
};

uint8_t *mem = (uint8_t *) &dummy_data;
struct dummy_data boot_dummy_data = {
.kernel = {
.version = 1,
.current = 0,
.threads = 0
},
.post_kernel = false,
.stack = {
.r0 = 0xdeadbeef,
.pc = 0xcafebabe,
},
.debug_offsets = {
offsetof(struct my_kernel, version),
offsetof(struct my_kernel, current),
offsetof(struct my_kernel, threads),
offsetof(struct my_thread, next),
offsetof(struct my_thread, next),
offsetof(struct my_thread, state),
offsetof(struct my_thread, state),
offsetof(struct my_thread, priority),
offsetof(struct my_thread, saved.psp),
offsetof(struct my_thread, name),
offsetof(struct my_thread, arch),
}
};

uint8_t *mem;



Expand Down Expand Up @@ -265,10 +297,12 @@ void test_init(void)
TEST_ASSERT(symbols != NULL);
symbols[0].address = offsetof(struct dummy_data, kernel);
symbols[1].address = offsetof(struct dummy_data, debug_offsets);
symbols[4].address = offsetof(struct dummy_data, post_kernel);
}

void test_parsing(void)
{
mem = (uint8_t *) &dummy_data;
test_init();
int ret = RTOS_UpdateThreads();
TEST_ASSERT(ret == 0);
Expand All @@ -295,8 +329,41 @@ void test_parsing(void)
RTOS_Init(&api, 0);
}

void test_boot(void)
{
mem = (uint8_t *) &boot_dummy_data;
test_init();

// Order of calls matches what is seen from JlinkGDBServer
uint32_t n = RTOS_GetNumThreads();
TEST_CHECK(n == 1);

int ret = RTOS_UpdateThreads();
TEST_ASSERT(ret == -1);

TEST_CHECK(RTOS_GetThreadId(0) == GDB_NO_THREAD);

char display[32];
ret = RTOS_GetThreadDisplay(display, GDB_NO_THREAD);
TEST_ASSERT(ret > 0);

n = RTOS_GetNumThreads();
TEST_CHECK(n == 1);

uint32_t id = RTOS_GetCurrentThreadId();
TEST_CHECK(id == GDB_NO_THREAD);

char val[10];
ret = RTOS_GetThreadReg(val, RTOS_PLUGIN_CPU_REG_CORTEX_M_PC, id);
TEST_CHECK(ret == -1);

// Force clear
RTOS_Init(&api, 0);
}

TEST_LIST = {
{ "test_init", test_init },
{ "test_parsing", test_parsing},
{ "test_boot", test_boot},
{ NULL, NULL } /* zeroed record marking the end of the list */
};
32 changes: 23 additions & 9 deletions zephyr_plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
#define JLINK_CORE_CORTEX_M7 0x0E0100FF
#define JLINK_CORE_CORTEX_M_V8MAINL 0x0E0200FF

/**
* Magic number used by GDB when no thread is running.
*/
#define GDB_NO_THREAD 0xDEAD

/**
* List of debug information that Zephyr exposes.
*
Expand Down Expand Up @@ -168,7 +173,7 @@ bool threads_updated = false;
/** Head of thread list */
struct thread_t *threads_head = NULL;
/** Currently running thread */
uint32_t current_base = 0;
uint32_t current_base = GDB_NO_THREAD;
/* size_t size */
uint8_t size_t_size = 0;
/** Num thread offset */
Expand Down Expand Up @@ -238,7 +243,7 @@ static void clear(void)
p = q;
}
threads_head = NULL;
current_base = 0;
current_base = GDB_NO_THREAD;
}

struct thread_t *base_to_thread(uint32_t base) {
Expand Down Expand Up @@ -291,7 +296,8 @@ static void update_handler_thread(void)
#endif
t = new_thread();
sprintf(t->name, "EXCEPTION/INTERRUPT");
current_base = 0;
t->base = GDB_NO_THREAD;
current_base = GDB_NO_THREAD;
}
}

Expand Down Expand Up @@ -555,7 +561,7 @@ EXPORT uint32_t RTOS_GetThreadId(uint32_t n) {
if (t)
return t->base;
else
return 0;
return current_base;
}

EXPORT int RTOS_GetThreadDisplay(char *pDisplay, uint32_t threadid) {
Expand Down Expand Up @@ -753,7 +759,7 @@ EXPORT int RTOS_UpdateThreads(void) {

EXPORT uint32_t RTOS_GetNumThreads(void) {
#if !defined(_NO_DEBUG_LOG) && VERBOSE_LOGGING
api->pfLogOutf("%s() --> %d\n", __func__, n_threads());
api->pfLogOutf("%s()\n", __func__);
#endif
/* RTOS_GetNumThreads can be called before RTOS_UpdateThreads when first
* attaching a debugger. As RTOS_UpdateThreads populates the thread
Expand All @@ -763,9 +769,17 @@ EXPORT uint32_t RTOS_GetNumThreads(void) {
* immediately.
*/
if (!threads_updated) {
if (RTOS_UpdateThreads() < 0) {
return 0;
}
RTOS_UpdateThreads();
}
uint32_t threads = n_threads();
/* GDB gets confused if there is no thread running. Consider the current
* execution context a thread even if the kernel has not started yet.
*/
if( threads == 0 ) {
threads = 1;
}
return n_threads();
#if !defined(_NO_DEBUG_LOG) && VERBOSE_LOGGING
api->pfLogOutf("%s() returned %u\n", __func__, threads);
#endif
return threads;
}