You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2021/11/10 17:31:18 UTC
[incubator-nuttx] 03/03: tools: add gdbplugin for JLinkGDBServer
nuttx thread aware
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 407bc04934737cdf9e473a64b4663c6aefa9312a
Author: zhuyanlin <zh...@xiaomi.com>
AuthorDate: Mon Oct 11 21:38:51 2021 +0800
tools: add gdbplugin for JLinkGDBServer nuttx thread aware
Use `JLinkGDBServer -rtos libnuttxplugin` to add this plugin.
Then in gdb can use command:
- `info threads` to show all threads infos
- `thread (id) ` to switch thread.
- `bt` to show thread backtrace.
Signed-off-by: zhuyanlin <zh...@xiaomi.com>
---
tools/Config.mk | 3 +
tools/Makefile.host | 20 +-
tools/jlink-nuttx.c | 643 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 663 insertions(+), 3 deletions(-)
diff --git a/tools/Config.mk b/tools/Config.mk
index 770950a..b200e6a 100644
--- a/tools/Config.mk
+++ b/tools/Config.mk
@@ -92,6 +92,9 @@ endif
ifeq ($(CONFIG_HOST_WINDOWS),y)
HOSTEXEEXT ?= .exe
+ HOSTDYNEXT ?= .dll
+else ifeq ($(CONFIG_HOST_LINUX),y)
+ HOSTDYNEXT ?= .so
endif
# This define is passed as EXTRAFLAGS for kernel-mode builds. It is also passed
diff --git a/tools/Makefile.host b/tools/Makefile.host
index a49b397..fdc06a4 100644
--- a/tools/Makefile.host
+++ b/tools/Makefile.host
@@ -35,7 +35,8 @@ all: b16$(HOSTEXEEXT) bdf-converter$(HOSTEXEEXT) cmpconfig$(HOSTEXEEXT) \
mksymtab$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkversion$(HOSTEXEEXT) \
cnvwindeps$(HOSTEXEEXT) nxstyle$(HOSTEXEEXT) initialconfig$(HOSTEXEEXT) \
gencromfs$(HOSTEXEEXT) convert-comments$(HOSTEXEEXT) lowhex$(HOSTEXEEXT) \
- detab$(HOSTEXEEXT) rmcr$(HOSTEXEEXT) incdir$(HOSTEXEEXT)
+ detab$(HOSTEXEEXT) rmcr$(HOSTEXEEXT) incdir$(HOSTEXEEXT) \
+ jlink-nuttx$(HOSTDYNEXT)
default: mkconfig$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \
cnvwindeps$(HOSTEXEEXT) incdir$(HOSTEXEEXT)
@@ -43,9 +44,11 @@ ifdef HOSTEXEEXT
.PHONY: b16 bdf-converter cmpconfig clean configure kconfig2html mkconfig \
mkdeps mksymtab mksyscall mkversion cnvwindeps nxstyle initialconfig \
gencromfs convert-comments lowhex detab rmcr incdir
-else
-.PHONY: clean
endif
+ifdef HOSTDYNEXT
+.PHONY: jlink-nuttx
+endif
+.PHONY: clean
# b16 - Fixed precision math conversion tool
@@ -222,6 +225,15 @@ ifdef HOSTEXEEXT
mkdeps: mkdeps$(HOSTEXEEXT)
endif
+# jlink-nuttx - Create jlink GDBServer nuttx plugin
+
+jlink-nuttx$(HOSTDYNEXT): jlink-nuttx.c
+ $(Q) $(HOSTCC) $(HOSTCFLAGS) -shared jlink-nuttx.c -o jlink-nuttx$(HOSTDYNEXT)
+
+ifdef HOSTDYNEXT
+jlink-nuttx: jlink-nuttx$(HOSTDYNEXT)
+endif
+
clean:
$(call DELFILE, b16)
$(call DELFILE, b16.exe)
@@ -258,6 +270,8 @@ clean:
$(call DELFILE, nxstyle.exe)
$(call DELFILE, rmcr)
$(call DELFILE, rmcr.exe)
+ $(call DELFILE, jlink-nuttx.so)
+ $(call DELFILE, jlink-nuttx.dll)
ifneq ($(CONFIG_WINDOWS_NATIVE),y)
$(Q) rm -rf *.dSYM
endif
diff --git a/tools/jlink-nuttx.c b/tools/jlink-nuttx.c
new file mode 100644
index 0000000..ee20c94
--- /dev/null
+++ b/tools/jlink-nuttx.c
@@ -0,0 +1,643 @@
+/****************************************************************************
+ * tools/jlink-nuttx.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Marcos for J-Link plugin API */
+
+#define PLUGIN_VER 100
+#define DISPLAY_LENGTH 256
+#define THREADID_BASE 1
+
+/* Marco for TCB struct size */
+
+#define TCB_NAMESIZE 256
+
+/* Marcos for J-Link API ops */
+
+#define REALLOC(ptr, size) g_plugin_priv.jops->realloc(ptr, size)
+#define ALLOC(size) g_plugin_priv.jops->alloc(size)
+#define FREE(ptr) g_plugin_priv.jops->free(ptr)
+
+#define READMEM(addr, ptr, size) g_plugin_priv.jops->readmem(addr, ptr, size)
+#define READU32(addr, data) g_plugin_priv.jops->readu32(addr, data)
+#define READU16(addr, data) g_plugin_priv.jops->readu16(addr, data)
+#define READU8(addr, data) g_plugin_priv.jops->readu8(addr, data)
+
+#define WRITEU32(addr, data) g_plugin_priv.jops->writeu32(addr, data)
+
+#define PERROR g_plugin_priv.jops->erroroutf
+#define PLOG g_plugin_priv.jops->logoutf
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum symbol_e
+{
+ PIDHASH = 0,
+ NPIDHASH,
+ TCBINFO,
+ READYTORUN,
+ NSYMBOLS
+};
+
+struct tcbinfo_s
+{
+ uint16_t pid_off;
+ uint16_t state_off;
+ uint16_t pri_off;
+ uint16_t name_off;
+ uint16_t reg_num;
+ uint16_t reg_offs[0];
+};
+
+struct symbols_s
+{
+ const char *name;
+ int optional;
+ uint32_t address;
+};
+
+/* J-Link server functions that can be called by the plugin */
+
+struct jlink_ops_s
+{
+ /* API version v1.0 and higher */
+
+ void (*free) (void *p);
+ void *(*alloc) (unsigned size);
+ void *(*realloc) (void *p, unsigned size);
+ void (*logoutf) (const char *sformat, ...);
+ void (*debugoutf) (const char *sformat, ...);
+ void (*warnoutf) (const char *sformat, ...);
+ void (*erroroutf) (const char *sformat, ...);
+ int (*readmem) (uint32_t addr, char *pdata,
+ unsigned int numbytes);
+ char (*readu8) (uint32_t addr, uint8_t *pdata);
+ char (*readu16) (uint32_t addr, uint16_t *pdata);
+ char (*readu32) (uint32_t addr, uint32_t *pdata);
+ int (*writemem) (uint32_t addr, const char *pdata,
+ unsigned numbytes);
+ void (*writeu8) (uint32_t addr, uint8_t data);
+ void (*writeu16) (uint32_t addr, uint16_t data);
+ void (*writeu32) (uint32_t addr, uint32_t data);
+ uint32_t (*load16te) (const uint8_t *p);
+ uint32_t (*load24te) (const uint8_t *p);
+ uint32_t (*load32te) (const uint8_t *p);
+
+ /* API version v1.1 and higher */
+
+ uint32_t (*readreg) (uint32_t regindex);
+ void (*writereg) (uint32_t regindex, uint32_t value);
+
+ /* End marker */
+
+ void *dummy;
+};
+
+struct plugin_priv_s
+{
+ uint32_t *pidhash;
+ uint32_t npidhash;
+ struct tcbinfo_s *tcbinfo;
+ uint16_t running;
+ uint32_t ntcb;
+ const struct jlink_ops_s *jops;
+};
+
+/****************************************************************************
+ * Private data
+ ****************************************************************************/
+
+static struct symbols_s g_symbols[] =
+{
+ {"g_pidhash", 0, 0},
+ {"g_npidhash", 0, 0},
+ {"g_tcbinfo", 0, 0},
+ {"g_readytorun", 0, 0},
+ { NULL, 0, 0}
+};
+
+static struct plugin_priv_s g_plugin_priv;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline int encode_hex(char *line, uint32_t value)
+{
+ /* output line in intel hex format */
+
+ return snprintf(line, 9, "%02x%02x%02x%02x", value & 0xff,
+ (value & 0xff00) >> 8, (value & 0xff0000) >> 16,
+ (value & 0xff000000) >> 24);
+}
+
+static inline uint32_t decode_hex(const char *line)
+{
+ /* Get value from hex format line */
+
+ uint32_t i;
+ uint32_t value = 0;
+
+ for (i = 7; i >= 0; )
+ {
+ value += (value << 8) + (line[i--] - '0');
+ value += (line[i--] - '0') << 4;
+ }
+
+ return value;
+}
+
+static int get_pid(struct plugin_priv_s *priv, uint32_t idx,
+ uint16_t *pid)
+{
+ int ret;
+
+ ret = READU16(priv->pidhash[idx] + priv->tcbinfo->pid_off, pid);
+ if (ret != 0)
+ {
+ PERROR("read %d pid error return %d\n", idx, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_idx_from_pid(struct plugin_priv_s *priv,
+ uint32_t pid)
+{
+ int idx;
+
+ for (idx = 0; idx < priv->ntcb; idx++)
+ {
+ uint16_t tmppid;
+
+ if (get_pid(priv, idx, &tmppid))
+ {
+ return -1;
+ }
+
+ if (tmppid == pid)
+ {
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+static int setget_reg(struct plugin_priv_s *priv, uint32_t idx,
+ uint32_t regidx, uint32_t *regval, bool write)
+{
+ uint32_t regaddr;
+ int ret = 0;
+
+ regaddr = priv->pidhash[idx] + priv->tcbinfo->reg_offs[regidx];
+
+ if (write)
+ {
+ WRITEU32(regaddr, *regval);
+ }
+ else
+ {
+ ret = READU32(regaddr, regval);
+ if (ret != 0)
+ {
+ PERROR("regread %d regidx %d error %d\n", idx, regidx, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int update_tcbinfo(struct plugin_priv_s *priv)
+{
+ if (!priv->tcbinfo)
+ {
+ uint16_t reg_num;
+ int ret;
+
+ ret = READU16(g_symbols[TCBINFO].address +
+ offsetof(struct tcbinfo_s, reg_num), ®_num);
+ if (ret != 0 || !reg_num)
+ {
+ PERROR("error reading regs ret %d\n", ret);
+ return ret;
+ }
+
+ priv->tcbinfo = ALLOC(sizeof(struct tcbinfo_s) +
+ reg_num * sizeof(uint16_t));
+
+ if (!priv->tcbinfo)
+ {
+ PERROR("error in malloc tcbinfo_s\n");
+ return -ENOMEM;
+ }
+
+ ret = READMEM(g_symbols[TCBINFO].address, (char *)priv->tcbinfo,
+ sizeof(struct tcbinfo_s) + reg_num * sizeof(uint16_t));
+ if (ret != sizeof(struct tcbinfo_s) + reg_num * sizeof(uint16_t))
+ {
+ PERROR("error in read tcbinfo_s ret %d\n", ret);
+ return ret;
+ }
+
+ PLOG("setup success! regs %d\n", priv->tcbinfo->reg_num);
+ }
+
+ return 0;
+}
+
+static int update_pidhash(struct plugin_priv_s *priv)
+{
+ uint32_t npidhash;
+ uint32_t pidhashaddr;
+ int ret;
+
+ ret = READU32(g_symbols[NPIDHASH].address, &npidhash);
+ if (ret != 0 || npidhash == 0)
+ {
+ PERROR("error reading npidhash ret %d\n", ret);
+ return ret;
+ }
+
+ if (npidhash != priv->npidhash)
+ {
+ priv->pidhash = REALLOC(priv->pidhash,
+ npidhash * sizeof(uint32_t *));
+ if (!priv->pidhash)
+ {
+ PERROR("error in malloc pidhash\n");
+ return -ENOMEM;
+ }
+
+ PLOG("npidhash change from %d to %d!\n", priv->npidhash, npidhash);
+ priv->npidhash = npidhash;
+ }
+
+ ret = READU32(g_symbols[PIDHASH].address, &pidhashaddr);
+ if (ret != 0 || pidhashaddr == 0)
+ {
+ PERROR("error in read pidhashaddr ret %d\n", ret);
+ return ret;
+ }
+
+ ret = READMEM(pidhashaddr, (char *)priv->pidhash,
+ priv->npidhash * sizeof(uint32_t *));
+ if (ret != priv->npidhash * sizeof(uint32_t *))
+ {
+ PERROR("error in read pidhash ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int normalize_tcb(struct plugin_priv_s *priv)
+{
+ uint32_t i;
+ uint32_t tcbaddr;
+ int ret;
+
+ priv->ntcb = 0;
+
+ for (i = 0; i < priv->npidhash; i++)
+ {
+ if (priv->pidhash[i])
+ {
+ priv->pidhash[priv->ntcb++] = priv->pidhash[i];
+ }
+ }
+
+ ret = READU32(g_symbols[READYTORUN].address, &tcbaddr);
+ if (ret != 0)
+ {
+ PERROR("read readytorun error return %d\n", ret);
+ }
+
+ ret = READU16(tcbaddr + priv->tcbinfo->pid_off, &priv->running);
+ if (ret != 0)
+ {
+ PERROR("read readytorun pid error return %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int RTOS_Init(const struct jlink_ops_s *api, uint32_t core)
+{
+ g_plugin_priv.jops = api;
+
+ return 1;
+}
+
+uint32_t RTOS_GetVersion(void)
+{
+ return PLUGIN_VER;
+}
+
+struct symbols_s *RTOS_GetSymbols(void)
+{
+ return g_symbols;
+}
+
+uint32_t RTOS_GetNumThreads(void)
+{
+ return g_plugin_priv.ntcb;
+}
+
+uint32_t RTOS_GetCurrentThreadId(void)
+{
+ return THREADID_BASE + g_plugin_priv.running;
+}
+
+uint32_t RTOS_GetThreadId(uint32_t n)
+{
+ if (n < g_plugin_priv.ntcb)
+ {
+ uint16_t pid;
+
+ if (get_pid(&g_plugin_priv, n, &pid) == 0)
+ {
+ return THREADID_BASE + pid;
+ }
+ }
+
+ return 0;
+}
+
+int RTOS_GetThreadDisplay(char *display, uint32_t threadid)
+{
+ int idx;
+ uint32_t len = 0;
+ char name[TCB_NAMESIZE];
+ uint8_t readval;
+ int ret;
+
+ threadid -= THREADID_BASE;
+
+ idx = get_idx_from_pid(&g_plugin_priv, threadid);
+ if (idx < 0)
+ {
+ PERROR("error in get_idx_from_pid return %d\n", idx);
+ return -1;
+ }
+
+ len += snprintf(display + len,
+ DISPLAY_LENGTH - len, "[PID:%03d]", threadid);
+
+ ret = READMEM(g_plugin_priv.pidhash[idx] +
+ g_plugin_priv.tcbinfo->name_off, name, TCB_NAMESIZE);
+ if (ret != TCB_NAMESIZE)
+ {
+ PERROR("error in read tcb name return %d\n", ret);
+ }
+
+ /* check tcb name is valid or not */
+
+ if ((name[0] > 'a' && name[0] < 'z')
+ || (name[0] > 'A' && name[0] < 'Z'))
+ {
+ len += snprintf(display + len, DISPLAY_LENGTH - len,
+ "%s", name);
+ }
+ else
+ {
+ len += snprintf(display + len, DISPLAY_LENGTH - len,
+ "thread-%d", threadid);
+ }
+
+ ret = READU8(g_plugin_priv.pidhash[idx] +
+ g_plugin_priv.tcbinfo->state_off, &readval);
+ if (ret != 0)
+ {
+ PERROR("error in read tcb state return %d\n", ret);
+ }
+
+ len += snprintf(display + len, DISPLAY_LENGTH - len, ":%04d",
+ readval);
+
+ ret = READU8(g_plugin_priv.pidhash[idx] +
+ g_plugin_priv.tcbinfo->pri_off, &readval);
+ if (ret != 0)
+ {
+ PERROR("error in read tcb pri return %d\n", ret);
+ }
+
+ len += snprintf(display + len,
+ DISPLAY_LENGTH - len, "[PRI:%03d]", readval);
+
+ return len;
+}
+
+int RTOS_GetThreadReg(char *hexregval, uint32_t regindex, uint32_t threadid)
+{
+ int idx;
+ uint32_t regval = 0;
+
+ threadid -= THREADID_BASE;
+
+ /* current task read by J-Link self */
+
+ if (threadid == g_plugin_priv.running)
+ {
+ return -1;
+ }
+
+ if (regindex > g_plugin_priv.tcbinfo->reg_num)
+ {
+ return -1;
+ }
+
+ idx = get_idx_from_pid(&g_plugin_priv, threadid);
+ if (idx < 0)
+ {
+ PERROR("error in get_idx_from_pid return %d\n", idx);
+ return -1;
+ }
+
+ if (g_plugin_priv.tcbinfo->reg_offs[regindex])
+ {
+ setget_reg(&g_plugin_priv, idx, regindex, ®val, false);
+
+ encode_hex(hexregval, regval);
+ return 0;
+ }
+
+ return -1;
+}
+
+int RTOS_GetThreadRegList(char *hexreglist, uint32_t threadid)
+{
+ int idx;
+ uint32_t j;
+ uint32_t regval;
+
+ threadid -= THREADID_BASE;
+
+ /* current task read by J-Link self */
+
+ if (threadid == g_plugin_priv.running)
+ {
+ return -1;
+ }
+
+ idx = get_idx_from_pid(&g_plugin_priv, threadid);
+ if (idx < 0)
+ {
+ PERROR("error in get_idx_from_pid return %d\n", idx);
+ return -1;
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ regval = 0;
+
+ if (g_plugin_priv.tcbinfo->reg_offs[j])
+ {
+ setget_reg(&g_plugin_priv, idx, j, ®val, false);
+ }
+
+ hexreglist += encode_hex(hexreglist, regval);
+ }
+
+ return 0;
+}
+
+int RTOS_SetThreadReg(char *hexregval,
+ uint32_t regindex, uint32_t threadid)
+{
+ int idx;
+ uint32_t regval;
+
+ threadid -= THREADID_BASE;
+
+ if (threadid == g_plugin_priv.running)
+ {
+ return -1;
+ }
+
+ if (regindex > g_plugin_priv.tcbinfo->reg_num)
+ {
+ return -1;
+ }
+
+ idx = get_idx_from_pid(&g_plugin_priv, threadid);
+ if (idx < 0)
+ {
+ PERROR("error in get_idx_from_pid return %d\n", idx);
+ return -1;
+ }
+
+ if (g_plugin_priv.tcbinfo->reg_offs[regindex])
+ {
+ regval = decode_hex(hexregval);
+
+ setget_reg(&g_plugin_priv, idx, regindex, ®val, true);
+ return 0;
+ }
+
+ return -1;
+}
+
+int RTOS_SetThreadRegList(char *hexreglist, uint32_t threadid)
+{
+ int idx;
+ uint32_t j;
+ uint32_t regval;
+
+ threadid -= THREADID_BASE;
+
+ if (threadid == g_plugin_priv.running)
+ {
+ return -1;
+ }
+
+ idx = get_idx_from_pid(&g_plugin_priv, threadid);
+ if (idx < 0)
+ {
+ PERROR("error in get_idx_from_pid return %d\n", idx);
+ return -1;
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ if (g_plugin_priv.tcbinfo->reg_offs[j])
+ {
+ regval = decode_hex(hexreglist);
+
+ setget_reg(&g_plugin_priv, idx, j, ®val, true);
+
+ hexreglist += 4;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int RTOS_UpdateThreads(void)
+{
+ int ret;
+
+ ret = update_tcbinfo(&g_plugin_priv);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = update_pidhash(&g_plugin_priv);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = normalize_tcb(&g_plugin_priv);
+ if (ret)
+ {
+ return ret;
+ }
+
+ return 0;
+}