/ components / esp_gdbstub / src / gdbstub.c
gdbstub.c
  1  // Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  //
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  
 15  #include <string.h>
 16  #include "esp_gdbstub.h"
 17  #include "esp_gdbstub_common.h"
 18  #include "sdkconfig.h"
 19  
 20  
 21  #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
 22  static inline int gdb_tid_to_task_index(int tid);
 23  static inline int task_index_to_gdb_tid(int tid);
 24  static void init_task_info(void);
 25  static void find_paniced_task_index(void);
 26  static void set_active_task(size_t index);
 27  static int handle_task_commands(unsigned char *cmd, int len);
 28  #endif
 29  
 30  static void send_reason(void);
 31  
 32  
 33  static esp_gdbstub_scratch_t s_scratch;
 34  
 35  
 36  void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame)
 37  {
 38  #ifndef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
 39      esp_gdbstub_frame_to_regfile(frame, &s_scratch.regfile);
 40  #else
 41      if (s_scratch.state == GDBSTUB_STARTED) {
 42          /* We have re-entered GDB Stub. Try disabling task support. */
 43          s_scratch.state = GDBSTUB_TASK_SUPPORT_DISABLED;
 44          /* Flush any pending GDB packet (this creates a garbage value) */
 45          esp_gdbstub_send_end();
 46      } else if (s_scratch.state == GDBSTUB_NOT_STARTED) {
 47          s_scratch.state = GDBSTUB_STARTED;
 48          /* Save the paniced frame and get the list of tasks */
 49          memcpy(&s_scratch.paniced_frame, frame, sizeof(*frame));
 50          init_task_info();
 51          find_paniced_task_index();
 52          /* Current task is the paniced task */
 53          if (s_scratch.paniced_task_index == GDBSTUB_CUR_TASK_INDEX_UNKNOWN) {
 54              set_active_task(0);
 55          } else {
 56              set_active_task(s_scratch.paniced_task_index);
 57          }
 58      }
 59  #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
 60  
 61      esp_gdbstub_target_init();
 62      s_scratch.signal = esp_gdbstub_get_signal(frame);
 63      send_reason();
 64      while (true) {
 65          unsigned char *cmd;
 66          size_t size;
 67          int res = esp_gdbstub_read_command(&cmd, &size);
 68          if (res > 0) {
 69              /* character received instead of a command */
 70              continue;
 71          }
 72          if (res == GDBSTUB_ST_ERR) {
 73              esp_gdbstub_send_str_packet("E01");
 74              continue;
 75          }
 76          res = esp_gdbstub_handle_command(cmd, size);
 77          if (res == GDBSTUB_ST_ERR) {
 78              esp_gdbstub_send_str_packet(NULL);
 79          }
 80      }
 81  }
 82  
 83  
 84  static void send_reason(void)
 85  {
 86      esp_gdbstub_send_start();
 87      esp_gdbstub_send_char('T');
 88      esp_gdbstub_send_hex(s_scratch.signal, 8);
 89      esp_gdbstub_send_end();
 90  }
 91  
 92  static uint32_t gdbstub_hton(uint32_t i)
 93  {
 94      return __builtin_bswap32(i);
 95  }
 96  
 97  /** Send all registers to gdb */
 98  static void handle_g_command(const unsigned char* cmd, int len)
 99  {
100      uint32_t *p = (uint32_t *) &s_scratch.regfile;
101      esp_gdbstub_send_start();
102      for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
103          esp_gdbstub_send_hex(gdbstub_hton(*p++), 32);
104      }
105      esp_gdbstub_send_end();
106  }
107  
108  /** Receive register values from gdb */
109  static void handle_G_command(const unsigned char* cmd, int len)
110  {
111      uint32_t *p = (uint32_t *) &s_scratch.regfile;
112      for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
113          *p++ = gdbstub_hton(esp_gdbstub_gethex(&cmd, 32));
114      }
115      esp_gdbstub_send_str_packet("OK");
116  }
117  
118  /** Read memory to gdb */
119  static void handle_m_command(const unsigned char* cmd, int len)
120  {
121      intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
122      cmd++;
123      uint32_t size = esp_gdbstub_gethex(&cmd, -1);
124  
125      if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
126          esp_gdbstub_send_str_packet("E01");
127          return;
128      }
129  
130      esp_gdbstub_send_start();
131      for (int i = 0; i < size; ++i) {
132          int b = esp_gdbstub_readmem(addr++);
133          esp_gdbstub_send_hex(b, 8);
134      }
135      esp_gdbstub_send_end();
136  }
137  
138  /** Handle a command received from gdb */
139  int esp_gdbstub_handle_command(unsigned char *cmd, int len)
140  {
141      unsigned char *data = cmd + 1;
142      if (cmd[0] == 'g') 
143      {
144          handle_g_command(data, len - 1);
145      } else if (cmd[0] == 'G') {
146          /* receive content for all registers from gdb */
147          handle_G_command(data, len - 1);
148      } else if (cmd[0] == 'm') {
149          /* read memory to gdb */
150          handle_m_command(data, len - 1);
151      } else if (cmd[0] == '?') {
152          /* Reply with stop reason */
153          send_reason();
154  #if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
155      } else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
156          return handle_task_commands(cmd, len);
157  #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
158      } else {
159          /* Unrecognized command */
160          return GDBSTUB_ST_ERR;
161      }
162      return GDBSTUB_ST_OK;
163  }
164  
165  /* Everything below is related to the support for listing FreeRTOS tasks as threads in GDB */
166  
167  #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
168  
169  /* Some terminology related to task/thread indices:
170   *
171   * "task index" — Index used here in gdbstub.c to enumberate all the tasks.
172   *                Range is from 0 to <number of tasks> - 1.
173   *                The order is defined by uxTaskGetSnapshotAll.
174   *
175   * "GDB TID" — Thread ID used by GDB internally and in the remote protocol.
176   *             IDs of real threads can be any positive values other than 0.
177   *             For example, OpenOCD uses the TCB pointer (cast to a 32-bit int) as GDB TID.
178   *             Values 0 and -1, when used in place of TID in the remote protocol
179   *             have special meanings:
180   *             -1: indicates all threads, may be used in 'Hc' command
181   *              0: indicates an arbitrary process or thread (GDB client doesn't care, server decides)
182   *
183   * Since GDB TIDs are arbitrary, except that 0 is reserved, we set them using a simple rule:
184   *    GDB TID = task index + 1.
185   * The two functions below perform the conversions between the kinds of IDs.
186   */
187  static inline int gdb_tid_to_task_index(int tid)
188  {
189      return tid - 1;
190  }
191  
192  static inline int task_index_to_gdb_tid(int tid)
193  {
194      return tid + 1;
195  }
196  
197  static void init_task_info(void)
198  {
199      unsigned tcb_size;
200      s_scratch.task_count = uxTaskGetSnapshotAll(s_scratch.tasks, GDBSTUB_TASKS_NUM, &tcb_size);
201  }
202  
203  static bool get_task_handle(size_t index, TaskHandle_t *handle)
204  {
205      if (index >= s_scratch.task_count) {
206          return false;
207      }
208      *handle = (TaskHandle_t) s_scratch.tasks[index].pxTCB;
209      return true;
210  }
211  
212  /** Get the index of the task running on the current CPU, and save the result */
213  static void find_paniced_task_index(void)
214  {
215      TaskHandle_t cur_handle = xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
216      TaskHandle_t handle;
217      for (int i = 0; i < s_scratch.task_count; i++) {
218          if (get_task_handle(i, &handle) && cur_handle == handle) {
219              s_scratch.paniced_task_index = i;
220              return;
221          }
222      }
223      s_scratch.paniced_task_index = GDBSTUB_CUR_TASK_INDEX_UNKNOWN;
224  }
225  
226  /** Select the current task and update the regfile */
227  static void set_active_task(size_t index)
228  {
229      if (index == s_scratch.paniced_task_index) {
230          /* Have to get the registers from exception frame */
231          esp_gdbstub_frame_to_regfile(&s_scratch.paniced_frame, &s_scratch.regfile);
232      } else {
233          /* Get the registers from TCB.
234           * FIXME: for the task currently running on the other CPU, extracting the registers from TCB
235           * isn't valid. Need to use some IPC mechanism to obtain the registers of the other CPU.
236           */
237          TaskHandle_t handle = NULL;
238          get_task_handle(index, &handle);
239          if (handle != NULL) {
240              esp_gdbstub_tcb_to_regfile(handle, &s_scratch.regfile);
241          }
242      }
243      s_scratch.current_task_index = index;
244  }
245  
246  /** H command sets the "current task" for the purpose of further commands */
247  static void handle_H_command(const unsigned char* cmd, int len)
248  {
249      const char *ret = "OK";
250      if (cmd[1] == 'g') {
251          cmd += 2;
252          int requested_tid = esp_gdbstub_gethex(&cmd, -1);
253          int requested_task_index = gdb_tid_to_task_index(requested_tid);
254          if (requested_tid == 0) {
255              /* 0 means "arbitrary process or thread", keep the current thread */
256          } else if (requested_task_index >= s_scratch.task_count ||
257                     requested_tid == -1) { /* -1 means "all threads", which doesn't make sense for "Hg" */
258              ret = "E00";
259          } else {
260              set_active_task(requested_task_index);
261          }
262          esp_gdbstub_send_str_packet(ret);
263      } else if (cmd[1] == 'c') {
264          /* Select the task for "continue" and "step" operations. No-op in post-mortem mode. */
265          /* Note that the argument may be -1 to indicate "all threads" or 0 to indicate "any thread" */
266          esp_gdbstub_send_str_packet(ret);
267      } else {
268          esp_gdbstub_send_str_packet(NULL);
269      }
270  }
271  
272  /** qC returns the current thread ID */
273  static void handle_qC_command(const unsigned char* cmd, int len)
274  {
275      esp_gdbstub_send_start();
276      esp_gdbstub_send_str("QC");
277      esp_gdbstub_send_hex(task_index_to_gdb_tid(s_scratch.current_task_index), 32);
278      esp_gdbstub_send_end();
279  }
280  
281  /** T command checks if the task is alive.
282   *  Since GDB isn't going to ask about the tasks which haven't been listed by q*ThreadInfo,
283   *  and the state of tasks can not change (no stepping allowed), simply return "OK" here.
284   */
285  static void handle_T_command(const unsigned char* cmd, int len)
286  {
287      esp_gdbstub_send_str_packet("OK");
288  }
289  
290  /** Called by qfThreadInfo and qsThreadInfo handlers */
291  static void send_single_thread_info(int task_index)
292  {
293      esp_gdbstub_send_start();
294      esp_gdbstub_send_str("m");
295      esp_gdbstub_send_hex(task_index_to_gdb_tid(task_index), 32);
296      esp_gdbstub_send_end();
297  }
298  
299  /** qfThreadInfo requests the start of the thread list, qsThreadInfo (below) is repeated to
300   *  get the subsequent threads.
301   */
302  static void handle_qfThreadInfo_command(const unsigned char* cmd, int len)
303  {
304      assert(s_scratch.task_count > 0);  /* There should be at least one task */
305      send_single_thread_info(0);
306      s_scratch.thread_info_index = 1;
307  }
308  
309  static void handle_qsThreadInfo_command(const unsigned char* cmd, int len)
310  {
311      int task_index = s_scratch.thread_info_index;
312      if (task_index == s_scratch.task_count) {
313          /* No more tasks */
314          esp_gdbstub_send_str_packet("l");
315          return;
316      }
317      send_single_thread_info(s_scratch.thread_info_index);
318      s_scratch.thread_info_index++;
319  }
320  
321  /** qThreadExtraInfo requests the thread name */
322  static void handle_qThreadExtraInfo_command(const unsigned char* cmd, int len)
323  {
324      cmd += sizeof("qThreadExtraInfo,") - 1;
325      int task_index = gdb_tid_to_task_index(esp_gdbstub_gethex(&cmd, -1));
326      TaskHandle_t handle;
327      if (!get_task_handle(task_index, &handle)) {
328          esp_gdbstub_send_str_packet("E01");
329          return;
330      }
331      esp_gdbstub_send_start();
332      const char* task_name = pcTaskGetTaskName(handle);
333      while (*task_name) {
334          esp_gdbstub_send_hex(*task_name, 8);
335          task_name++;
336      }
337      /** TODO: add "Running" or "Suspended" and "CPU0" or "CPU1" */
338      esp_gdbstub_send_end();
339  }
340  
341  bool command_name_matches(const char* pattern, const unsigned char* ucmd, int len)
342  {
343      const char* cmd = (const char*) ucmd;
344      const char* end = cmd + len;
345      for (; *pattern && cmd < end; ++cmd, ++pattern) {
346          if (*pattern == '?') {
347              continue;
348          }
349          if (*pattern != *cmd) {
350              return false;
351          }
352      }
353      return *pattern == 0 && (cmd == end || *cmd == ',');
354  }
355  
356  /** Handle all the thread-related commands */
357  static int handle_task_commands(unsigned char *cmd, int len)
358  {
359      if (cmd[0] == 'H') {
360          /* Continue with task */
361          handle_H_command(cmd, len);
362      } else if (cmd[0] == 'T') {
363          /* Task alive check */
364          handle_T_command(cmd, len);
365      } else if (cmd[0] == 'q') {
366          if (command_name_matches("qfThreadInfo", cmd, len)) {
367              handle_qfThreadInfo_command(cmd, len);
368          } else if (command_name_matches("qsThreadInfo", cmd, len)) {
369              handle_qsThreadInfo_command(cmd, len);
370          } else if (command_name_matches("qC", cmd, len)) {
371              handle_qC_command(cmd, len);
372          } else if (command_name_matches("qThreadExtraInfo", cmd, len)) {
373              handle_qThreadExtraInfo_command(cmd, len);
374          } else {
375              /* Unrecognized command */
376              return GDBSTUB_ST_ERR;
377          }
378      } else {
379          /* Unrecognized command */
380          return GDBSTUB_ST_ERR;
381      }
382      return GDBSTUB_ST_OK;
383  }
384  
385  #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
386