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