| | 1 | /* Haiku native-dependent code common to multiple platforms. |
| | 2 | |
| | 3 | Copyright 2011 Jérôme Duval <korli@users.berlios.de>. |
| | 4 | Copyright 2005 Ingo Weinhold <bonefish@cs.tu-berlin.de>. |
| | 5 | |
| | 6 | This file is part of GDB. |
| | 7 | |
| | 8 | This program is free software; you can redistribute it and/or modify |
| | 9 | it under the terms of the GNU General Public License as published by |
| | 10 | the Free Software Foundation; either version 2 of the License, or |
| | 11 | (at your option) any later version. |
| | 12 | |
| | 13 | This program is distributed in the hope that it will be useful, |
| | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | 16 | GNU General Public License for more details. |
| | 17 | |
| | 18 | You should have received a copy of the GNU General Public License |
| | 19 | along with this program; if not, write to the Free Software |
| | 20 | Foundation, Inc., 59 Temple Place - Suite 330, |
| | 21 | Boston, MA 02111-1307, USA. */ |
| | 22 | |
| | 23 | #include <stdio.h> |
| | 24 | #include <string.h> |
| | 25 | |
| | 26 | #include <debugger.h> |
| | 27 | #include <image.h> |
| | 28 | #include <OS.h> |
| | 29 | |
| | 30 | #include <debug_support.h> |
| | 31 | |
| | 32 | #include "defs.h" // include this first -- otherwise "command.h" will choke |
| | 33 | #include "command.h" |
| | 34 | #include "gdbcore.h" |
| | 35 | #include "gdbthread.h" |
| | 36 | #include "haiku-nat.h" |
| | 37 | #include "inferior.h" |
| | 38 | #include "observer.h" |
| | 39 | #include "regcache.h" |
| | 40 | #include "solib-haiku.h" |
| | 41 | #include "symfile.h" |
| | 42 | #include "target.h" |
| | 43 | |
| | 44 | #define TRACE_HAIKU_NAT |
| | 45 | #ifdef TRACE_HAIKU_NAT |
| | 46 | #define TRACE(x) printf x |
| | 47 | #else |
| | 48 | #define TRACE(x) while (false) {} |
| | 49 | #endif |
| | 50 | |
| | 51 | static struct target_ops *sHaikuTarget = NULL; |
| | 52 | |
| | 53 | typedef struct debug_event { |
| | 54 | struct debug_event *next; |
| | 55 | debug_debugger_message message; |
| | 56 | debug_debugger_message_data data; |
| | 57 | } debug_event; |
| | 58 | |
| | 59 | typedef struct debug_event_list { |
| | 60 | debug_event *head; |
| | 61 | debug_event *tail; |
| | 62 | } debug_event_list; |
| | 63 | |
| | 64 | typedef enum { |
| | 65 | SIGNAL_ARRIVED, // the signal has actually arrived and is going to be |
| | 66 | // handled (in any way) |
| | 67 | SIGNAL_WILL_ARRIVE, // the signal has not yet been sent, but will be sent |
| | 68 | // (e.g. an exception occurred and will cause the |
| | 69 | // signal when not being ignored) |
| | 70 | SIGNAL_FAKED, // the signal didn't arrive and won't arrive, we faked |
| | 71 | // it (often in case of SIGTRAP) |
| | 72 | } pending_signal_status; |
| | 73 | |
| | 74 | typedef struct thread_debug_info { |
| | 75 | struct thread_debug_info *next; |
| | 76 | thread_id thread; |
| | 77 | bool stopped; |
| | 78 | debug_event *last_event; // last debug message from |
| | 79 | // the thread; valid only, |
| | 80 | // if stopped |
| | 81 | int reprocess_event; // > 0, the event |
| | 82 | // shall be processed a |
| | 83 | // that many times, which |
| | 84 | // will probably trigger |
| | 85 | // another target event |
| | 86 | int signal; // only valid, if stopped |
| | 87 | pending_signal_status signal_status; |
| | 88 | } thread_debug_info; |
| | 89 | |
| | 90 | typedef struct extended_image_info { |
| | 91 | haiku_image_info info; |
| | 92 | struct extended_image_info *next; |
| | 93 | } extended_image_info; |
| | 94 | |
| | 95 | typedef struct team_debug_info { |
| | 96 | team_id team; |
| | 97 | port_id debugger_port; |
| | 98 | thread_id nub_thread; |
| | 99 | debug_context context; |
| | 100 | thread_debug_info *threads; |
| | 101 | extended_image_info *images; |
| | 102 | debug_event_list events; |
| | 103 | } team_debug_info; |
| | 104 | |
| | 105 | static team_debug_info sTeamDebugInfo; |
| | 106 | |
| | 107 | |
| | 108 | typedef struct signal_map_entry { |
| | 109 | enum target_signal target; |
| | 110 | int haiku; |
| | 111 | } signal_map_entry; |
| | 112 | |
| | 113 | static const signal_map_entry sSignalMap[] = { |
| | 114 | { TARGET_SIGNAL_0, 0 }, |
| | 115 | { TARGET_SIGNAL_HUP, SIGHUP }, |
| | 116 | { TARGET_SIGNAL_INT, SIGINT }, |
| | 117 | { TARGET_SIGNAL_QUIT, SIGQUIT }, |
| | 118 | { TARGET_SIGNAL_ILL, SIGILL }, |
| | 119 | { TARGET_SIGNAL_CHLD, SIGCHLD }, |
| | 120 | { TARGET_SIGNAL_ABRT, SIGABRT }, |
| | 121 | { TARGET_SIGNAL_PIPE, SIGPIPE }, |
| | 122 | { TARGET_SIGNAL_FPE, SIGFPE }, |
| | 123 | { TARGET_SIGNAL_KILL, SIGKILL }, |
| | 124 | { TARGET_SIGNAL_STOP, SIGSTOP }, |
| | 125 | { TARGET_SIGNAL_SEGV, SIGSEGV }, |
| | 126 | { TARGET_SIGNAL_CONT, SIGCONT }, |
| | 127 | { TARGET_SIGNAL_TSTP, SIGTSTP }, |
| | 128 | { TARGET_SIGNAL_ALRM, SIGALRM }, |
| | 129 | { TARGET_SIGNAL_TERM, SIGTERM }, |
| | 130 | { TARGET_SIGNAL_TTIN, SIGTTIN }, |
| | 131 | { TARGET_SIGNAL_TTOU, SIGTTOU }, |
| | 132 | { TARGET_SIGNAL_USR1, SIGUSR1 }, |
| | 133 | { TARGET_SIGNAL_USR2, SIGUSR2 }, |
| | 134 | { TARGET_SIGNAL_WINCH, SIGWINCH }, |
| | 135 | { TARGET_SIGNAL_TRAP, SIGTRAP }, |
| | 136 | { TARGET_SIGNAL_0, SIGKILLTHR }, // not debuggable anyway |
| | 137 | { -1, -1 } |
| | 138 | }; |
| | 139 | |
| | 140 | |
| | 141 | // #pragma mark - |
| | 142 | |
| | 143 | |
| | 144 | static char *haiku_pid_to_str (struct target_ops *ops, ptid_t ptid); |
| | 145 | |
| | 146 | |
| | 147 | static int |
| | 148 | target_to_haiku_signal(enum target_signal targetSignal) |
| | 149 | { |
| | 150 | int i; |
| | 151 | |
| | 152 | if (targetSignal < 0) |
| | 153 | return -1; |
| | 154 | |
| | 155 | for (i = 0; sSignalMap[i].target != -1 || sSignalMap[i].haiku != -1; i++) { |
| | 156 | if (targetSignal == sSignalMap[i].target) |
| | 157 | return sSignalMap[i].haiku; |
| | 158 | } |
| | 159 | |
| | 160 | return -1; |
| | 161 | } |
| | 162 | |
| | 163 | |
| | 164 | static enum target_signal |
| | 165 | haiku_to_target_signal(int haikuSignal) |
| | 166 | { |
| | 167 | int i; |
| | 168 | |
| | 169 | if (haikuSignal < 0) |
| | 170 | return TARGET_SIGNAL_0; |
| | 171 | |
| | 172 | for (i = 0; sSignalMap[i].target != -1 || sSignalMap[i].haiku != -1; i++) { |
| | 173 | if (haikuSignal == sSignalMap[i].haiku) |
| | 174 | return sSignalMap[i].target; |
| | 175 | } |
| | 176 | |
| | 177 | return TARGET_SIGNAL_UNKNOWN; |
| | 178 | } |
| | 179 | |
| | 180 | |
| | 181 | static thread_debug_info * |
| | 182 | haiku_find_thread(team_debug_info *teamDebugInfo, thread_id threadID) |
| | 183 | { |
| | 184 | thread_debug_info *info; |
| | 185 | for (info = teamDebugInfo->threads; info; info = info->next) { |
| | 186 | if (info->thread == threadID) |
| | 187 | return info; |
| | 188 | } |
| | 189 | |
| | 190 | return NULL; |
| | 191 | } |
| | 192 | |
| | 193 | |
| | 194 | static thread_debug_info * |
| | 195 | haiku_add_thread(team_debug_info *teamDebugInfo, thread_id threadID) |
| | 196 | { |
| | 197 | struct thread_info *gdbThreadInfo; |
| | 198 | thread_debug_info *threadDebugInfo; |
| | 199 | |
| | 200 | if (threadID == teamDebugInfo->nub_thread) { |
| | 201 | error("haiku_thread_added(): Trying to add debug nub thread (%ld)\n", |
| | 202 | threadID); |
| | 203 | } |
| | 204 | |
| | 205 | // find the thread first |
| | 206 | threadDebugInfo = haiku_find_thread(teamDebugInfo, threadID); |
| | 207 | if (threadDebugInfo) |
| | 208 | return threadDebugInfo; |
| | 209 | |
| | 210 | // allocate a new thread debug info |
| | 211 | threadDebugInfo = XMALLOC(thread_debug_info); |
| | 212 | if (!threadDebugInfo) |
| | 213 | error("haiku_thread_added(): Out of memory!\n"); |
| | 214 | |
| | 215 | // init and add it |
| | 216 | threadDebugInfo->thread = threadID; |
| | 217 | threadDebugInfo->next = teamDebugInfo->threads; |
| | 218 | threadDebugInfo->stopped = false; |
| | 219 | threadDebugInfo->last_event = NULL; |
| | 220 | teamDebugInfo->threads = threadDebugInfo; |
| | 221 | |
| | 222 | // add it to gdb's thread DB |
| | 223 | gdbThreadInfo = add_thread(ptid_build(teamDebugInfo->team, 0, threadID)); |
| | 224 | |
| | 225 | // Note: In theory we could spare us the whole thread list management, since |
| | 226 | // gdb's thread DB is doing exactly the same. We could put our data as |
| | 227 | // thread_info::private. The only catch is that when the thread_info is |
| | 228 | // freed, xfree() is invoked on the private data directly, but there's no |
| | 229 | // callback invoked before that would allow us to do cleanup (e.g. free |
| | 230 | // last_event). |
| | 231 | |
| | 232 | TRACE(("haiku_add_thread(): team %ld thread %ld added: " |
| | 233 | "gdb thread info: %p\n", teamDebugInfo->team, threadID, gdbThreadInfo)); |
| | 234 | |
| | 235 | return threadDebugInfo; |
| | 236 | } |
| | 237 | |
| | 238 | |
| | 239 | static void |
| | 240 | haiku_remove_thread(team_debug_info *teamDebugInfo, thread_id threadID) |
| | 241 | { |
| | 242 | thread_debug_info **info; |
| | 243 | for (info = &teamDebugInfo->threads; *info; info = &(*info)->next) { |
| | 244 | if ((*info)->thread == threadID) { |
| | 245 | thread_debug_info *foundInfo = *info; |
| | 246 | *info = foundInfo->next; |
| | 247 | if (foundInfo->last_event) |
| | 248 | xfree(foundInfo->last_event); |
| | 249 | xfree(foundInfo); |
| | 250 | |
| | 251 | // remove it from gdb's thread DB |
| | 252 | delete_thread(ptid_build(teamDebugInfo->team, 0, threadID)); |
| | 253 | |
| | 254 | return; |
| | 255 | } |
| | 256 | } |
| | 257 | } |
| | 258 | |
| | 259 | |
| | 260 | static void |
| | 261 | haiku_init_thread_list(team_debug_info *teamDebugInfo) |
| | 262 | { |
| | 263 | thread_info threadInfo; |
| | 264 | int32 cookie = 0; |
| | 265 | |
| | 266 | // init gdb's thread DB |
| | 267 | init_thread_list(); |
| | 268 | |
| | 269 | while (get_next_thread_info(teamDebugInfo->team, &cookie, &threadInfo) |
| | 270 | == B_OK) { |
| | 271 | if (threadInfo.thread != teamDebugInfo->nub_thread) |
| | 272 | haiku_add_thread(teamDebugInfo, threadInfo.thread); |
| | 273 | } |
| | 274 | } |
| | 275 | |
| | 276 | |
| | 277 | static void |
| | 278 | haiku_cleanup_thread_list(team_debug_info *teamDebugInfo) |
| | 279 | { |
| | 280 | while (teamDebugInfo->threads) { |
| | 281 | thread_debug_info *thread = teamDebugInfo->threads; |
| | 282 | teamDebugInfo->threads = thread->next; |
| | 283 | xfree(thread); |
| | 284 | } |
| | 285 | |
| | 286 | // clear gdb's thread DB |
| | 287 | init_thread_list(); |
| | 288 | } |
| | 289 | |
| | 290 | |
| | 291 | // #pragma mark - |
| | 292 | |
| | 293 | static extended_image_info ** |
| | 294 | haiku_find_image_insert_location(team_debug_info *teamDebugInfo, |
| | 295 | image_id imageID) |
| | 296 | { |
| | 297 | extended_image_info **image; |
| | 298 | |
| | 299 | for (image = &teamDebugInfo->images; *image; image = &(*image)->next) { |
| | 300 | if ((*image)->info.id >= imageID) |
| | 301 | return image; |
| | 302 | } |
| | 303 | |
| | 304 | return image; |
| | 305 | } |
| | 306 | |
| | 307 | |
| | 308 | static extended_image_info * |
| | 309 | haiku_find_image(team_debug_info *teamDebugInfo, image_id imageID) |
| | 310 | { |
| | 311 | extended_image_info *image = *haiku_find_image_insert_location( |
| | 312 | teamDebugInfo, imageID); |
| | 313 | |
| | 314 | return (image && image->info.id == imageID ? image : NULL); |
| | 315 | } |
| | 316 | |
| | 317 | |
| | 318 | static extended_image_info * |
| | 319 | haiku_add_image(team_debug_info *teamDebugInfo, image_info *imageInfo) |
| | 320 | { |
| | 321 | extended_image_info **imageP = haiku_find_image_insert_location( |
| | 322 | teamDebugInfo, imageInfo->id); |
| | 323 | extended_image_info *image = *imageP; |
| | 324 | |
| | 325 | // already known? |
| | 326 | if (image && image->info.id == imageInfo->id) |
| | 327 | return image; |
| | 328 | |
| | 329 | image = XMALLOC(extended_image_info); |
| | 330 | if (!image) |
| | 331 | error("haiku_add_image(): Out of memory!"); |
| | 332 | |
| | 333 | image->info.id = imageInfo->id; |
| | 334 | strncpy(image->info.name, imageInfo->name, sizeof(image->info.name)); |
| | 335 | image->info.name[sizeof(image->info.name) - 1] = '\0'; |
| | 336 | // TODO: This should be the shared objects soname, not the path. We |
| | 337 | // probably need to extend the debugger API. |
| | 338 | strncpy(image->info.path, imageInfo->name, sizeof(image->info.path)); |
| | 339 | image->info.path[sizeof(image->info.path) - 1] = '\0'; |
| | 340 | image->info.text_address = (CORE_ADDR)imageInfo->text; |
| | 341 | image->info.text_size = imageInfo->text_size; |
| | 342 | image->info.data_address = (CORE_ADDR)imageInfo->data; |
| | 343 | image->info.data_size = imageInfo->data_size; |
| | 344 | image->info.is_app_image = (imageInfo->type == B_APP_IMAGE); |
| | 345 | |
| | 346 | image->next = *imageP; |
| | 347 | *imageP = image; |
| | 348 | |
| | 349 | return image; |
| | 350 | } |
| | 351 | |
| | 352 | |
| | 353 | static void |
| | 354 | haiku_remove_image(team_debug_info *teamDebugInfo, image_id imageID) |
| | 355 | { |
| | 356 | extended_image_info **imageP = haiku_find_image_insert_location( |
| | 357 | teamDebugInfo, imageID); |
| | 358 | extended_image_info *image = *imageP; |
| | 359 | |
| | 360 | if (image && image->info.id == imageID) { |
| | 361 | *imageP = image->next; |
| | 362 | xfree(image); |
| | 363 | } |
| | 364 | } |
| | 365 | |
| | 366 | |
| | 367 | static void |
| | 368 | haiku_init_image_list(team_debug_info *teamDebugInfo) |
| | 369 | { |
| | 370 | int32 cookie = 0; |
| | 371 | image_info info; |
| | 372 | while (get_next_image_info(teamDebugInfo->team, &cookie, &info) == B_OK) |
| | 373 | haiku_add_image(teamDebugInfo, &info); |
| | 374 | } |
| | 375 | |
| | 376 | |
| | 377 | static void |
| | 378 | haiku_cleanup_image_list(team_debug_info *teamDebugInfo) |
| | 379 | { |
| | 380 | while (teamDebugInfo->images) { |
| | 381 | extended_image_info *image = teamDebugInfo->images; |
| | 382 | teamDebugInfo->images = image->next; |
| | 383 | xfree(image); |
| | 384 | } |
| | 385 | } |
| | 386 | |
| | 387 | |
| | 388 | /* |
| | 389 | * Service function. Call with -1 the first time. |
| | 390 | */ |
| | 391 | struct haiku_image_info * |
| | 392 | haiku_get_next_image_info(int lastID) |
| | 393 | { |
| | 394 | extended_image_info *image = *haiku_find_image_insert_location( |
| | 395 | &sTeamDebugInfo, (lastID >= 0 ? lastID : 0)); |
| | 396 | |
| | 397 | if (image && image->info.id == lastID) |
| | 398 | image = image->next; |
| | 399 | |
| | 400 | return (image ? &image->info : NULL); |
| | 401 | } |
| | 402 | |
| | 403 | |
| | 404 | // #pragma mark - |
| | 405 | |
| | 406 | static debug_event * |
| | 407 | haiku_enqueue_debug_event(debug_event_list *list, int32 message, |
| | 408 | debug_debugger_message_data *data, int32 size) |
| | 409 | { |
| | 410 | debug_event *event = XMALLOC(debug_event); |
| | 411 | |
| | 412 | if (!event) |
| | 413 | error("haiku_enqueue_debug_event(): Out of memory!\n"); |
| | 414 | |
| | 415 | // init the event |
| | 416 | event->next = NULL; |
| | 417 | event->message = message; |
| | 418 | memcpy(&event->data, data, |
| | 419 | (size >= 0 ? size : sizeof(debug_debugger_message_data))); |
| | 420 | |
| | 421 | // add it to the queue |
| | 422 | if (list->tail) { |
| | 423 | list->tail->next = event; |
| | 424 | list->tail = event; |
| | 425 | } else { |
| | 426 | list->head = list->tail = event; |
| | 427 | } |
| | 428 | |
| | 429 | return event; |
| | 430 | } |
| | 431 | |
| | 432 | |
| | 433 | static debug_event * |
| | 434 | haiku_dequeue_next_debug_event(debug_event_list *list) |
| | 435 | { |
| | 436 | debug_event *event = list->head; |
| | 437 | |
| | 438 | if (event) { |
| | 439 | // remove it from the queue |
| | 440 | list->head = event->next; |
| | 441 | if (list->tail == event) |
| | 442 | list->tail = NULL; |
| | 443 | |
| | 444 | event->next = NULL; // just because we're paranoid |
| | 445 | } |
| | 446 | |
| | 447 | return event; |
| | 448 | } |
| | 449 | |
| | 450 | |
| | 451 | static debug_event * |
| | 452 | haiku_remove_debug_event(debug_event_list *list, debug_event *eventToRemove) |
| | 453 | { |
| | 454 | debug_event **event; |
| | 455 | |
| | 456 | if (eventToRemove) { |
| | 457 | for (event = &list->head; *event; event = &(*event)->next) { |
| | 458 | if (*event == eventToRemove) { |
| | 459 | *event = (*event)->next; |
| | 460 | if (eventToRemove == list->tail) |
| | 461 | list->tail = NULL; |
| | 462 | return eventToRemove; |
| | 463 | } |
| | 464 | } |
| | 465 | } |
| | 466 | |
| | 467 | error("haiku_remove_debug_event(): event %p not found in list %p\n", |
| | 468 | eventToRemove, list); |
| | 469 | |
| | 470 | return NULL; |
| | 471 | } |
| | 472 | |
| | 473 | |
| | 474 | static void |
| | 475 | haiku_clear_debug_event_list(debug_event_list *list) |
| | 476 | { |
| | 477 | debug_event *event; |
| | 478 | |
| | 479 | while ((event = haiku_dequeue_next_debug_event(list))) |
| | 480 | xfree(event); |
| | 481 | } |
| | 482 | |
| | 483 | |
| | 484 | static debug_event * |
| | 485 | haiku_find_next_debug_event(debug_event_list *list, |
| | 486 | bool (*predicate)(void *closure, debug_event *event), void *closure) |
| | 487 | { |
| | 488 | debug_event *event; |
| | 489 | |
| | 490 | for (event = list->head; event; event = event->next) { |
| | 491 | if ((*predicate)(closure, event)) |
| | 492 | return event; |
| | 493 | } |
| | 494 | |
| | 495 | return NULL; |
| | 496 | } |
| | 497 | |
| | 498 | |
| | 499 | static void |
| | 500 | haiku_read_pending_debug_events(team_debug_info *teamDebugInfo, |
| | 501 | bool block, bool (*block_predicate)(void *closure, debug_event *event), |
| | 502 | void *closure) |
| | 503 | { |
| | 504 | while (true) { |
| | 505 | // read the next message from the debugger port |
| | 506 | debug_debugger_message_data message; |
| | 507 | int32 code; |
| | 508 | ssize_t bytesRead; |
| | 509 | debug_event *event; |
| | 510 | |
| | 511 | // TRACE(("haiku_read_pending_debug_events(): reading from debugger port " |
| | 512 | // "(%sblocking)...\n", (block ? "" : "non-"))); |
| | 513 | |
| | 514 | do { |
| | 515 | bytesRead = read_port_etc(teamDebugInfo->debugger_port, &code, |
| | 516 | &message, sizeof(message), (block ? 0 : B_RELATIVE_TIMEOUT), 0); |
| | 517 | } while (bytesRead == B_INTERRUPTED); |
| | 518 | |
| | 519 | if (bytesRead < 0) { |
| | 520 | if (bytesRead == B_WOULD_BLOCK && !block) |
| | 521 | break; |
| | 522 | |
| | 523 | error("Failed to read from debugger port: %s\n", |
| | 524 | strerror(bytesRead)); |
| | 525 | } |
| | 526 | |
| | 527 | // TRACE(("haiku_read_pending_debug_events(): got event: %lu, " |
| | 528 | // "thread: %ld, team: %ld, nub port: %ld\n", code, |
| | 529 | // message.origin.thread, message.origin.team, |
| | 530 | // message.origin.nub_port)); |
| | 531 | |
| | 532 | // got a message: queue it |
| | 533 | event = haiku_enqueue_debug_event(&teamDebugInfo->events, code, |
| | 534 | &message, bytesRead); |
| | 535 | |
| | 536 | block = !(*block_predicate)(closure, event); |
| | 537 | } |
| | 538 | } |
| | 539 | |
| | 540 | |
| | 541 | typedef struct thread_event_closure { |
| | 542 | team_debug_info *context; |
| | 543 | thread_id thread; |
| | 544 | debug_event *event; |
| | 545 | } thread_event_closure; |
| | 546 | |
| | 547 | |
| | 548 | static bool |
| | 549 | haiku_thread_event_predicate(void *_closure, debug_event *event) |
| | 550 | { |
| | 551 | thread_event_closure *closure = (thread_event_closure*)_closure; |
| | 552 | |
| | 553 | if (event->message == B_DEBUGGER_MESSAGE_TEAM_DELETED) { |
| | 554 | if (closure->context->team < 0 |
| | 555 | || event->data.origin.team == closure->context->team) { |
| | 556 | closure->event = event; |
| | 557 | } |
| | 558 | } |
| | 559 | |
| | 560 | if (!closure->event) { |
| | 561 | if (event->data.origin.team == closure->context->team |
| | 562 | && (closure->thread < 0 |
| | 563 | || closure->thread == event->data.origin.thread)) { |
| | 564 | closure->event = event; |
| | 565 | } |
| | 566 | } |
| | 567 | |
| | 568 | return (closure->event != NULL); |
| | 569 | } |
| | 570 | |
| | 571 | |
| | 572 | // #pragma mark - |
| | 573 | |
| | 574 | static void |
| | 575 | haiku_cleanup_team_debug_info() |
| | 576 | { |
| | 577 | destroy_debug_context(&sTeamDebugInfo.context); |
| | 578 | delete_port(sTeamDebugInfo.debugger_port); |
| | 579 | sTeamDebugInfo.debugger_port = -1; |
| | 580 | sTeamDebugInfo.team = -1; |
| | 581 | |
| | 582 | haiku_cleanup_thread_list(&sTeamDebugInfo); |
| | 583 | haiku_cleanup_image_list(&sTeamDebugInfo); |
| | 584 | } |
| | 585 | |
| | 586 | |
| | 587 | static status_t |
| | 588 | haiku_send_debugger_message(team_debug_info *teamDebugInfo, int32 messageCode, |
| | 589 | const void *message, int32 messageSize, void *reply, int32 replySize) |
| | 590 | { |
| | 591 | return send_debug_message(&teamDebugInfo->context, messageCode, |
| | 592 | message, messageSize, reply, replySize); |
| | 593 | } |
| | 594 | |
| | 595 | |
| | 596 | static void |
| | 597 | haiku_init_child_debugging (thread_id threadID, bool debugThread) |
| | 598 | { |
| | 599 | thread_info threadInfo; |
| | 600 | status_t result; |
| | 601 | port_id nubPort; |
| | 602 | |
| | 603 | // get a thread info |
| | 604 | result = get_thread_info(threadID, &threadInfo); |
| | 605 | if (result != B_OK) |
| | 606 | error("Thread with ID %ld not found: %s", threadID, strerror(result)); |
| | 607 | |
| | 608 | // init our team debug structure |
| | 609 | sTeamDebugInfo.team = threadInfo.team; |
| | 610 | sTeamDebugInfo.debugger_port = -1; |
| | 611 | sTeamDebugInfo.context.nub_port = -1; |
| | 612 | sTeamDebugInfo.context.reply_port = -1; |
| | 613 | sTeamDebugInfo.threads = NULL; |
| | 614 | sTeamDebugInfo.events.head = NULL; |
| | 615 | sTeamDebugInfo.events.tail = NULL; |
| | 616 | |
| | 617 | // create the debugger port |
| | 618 | sTeamDebugInfo.debugger_port = create_port(10, "gdb debug"); |
| | 619 | if (sTeamDebugInfo.debugger_port < 0) { |
| | 620 | error("Failed to create debugger port: %s", |
| | 621 | strerror(sTeamDebugInfo.debugger_port)); |
| | 622 | } |
| | 623 | |
| | 624 | // install ourselves as the team debugger |
| | 625 | nubPort = install_team_debugger(sTeamDebugInfo.team, |
| | 626 | sTeamDebugInfo.debugger_port); |
| | 627 | if (nubPort < 0) { |
| | 628 | error("Failed to install ourselves as debugger for team %ld: %s", |
| | 629 | sTeamDebugInfo.team, strerror(nubPort)); |
| | 630 | } |
| | 631 | |
| | 632 | // get the nub thread |
| | 633 | { |
| | 634 | team_info teamInfo; |
| | 635 | result = get_team_info(sTeamDebugInfo.team, &teamInfo); |
| | 636 | if (result != B_OK) { |
| | 637 | error("Failed to get info for team %ld: %s\n", |
| | 638 | sTeamDebugInfo.team, strerror(result)); |
| | 639 | } |
| | 640 | |
| | 641 | sTeamDebugInfo.nub_thread = teamInfo.debugger_nub_thread; |
| | 642 | } |
| | 643 | |
| | 644 | // init the debug context |
| | 645 | result = init_debug_context(&sTeamDebugInfo.context, sTeamDebugInfo.team, |
| | 646 | nubPort); |
| | 647 | if (result != B_OK) { |
| | 648 | error("Failed to init debug context for team %ld: %s\n", |
| | 649 | sTeamDebugInfo.team, strerror(result)); |
| | 650 | } |
| | 651 | |
| | 652 | // start debugging the thread |
| | 653 | if (debugThread) { |
| | 654 | result = debug_thread(threadID); |
| | 655 | if (result != B_OK) { |
| | 656 | error("Failed to start debugging thread %ld: %s", threadID, |
| | 657 | strerror(result)); |
| | 658 | } |
| | 659 | } |
| | 660 | |
| | 661 | // set the team debug flags |
| | 662 | { |
| | 663 | debug_nub_set_team_flags message; |
| | 664 | message.flags = B_TEAM_DEBUG_SIGNALS /*| B_TEAM_DEBUG_PRE_SYSCALL |
| | 665 | | B_TEAM_DEBUG_POST_SYSCALL*/ | B_TEAM_DEBUG_TEAM_CREATION |
| | 666 | | B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES; |
| | 667 | // TODO: We probably don't need all events |
| | 668 | |
| | 669 | haiku_send_debugger_message(&sTeamDebugInfo, |
| | 670 | B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message, sizeof(message), NULL, 0); |
| | 671 | } |
| | 672 | |
| | 673 | |
| | 674 | // the fun can start: push the target and init the rest |
| | 675 | push_target(sHaikuTarget); |
| | 676 | |
| | 677 | haiku_init_thread_list(&sTeamDebugInfo); |
| | 678 | haiku_init_image_list(&sTeamDebugInfo); |
| | 679 | |
| | 680 | disable_breakpoints_in_shlibs (); |
| | 681 | |
| | 682 | // child_clear_solibs (); |
| | 683 | // TODO: Implement? Do we need this? |
| | 684 | |
| | 685 | clear_proceed_status (); |
| | 686 | init_wait_for_inferior (); |
| | 687 | |
| | 688 | target_terminal_init (); |
| | 689 | target_terminal_inferior (); |
| | 690 | } |
| | 691 | |
| | 692 | |
| | 693 | static void |
| | 694 | haiku_continue_thread(team_debug_info *teamDebugInfo, thread_id threadID, |
| | 695 | uint32 handleEvent, bool step) |
| | 696 | { |
| | 697 | debug_nub_continue_thread message; |
| | 698 | status_t err; |
| | 699 | |
| | 700 | message.thread = threadID; |
| | 701 | message.handle_event = handleEvent; |
| | 702 | message.single_step = step; |
| | 703 | |
| | 704 | err = haiku_send_debugger_message(teamDebugInfo, |
| | 705 | B_DEBUG_MESSAGE_CONTINUE_THREAD, &message, sizeof(message), NULL, 0); |
| | 706 | if (err != B_OK) { |
| | 707 | printf_unfiltered ("Failed to resume thread %ld: %s\n", threadID, |
| | 708 | strerror(err)); |
| | 709 | } |
| | 710 | } |
| | 711 | |
| | 712 | |
| | 713 | static status_t |
| | 714 | haiku_stop_thread(team_debug_info *teamDebugInfo, thread_id threadID) |
| | 715 | { |
| | 716 | // check whether we know the thread |
| | 717 | status_t err; |
| | 718 | thread_event_closure threadEventClosure; |
| | 719 | |
| | 720 | thread_debug_info *threadInfo = haiku_find_thread(teamDebugInfo, threadID); |
| | 721 | if (!threadInfo) |
| | 722 | return B_BAD_THREAD_ID; |
| | 723 | |
| | 724 | // already stopped? |
| | 725 | if (threadInfo->stopped) |
| | 726 | return B_OK; |
| | 727 | |
| | 728 | // debug the thread |
| | 729 | err = debug_thread(threadID); |
| | 730 | if (err != B_OK) { |
| | 731 | TRACE(("haiku_stop_thread(): failed to debug thread %ld: %s\n", |
| | 732 | threadID, strerror(err))); |
| | 733 | return err; |
| | 734 | } |
| | 735 | |
| | 736 | // wait for the event to hit our port |
| | 737 | |
| | 738 | // TODO: debug_thread() doesn't guarantee that the thread will enter the |
| | 739 | // debug loop any time soon. E.g. a wait_for_thread() is (at the moment) |
| | 740 | // not interruptable, so we block here forever, if we already debug the |
| | 741 | // thread that is being waited for. We should probably limit the time |
| | 742 | // we're waiting, and return the info to the caller, which can then try |
| | 743 | // to deal with the situation in an appropriate way. |
| | 744 | |
| | 745 | // prepare closure for finding interesting events |
| | 746 | threadEventClosure.context = teamDebugInfo; |
| | 747 | threadEventClosure.thread = threadID; |
| | 748 | threadEventClosure.event = NULL; |
| | 749 | |
| | 750 | // find the first interesting queued event |
| | 751 | threadEventClosure.event = haiku_find_next_debug_event( |
| | 752 | &teamDebugInfo->events, haiku_thread_event_predicate, |
| | 753 | &threadEventClosure); |
| | 754 | |
| | 755 | // read all events pending on the port |
| | 756 | haiku_read_pending_debug_events(teamDebugInfo, |
| | 757 | (threadEventClosure.event == NULL), haiku_thread_event_predicate, |
| | 758 | &threadEventClosure); |
| | 759 | |
| | 760 | // We should check, whether we really got an event for the thread in |
| | 761 | // question, but the only possible other event is that the team has |
| | 762 | // been delete, which ends the game anyway. |
| | 763 | |
| | 764 | // TODO: That's actually not true. We also get messages when an add-on |
| | 765 | // has been loaded/unloaded, a signal arrives, or a thread calls the |
| | 766 | // debugger. |
| | 767 | |
| | 768 | return B_OK; |
| | 769 | } |
| | 770 | |
| | 771 | |
| | 772 | static status_t |
| | 773 | haiku_get_cpu_state(team_debug_info *teamDebugInfo, |
| | 774 | debug_nub_get_cpu_state_reply *reply) |
| | 775 | { |
| | 776 | status_t err; |
| | 777 | thread_id threadID = ptid_get_tid(inferior_ptid); |
| | 778 | debug_nub_get_cpu_state message; |
| | 779 | |
| | 780 | // make sure the thread is stopped |
| | 781 | err = haiku_stop_thread(teamDebugInfo, threadID); |
| | 782 | if (err != B_OK) { |
| | 783 | printf_unfiltered ("Failed to stop thread %ld: %s\n", |
| | 784 | threadID, strerror(err)); |
| | 785 | return err; |
| | 786 | } |
| | 787 | |
| | 788 | message.reply_port = teamDebugInfo->context.reply_port; |
| | 789 | message.thread = threadID; |
| | 790 | |
| | 791 | err = haiku_send_debugger_message(teamDebugInfo, |
| | 792 | B_DEBUG_MESSAGE_GET_CPU_STATE, &message, sizeof(message), reply, |
| | 793 | sizeof(*reply)); |
| | 794 | if (err == B_OK) |
| | 795 | err = reply->error; |
| | 796 | if (err != B_OK) { |
| | 797 | printf_unfiltered ("Failed to get status of thread %ld: %s\n", |
| | 798 | threadID, strerror(err)); |
| | 799 | } |
| | 800 | |
| | 801 | return err; |
| | 802 | } |
| | 803 | |
| | 804 | |
| | 805 | // #pragma mark - |
| | 806 | |
| | 807 | static void |
| | 808 | haiku_child_open (char *arg, int from_tty) |
| | 809 | { |
| | 810 | error ("Use the \"run\" command to start a child process."); |
| | 811 | } |
| | 812 | |
| | 813 | static void |
| | 814 | haiku_child_close (int x) |
| | 815 | { |
| | 816 | printf_unfiltered ("gdb: child_close, inferior_ptid=%d\n", |
| | 817 | PIDGET (inferior_ptid)); |
| | 818 | } |
| | 819 | |
| | 820 | |
| | 821 | static void |
| | 822 | haiku_child_attach (struct target_ops *ops, char *args, int from_tty) |
| | 823 | { |
| | 824 | extern int stop_after_trap; |
| | 825 | thread_id threadID; |
| | 826 | thread_info threadInfo; |
| | 827 | status_t result; |
| | 828 | |
| | 829 | TRACE(("haiku_child_attach(`%s', %d)\n", args, from_tty)); |
| | 830 | |
| | 831 | if (!args) |
| | 832 | error_no_arg ("thread-id to attach"); |
| | 833 | |
| | 834 | // get the thread ID |
| | 835 | threadID = strtoul (args, 0, 0); |
| | 836 | if (threadID <= 0) |
| | 837 | error("The given thread-id %ld is invalid.", threadID); |
| | 838 | |
| | 839 | haiku_init_child_debugging(threadID, true); |
| | 840 | |
| | 841 | target_terminal_ours (); |
| | 842 | |
| | 843 | TRACE(("haiku_child_attach() done\n")); |
| | 844 | } |
| | 845 | |
| | 846 | |
| | 847 | static void |
| | 848 | haiku_child_detach (struct target_ops *ops, char *args, int from_tty) |
| | 849 | { |
| | 850 | int detached = 1; |
| | 851 | status_t result; |
| | 852 | |
| | 853 | TRACE(("haiku_child_detach(`%s', %d)\n", args, from_tty)); |
| | 854 | |
| | 855 | result = remove_team_debugger(sTeamDebugInfo.team); |
| | 856 | if (result != B_OK) { |
| | 857 | error("Failed to detach process %ld: %s\n", sTeamDebugInfo.team, |
| | 858 | strerror(result)); |
| | 859 | } |
| | 860 | |
| | 861 | delete_command (NULL, 0); |
| | 862 | |
| | 863 | if (from_tty) { |
| | 864 | char *exec_file = get_exec_file (0); |
| | 865 | if (exec_file == 0) |
| | 866 | exec_file = ""; |
| | 867 | printf_unfiltered ("Detaching from program: %s, Pid %ld\n", exec_file, |
| | 868 | sTeamDebugInfo.team); |
| | 869 | gdb_flush (gdb_stdout); |
| | 870 | } |
| | 871 | |
| | 872 | haiku_cleanup_team_debug_info(); |
| | 873 | |
| | 874 | inferior_ptid = null_ptid; |
| | 875 | unpush_target (sHaikuTarget); |
| | 876 | } |
| | 877 | |
| | 878 | |
| | 879 | static void |
| | 880 | haiku_resume_thread(team_debug_info *teamDebugInfo, thread_debug_info *thread, |
| | 881 | int step, enum target_signal sig) |
| | 882 | { |
| | 883 | thread_id threadID = (thread ? thread->thread : -1); |
| | 884 | int pendingSignal = -1; |
| | 885 | int signalToSend = target_to_haiku_signal(sig); |
| | 886 | uint32 handleEvent = B_THREAD_DEBUG_HANDLE_EVENT; |
| | 887 | |
| | 888 | if (!thread || !thread->stopped) { |
| | 889 | TRACE(("haiku_resume_thread(): thread %ld not stopped\n", |
| | 890 | threadID)); |
| | 891 | return; |
| | 892 | } |
| | 893 | |
| | 894 | if (thread->reprocess_event > 0) { |
| | 895 | TRACE(("haiku_resume_thread(): thread %ld is expected to " |
| | 896 | "reprocess its current event\n", threadID)); |
| | 897 | return; |
| | 898 | } |
| | 899 | |
| | 900 | // get the pending signal |
| | 901 | if (thread->last_event) |
| | 902 | pendingSignal = thread->signal; |
| | 903 | |
| | 904 | // Decide what to do with the event and the pending and passed signal. |
| | 905 | // We simply adjust signalToSend, pendingSignal and handleEvent. |
| | 906 | // If signalToSend is set afterwards, we need to send it. pendingSignal we |
| | 907 | // need to ignore. handleEvent specifies whether to handle/ignore the event. |
| | 908 | if (signalToSend > 0) { |
| | 909 | if (pendingSignal > 0) { |
| | 910 | if (signalToSend == pendingSignal) { |
| | 911 | // signal to send is the pending signal |
| | 912 | // we don't need to send it |
| | 913 | signalToSend = -1; |
| | 914 | if (thread->signal_status != SIGNAL_WILL_ARRIVE) { |
| | 915 | // the signal has not yet been sent, so we need to ignore |
| | 916 | // it only in this case, but not in the other ones |
| | 917 | pendingSignal = -1; |
| | 918 | } |
| | 919 | } else { |
| | 920 | // signal to send is not the pending signal |
| | 921 | // If the signal has been sent or will be sent, we need to |
| | 922 | // ignore the event. |
| | 923 | if (thread->signal_status != SIGNAL_FAKED) |
| | 924 | handleEvent = B_THREAD_DEBUG_IGNORE_EVENT; |
| | 925 | // At any rate we don't need to ignore the signal. |
| | 926 | pendingSignal = -1; |
| | 927 | // send the signal |
| | 928 | } |
| | 929 | } else { |
| | 930 | // there's a signal to send, but no pending signal |
| | 931 | // handle the event |
| | 932 | // ignore the signal |
| | 933 | // send the signal |
| | 934 | } |
| | 935 | } else { |
| | 936 | if (pendingSignal > 0) { |
| | 937 | // there's a pending signal, but no signal to send |
| | 938 | // Ignore the event, if the signal has been sent or will be sent. |
| | 939 | if (thread->signal_status != SIGNAL_FAKED) |
| | 940 | handleEvent = B_THREAD_DEBUG_IGNORE_EVENT; |
| | 941 | } else { |
| | 942 | // there're neither signal to send nor pending signal |
| | 943 | // handle the event |
| | 944 | } |
| | 945 | } |
| | 946 | |
| | 947 | // ignore the pending signal, if necessary |
| | 948 | if (pendingSignal > 0) { |
| | 949 | debug_nub_set_signal_masks message; |
| | 950 | status_t err; |
| | 951 | |
| | 952 | message.thread = threadID; |
| | 953 | message.ignore_mask = 0; |
| | 954 | message.ignore_once_mask = B_DEBUG_SIGNAL_TO_MASK(pendingSignal); |
| | 955 | message.ignore_op = B_DEBUG_SIGNAL_MASK_OR; |
| | 956 | message.ignore_once_op = B_DEBUG_SIGNAL_MASK_OR; |
| | 957 | |
| | 958 | err = haiku_send_debugger_message(teamDebugInfo, |
| | 959 | B_DEBUG_MESSAGE_SET_SIGNAL_MASKS, &message, sizeof(message), |
| | 960 | NULL, 0); |
| | 961 | if (err != B_OK) { |
| | 962 | printf_unfiltered ("Set signal ignore masks for thread %ld: %s\n", |
| | 963 | threadID, strerror(err)); |
| | 964 | } |
| | 965 | } |
| | 966 | |
| | 967 | // send the signal |
| | 968 | if (signalToSend > 0) { |
| | 969 | if (send_signal(threadID, signalToSend) < 0) { |
| | 970 | printf_unfiltered ("Failed to send signal %d to thread %ld: %s\n", |
| | 971 | signalToSend, threadID, strerror(errno)); |
| | 972 | } |
| | 973 | } |
| | 974 | |
| | 975 | // resume thread |
| | 976 | haiku_continue_thread(teamDebugInfo, threadID, handleEvent, step); |
| | 977 | thread->stopped = false; |
| | 978 | } |
| | 979 | |
| | 980 | |
| | 981 | static void |
| | 982 | haiku_child_resume (struct target_ops *ops, ptid_t ptid, int step, |
| | 983 | enum target_signal sig) |
| | 984 | { |
| | 985 | team_id teamID = ptid_get_pid(ptid); |
| | 986 | thread_id threadID = ptid_get_tid(ptid); |
| | 987 | thread_debug_info *thread; |
| | 988 | |
| | 989 | TRACE(("haiku_child_resume(`%s', step: %d, signal: %d)\n", |
| | 990 | haiku_pid_to_str(ops, ptid), step, sig)); |
| | 991 | |
| | 992 | if (teamID < 0) { |
| | 993 | // resume all stopped threads (at least all haiku_wait_child() has |
| | 994 | // reported to be stopped) |
| | 995 | for (thread = sTeamDebugInfo.threads; thread; thread = thread->next) { |
| | 996 | if (thread->stopped) |
| | 997 | haiku_resume_thread(&sTeamDebugInfo, thread, step, sig); |
| | 998 | } |
| | 999 | } else { |
| | 1000 | // resume the thread in question |
| | 1001 | thread = haiku_find_thread(&sTeamDebugInfo, threadID); |
| | 1002 | if (thread) { |
| | 1003 | haiku_resume_thread(&sTeamDebugInfo, thread, step, sig); |
| | 1004 | } else { |
| | 1005 | printf_unfiltered ("haiku_child_resume(): unknown thread %ld\n", |
| | 1006 | threadID); |
| | 1007 | } |
| | 1008 | } |
| | 1009 | |
| | 1010 | } |
| | 1011 | |
| | 1012 | |
| | 1013 | static ptid_t |
| | 1014 | haiku_child_wait_internal (team_debug_info *teamDebugInfo, ptid_t ptid, |
| | 1015 | struct target_waitstatus *ourstatus, bool *ignore, bool *selectThread) |
| | 1016 | { |
| | 1017 | team_id teamID = ptid_get_pid(ptid); |
| | 1018 | team_id threadID = ptid_get_tid(ptid); |
| | 1019 | debug_event *event = NULL; |
| | 1020 | ptid_t retval = pid_to_ptid(-1); |
| | 1021 | struct thread_debug_info *thread; |
| | 1022 | int pendingSignal = -1; |
| | 1023 | pending_signal_status pendingSignalStatus = SIGNAL_FAKED; |
| | 1024 | int reprocessEvent = -1; |
| | 1025 | |
| | 1026 | *selectThread = false; |
| | 1027 | |
| | 1028 | if (teamID < 0 || threadID == 0) |
| | 1029 | threadID = -1; |
| | 1030 | |
| | 1031 | // if we're waiting for any thread, search the thread list for already |
| | 1032 | // stopped threads (needed for reprocessing events) |
| | 1033 | if (threadID < 0) { |
| | 1034 | for (thread = teamDebugInfo->threads; thread; thread = thread->next) { |
| | 1035 | if (thread->stopped) { |
| | 1036 | threadID = thread->thread; |
| | 1037 | break; |
| | 1038 | } |
| | 1039 | } |
| | 1040 | } |
| | 1041 | |
| | 1042 | // check, if the thread exists and is already stopped |
| | 1043 | if (threadID >= 0 |
| | 1044 | && (thread = haiku_find_thread(teamDebugInfo, threadID)) != NULL |
| | 1045 | && thread->stopped) { |
| | 1046 | // remove the event that stopped the thread from the thread (we will |
| | 1047 | // add it again, if it shall not be ignored) |
| | 1048 | event = thread->last_event; |
| | 1049 | thread->last_event = NULL; |
| | 1050 | thread->stopped = false; |
| | 1051 | reprocessEvent = thread->reprocess_event; |
| | 1052 | |
| | 1053 | TRACE(("haiku_child_wait_internal(): thread %ld already stopped. " |
| | 1054 | "re-digesting the last event (%p).\n", threadID, event)); |
| | 1055 | } else { |
| | 1056 | // prepare closure for finding interesting events |
| | 1057 | thread_event_closure threadEventClosure; |
| | 1058 | threadEventClosure.context = teamDebugInfo; |
| | 1059 | threadEventClosure.thread = threadID; |
| | 1060 | threadEventClosure.event = NULL; |
| | 1061 | |
| | 1062 | // find the first interesting queued event |
| | 1063 | threadEventClosure.event = haiku_find_next_debug_event( |
| | 1064 | &teamDebugInfo->events, haiku_thread_event_predicate, |
| | 1065 | &threadEventClosure); |
| | 1066 | |
| | 1067 | // read all events pending on the port |
| | 1068 | haiku_read_pending_debug_events(teamDebugInfo, |
| | 1069 | (threadEventClosure.event == NULL), haiku_thread_event_predicate, |
| | 1070 | &threadEventClosure); |
| | 1071 | |
| | 1072 | // get the event of interest |
| | 1073 | event = haiku_remove_debug_event(&teamDebugInfo->events, |
| | 1074 | threadEventClosure.event); |
| | 1075 | |
| | 1076 | if (!event) { |
| | 1077 | TRACE(("haiku_child_wait_internal(): got no event!\n")); |
| | 1078 | return retval; |
| | 1079 | } |
| | 1080 | |
| | 1081 | TRACE(("haiku_child_wait_internal(): got event: %u, thread: %ld, " |
| | 1082 | "team: %ld, nub port: %ld\n", event->message, |
| | 1083 | event->data.origin.thread, event->data.origin.team, |
| | 1084 | event->data.origin.nub_port)); |
| | 1085 | } |
| | 1086 | |
| | 1087 | if (event->data.origin.team != teamDebugInfo->team) { |
| | 1088 | // Spurious debug message. Doesn't concern our team. Ignore. |
| | 1089 | xfree(event); |
| | 1090 | return retval; |
| | 1091 | } |
| | 1092 | |
| | 1093 | retval = ptid_build(event->data.origin.team, 0, event->data.origin.thread); |
| | 1094 | |
| | 1095 | *ignore = false; |
| | 1096 | |
| | 1097 | switch (event->message) { |
| | 1098 | case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: |
| | 1099 | { |
| | 1100 | // print the debugger message |
| | 1101 | char debuggerMessage[1024]; |
| | 1102 | ssize_t bytesRead = debug_read_string(&teamDebugInfo->context, |
| | 1103 | event->data.debugger_call.message, debuggerMessage, |
| | 1104 | sizeof(debuggerMessage)); |
| | 1105 | if (bytesRead > 0) { |
| | 1106 | printf_unfiltered ("Thread %ld called debugger(): %s\n", |
| | 1107 | event->data.origin.thread, debuggerMessage); |
| | 1108 | } else { |
| | 1109 | printf_unfiltered ("Thread %ld called debugger(), but failed" |
| | 1110 | "to get the debugger message.\n", |
| | 1111 | event->data.origin.thread); |
| | 1112 | } |
| | 1113 | |
| | 1114 | // fall through... |
| | 1115 | } |
| | 1116 | case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: |
| | 1117 | case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: |
| | 1118 | case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: |
| | 1119 | case B_DEBUGGER_MESSAGE_SINGLE_STEP: |
| | 1120 | ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| | 1121 | ourstatus->value.sig = TARGET_SIGNAL_TRAP; |
| | 1122 | pendingSignal = SIGTRAP; |
| | 1123 | pendingSignalStatus = SIGNAL_FAKED; |
| | 1124 | break; |
| | 1125 | |
| | 1126 | case B_DEBUGGER_MESSAGE_PRE_SYSCALL: |
| | 1127 | ourstatus->kind = TARGET_WAITKIND_SYSCALL_ENTRY; |
| | 1128 | ourstatus->value.syscall_number = event->data.pre_syscall.syscall; |
| | 1129 | break; |
| | 1130 | |
| | 1131 | case B_DEBUGGER_MESSAGE_POST_SYSCALL: |
| | 1132 | ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN; |
| | 1133 | ourstatus->value.syscall_number = event->data.post_syscall.syscall; |
| | 1134 | break; |
| | 1135 | |
| | 1136 | case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: |
| | 1137 | pendingSignal = event->data.signal_received.signal; |
| | 1138 | pendingSignalStatus = SIGNAL_ARRIVED; |
| | 1139 | ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| | 1140 | ourstatus->value.sig = haiku_to_target_signal(pendingSignal); |
| | 1141 | break; |
| | 1142 | |
| | 1143 | case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: |
| | 1144 | { |
| | 1145 | // print the exception message |
| | 1146 | char exception[1024]; |
| | 1147 | get_debug_exception_string(event->data.exception_occurred.exception, |
| | 1148 | exception, sizeof(exception)); |
| | 1149 | printf_unfiltered ("Thread %ld caused an exception: %s\n", |
| | 1150 | event->data.origin.thread, exception); |
| | 1151 | |
| | 1152 | pendingSignal = event->data.exception_occurred.signal; |
| | 1153 | pendingSignalStatus = SIGNAL_WILL_ARRIVE; |
| | 1154 | ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| | 1155 | ourstatus->value.sig = haiku_to_target_signal(pendingSignal); |
| | 1156 | break; |
| | 1157 | } |
| | 1158 | |
| | 1159 | case B_DEBUGGER_MESSAGE_TEAM_CREATED: |
| | 1160 | // ignore |
| | 1161 | *ignore = true; |
| | 1162 | break; |
| | 1163 | |
| | 1164 | case B_DEBUGGER_MESSAGE_TEAM_DELETED: |
| | 1165 | ourstatus->kind = TARGET_WAITKIND_EXITED; |
| | 1166 | ourstatus->value.integer = 0; |
| | 1167 | // TODO: Extend the debugger interface? |
| | 1168 | break; |
| | 1169 | |
| | 1170 | case B_DEBUGGER_MESSAGE_THREAD_CREATED: |
| | 1171 | { |
| | 1172 | // internal bookkeeping only |
| | 1173 | thread_id newThread = event->data.thread_created.new_thread; |
| | 1174 | if (newThread != teamDebugInfo->nub_thread) |
| | 1175 | haiku_add_thread(teamDebugInfo, newThread); |
| | 1176 | *ignore = true; |
| | 1177 | break; |
| | 1178 | } |
| | 1179 | |
| | 1180 | case B_DEBUGGER_MESSAGE_THREAD_DELETED: |
| | 1181 | // internal bookkeeping |
| | 1182 | haiku_remove_thread(teamDebugInfo, |
| | 1183 | event->data.thread_deleted.origin.thread); |
| | 1184 | *ignore = true; |
| | 1185 | |
| | 1186 | // TODO: What if this is the current thread? |
| | 1187 | break; |
| | 1188 | |
| | 1189 | case B_DEBUGGER_MESSAGE_IMAGE_CREATED: |
| | 1190 | if (reprocessEvent < 0) { |
| | 1191 | // first time we see the event: update our image list |
| | 1192 | // haiku_add_image(teamDebugInfo, |
| | 1193 | // &event->data.image_created.info); |
| | 1194 | haiku_cleanup_image_list(teamDebugInfo); |
| | 1195 | haiku_init_image_list(teamDebugInfo); |
| | 1196 | // TODO: We don't get events when images have been removed in preparation of |
| | 1197 | // an exec*() yet. |
| | 1198 | } |
| | 1199 | |
| | 1200 | ourstatus->kind = TARGET_WAITKIND_LOADED; |
| | 1201 | ourstatus->value.integer = 0; |
| | 1202 | if (event->data.image_created.info.type == B_APP_IMAGE) { |
| | 1203 | // An app image has been loaded, i.e. the application is now |
| | 1204 | // fully loaded. We need to send a TARGET_WAITKIND_LOADED |
| | 1205 | // first, so that GDB's shared object list is updated correctly. |
| | 1206 | // But we also need to send an TARGET_WAITKIND_EXECD event. |
| | 1207 | // We use the reprocessEvent mechanism here. |
| | 1208 | |
| | 1209 | if (reprocessEvent == 2) { |
| | 1210 | #if 0 |
| | 1211 | // the second time the event is processed: send the `exec' |
| | 1212 | // event |
| | 1213 | if (inferior_ignoring_startup_exec_events > 0) { |
| | 1214 | // we shall not send an exec event, so we skip that |
| | 1215 | // and send the `trap' immediately |
| | 1216 | TRACE(("haiku_child_wait_internal(): ignoring exec (%d)\n", |
| | 1217 | inferior_ignoring_startup_exec_events)); |
| | 1218 | inferior_ignoring_startup_exec_events--; |
| | 1219 | reprocessEvent = 1; |
| | 1220 | } |
| | 1221 | #endif |
| | 1222 | } |
| | 1223 | |
| | 1224 | if (reprocessEvent < 0) { |
| | 1225 | TRACE(("haiku_child_wait_internal(): B_APP_IMAGE created, reprocess < 0\n")); |
| | 1226 | // the first time the event is processed: send the |
| | 1227 | // `loaded' event |
| | 1228 | reprocessEvent = 2; |
| | 1229 | } else if (reprocessEvent == 2) { |
| | 1230 | TRACE(("haiku_child_wait_internal(): B_APP_IMAGE created, reprocess -> exec\n")); |
| | 1231 | // the second time the event is processed: send the `exec' |
| | 1232 | // event |
| | 1233 | ourstatus->kind = TARGET_WAITKIND_EXECD; |
| | 1234 | ourstatus->value.execd_pathname |
| | 1235 | = xstrdup(event->data.image_created.info.name); |
| | 1236 | |
| | 1237 | reprocessEvent = 1; |
| | 1238 | } else if (reprocessEvent <= 1) { |
| | 1239 | // the third time the event is processed: send the `trap' |
| | 1240 | // event |
| | 1241 | ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| | 1242 | ourstatus->value.sig = TARGET_SIGNAL_TRAP; |
| | 1243 | pendingSignal = SIGTRAP; |
| | 1244 | pendingSignalStatus = SIGNAL_FAKED; |
| | 1245 | |
| | 1246 | reprocessEvent = 0; |
| | 1247 | } |
| | 1248 | } |
| | 1249 | |
| | 1250 | // re_enable_breakpoints_in_shlibs (); |
| | 1251 | // TODO: Needed? |
| | 1252 | break; |
| | 1253 | |
| | 1254 | case B_DEBUGGER_MESSAGE_IMAGE_DELETED: |
| | 1255 | haiku_remove_image(teamDebugInfo, |
| | 1256 | event->data.image_deleted.info.id); |
| | 1257 | |
| | 1258 | // send TARGET_WAITKIND_LOADED here too, it causes the shared |
| | 1259 | // object list to be updated |
| | 1260 | ourstatus->kind = TARGET_WAITKIND_LOADED; |
| | 1261 | ourstatus->value.integer = 0; |
| | 1262 | break; |
| | 1263 | |
| | 1264 | case B_DEBUGGER_MESSAGE_HANDED_OVER: |
| | 1265 | { |
| | 1266 | TRACE(("haiku_child_wait_internal(): B_DEBUGGER_MESSAGE_HANDED_OVER: causing " |
| | 1267 | "thread: %ld\n", event->data.handed_over.causing_thread)); |
| | 1268 | // The debugged team has been handed over to us by another debugger |
| | 1269 | // (likely the debug server). This event also tells us, which |
| | 1270 | // thread has caused the original debugger to be installed (if any). |
| | 1271 | // So, if we're not looking for any particular thread, select that |
| | 1272 | // thread. |
| | 1273 | if (threadID < 0 && event->data.handed_over.causing_thread >= 0) { |
| | 1274 | *selectThread = true; |
| | 1275 | retval = ptid_build(event->data.origin.team, 0, |
| | 1276 | event->data.handed_over.causing_thread); |
| | 1277 | } |
| | 1278 | *ignore = true; |
| | 1279 | } |
| | 1280 | |
| | 1281 | default: |
| | 1282 | // unknown message, ignore |
| | 1283 | *ignore = true; |
| | 1284 | break; |
| | 1285 | } |
| | 1286 | |
| | 1287 | // make the event the thread's last event or delete it |
| | 1288 | if (!*ignore && event->data.origin.thread >= 0) { |
| | 1289 | struct thread_debug_info *thread = haiku_find_thread(teamDebugInfo, |
| | 1290 | event->data.origin.thread); |
| | 1291 | if (thread->last_event) |
| | 1292 | xfree(thread->last_event); |
| | 1293 | thread->last_event = event; |
| | 1294 | thread->stopped = true; |
| | 1295 | thread->signal = pendingSignal; |
| | 1296 | thread->signal_status = pendingSignalStatus; |
| | 1297 | thread->reprocess_event |
| | 1298 | = (reprocessEvent >= 0 ? reprocessEvent : 0); |
| | 1299 | } else { |
| | 1300 | xfree(event); |
| | 1301 | |
| | 1302 | // continue the thread |
| | 1303 | if (event->data.origin.thread >= 0) { |
| | 1304 | haiku_continue_thread(teamDebugInfo, event->data.origin.thread, |
| | 1305 | B_THREAD_DEBUG_HANDLE_EVENT, false); |
| | 1306 | } |
| | 1307 | |
| | 1308 | // *ignore = true; |
| | 1309 | // TODO: This should indeed not be needed. It definitely eats the |
| | 1310 | // `team deleted' events. |
| | 1311 | } |
| | 1312 | |
| | 1313 | return retval; |
| | 1314 | } |
| | 1315 | |
| | 1316 | |
| | 1317 | static ptid_t |
| | 1318 | haiku_child_wait (struct target_ops *ops, ptid_t ptid, |
| | 1319 | struct target_waitstatus *ourstatus, int options) |
| | 1320 | { |
| | 1321 | ptid_t retval; |
| | 1322 | bool ignore = true; |
| | 1323 | bool selectThread = false; |
| | 1324 | |
| | 1325 | TRACE(("haiku_child_wait(`%s', %p)\n", |
| | 1326 | haiku_pid_to_str(ops, ptid), ourstatus)); |
| | 1327 | |
| | 1328 | do { |
| | 1329 | retval = haiku_child_wait_internal(&sTeamDebugInfo, ptid, ourstatus, |
| | 1330 | &ignore, &selectThread); |
| | 1331 | if (selectThread) |
| | 1332 | ptid = retval; |
| | 1333 | } while (ignore); |
| | 1334 | |
| | 1335 | TRACE(("haiku_child_wait() done: `%s'\n", haiku_pid_to_str(ops, retval))); |
| | 1336 | |
| | 1337 | return retval; |
| | 1338 | } |
| | 1339 | |
| | 1340 | |
| | 1341 | static void |
| | 1342 | haiku_child_fetch_inferior_registers (struct target_ops *ops, |
| | 1343 | struct regcache *cache, int reg) |
| | 1344 | { |
| | 1345 | debug_nub_get_cpu_state_reply reply; |
| | 1346 | |
| | 1347 | TRACE(("haiku_child_fetch_inferior_registers(%d)\n", reg)); |
| | 1348 | |
| | 1349 | // get the CPU state |
| | 1350 | haiku_get_cpu_state(&sTeamDebugInfo, &reply); |
| | 1351 | |
| | 1352 | // printf("haiku_child_fetch_inferior_registers(): eip: %p, ebp: %p\n", |
| | 1353 | // (void*)reply.cpu_state.eip, (void*)reply.cpu_state.ebp); |
| | 1354 | |
| | 1355 | // supply the registers (architecture specific) |
| | 1356 | haiku_supply_registers(reg, &reply.cpu_state); |
| | 1357 | } |
| | 1358 | |
| | 1359 | |
| | 1360 | static void |
| | 1361 | haiku_child_store_inferior_registers (struct target_ops *ops, |
| | 1362 | struct regcache *cache, int reg) |
| | 1363 | { |
| | 1364 | status_t err; |
| | 1365 | thread_id threadID = ptid_get_tid(inferior_ptid); |
| | 1366 | debug_nub_get_cpu_state_reply reply; |
| | 1367 | debug_nub_set_cpu_state message; |
| | 1368 | |
| | 1369 | TRACE(("haiku_child_store_inferior_registers(%d)\n", reg)); |
| | 1370 | |
| | 1371 | // get the current CPU state |
| | 1372 | haiku_get_cpu_state(&sTeamDebugInfo, &reply); |
| | 1373 | |
| | 1374 | // collect the registers (architecture specific) |
| | 1375 | haiku_collect_registers(reg, &reply.cpu_state); |
| | 1376 | |
| | 1377 | // set the new CPU state |
| | 1378 | message.thread = threadID; |
| | 1379 | memcpy(&message.cpu_state, &reply.cpu_state, sizeof(debug_cpu_state)); |
| | 1380 | err = haiku_send_debugger_message(&sTeamDebugInfo, |
| | 1381 | B_DEBUG_MESSAGE_SET_CPU_STATE, &message, sizeof(message), NULL, 0); |
| | 1382 | if (err != B_OK) { |
| | 1383 | printf_unfiltered ("Failed to set status of thread %ld: %s\n", |
| | 1384 | threadID, strerror(err)); |
| | 1385 | } |
| | 1386 | } |
| | 1387 | |
| | 1388 | static void |
| | 1389 | haiku_child_prepare_to_store (struct regcache *cache) |
| | 1390 | { |
| | 1391 | // Since we always fetching the current state in |
| | 1392 | // haiku_child_store_inferior_registers(), this should be a no-op. |
| | 1393 | } |
| | 1394 | |
| | 1395 | static int |
| | 1396 | haiku_child_deprecated_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len, |
| | 1397 | int write, struct mem_attrib *attrib, struct target_ops *target) |
| | 1398 | { |
| | 1399 | TRACE(("haiku_child_deprecated_xfer_memory(0x%8lx, %p, %d, %d, %p, %p)\n", |
| | 1400 | (uint32)memaddr, myaddr, len, write, attrib, target)); |
| | 1401 | |
| | 1402 | if (len <= 0) |
| | 1403 | return 0; |
| | 1404 | |
| | 1405 | if (write) { |
| | 1406 | // write |
| | 1407 | debug_nub_write_memory message; |
| | 1408 | debug_nub_write_memory_reply reply; |
| | 1409 | status_t err; |
| | 1410 | |
| | 1411 | if (len > B_MAX_READ_WRITE_MEMORY_SIZE) |
| | 1412 | len = B_MAX_READ_WRITE_MEMORY_SIZE; |
| | 1413 | |
| | 1414 | message.reply_port = sTeamDebugInfo.context.reply_port; |
| | 1415 | message.address = (void*)memaddr; |
| | 1416 | message.size = len; |
| | 1417 | memcpy(message.data, myaddr, len); |
| | 1418 | |
| | 1419 | err = haiku_send_debugger_message(&sTeamDebugInfo, |
| | 1420 | B_DEBUG_MESSAGE_WRITE_MEMORY, &message, sizeof(message), &reply, |
| | 1421 | sizeof(reply)); |
| | 1422 | if (err != B_OK || reply.error != B_OK) |
| | 1423 | { |
| | 1424 | TRACE(("haiku_child_deprecated_xfer_memory() failed: %lx\n", |
| | 1425 | (err != B_OK ? err : reply.error))); |
| | 1426 | return 0; |
| | 1427 | } |
| | 1428 | |
| | 1429 | TRACE(("haiku_child_deprecated_xfer_memory(): -> %ld\n", reply.size)); |
| | 1430 | return reply.size; |
| | 1431 | } else { |
| | 1432 | // read |
| | 1433 | ssize_t bytesRead = debug_read_memory_partial(&sTeamDebugInfo.context, |
| | 1434 | (const void *)memaddr, myaddr, len); |
| | 1435 | return (bytesRead < 0 ? 0 : bytesRead); |
| | 1436 | } |
| | 1437 | |
| | 1438 | return -1; |
| | 1439 | } |
| | 1440 | |
| | 1441 | LONGEST |
| | 1442 | haiku_child_xfer_partial (struct target_ops *ops, enum target_object object, |
| | 1443 | const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf, |
| | 1444 | ULONGEST offset, LONGEST len) |
| | 1445 | { |
| | 1446 | if (!readbuf && !writebuf) |
| | 1447 | return -1; |
| | 1448 | |
| | 1449 | switch (object) { |
| | 1450 | case TARGET_OBJECT_MEMORY: |
| | 1451 | { |
| | 1452 | int write = !readbuf; |
| | 1453 | return haiku_child_deprecated_xfer_memory (offset, |
| | 1454 | (write ? (void*)writebuf : readbuf), len, write, NULL, ops); |
| | 1455 | } |
| | 1456 | } |
| | 1457 | |
| | 1458 | return -1; |
| | 1459 | } |
| | 1460 | |
| | 1461 | static void |
| | 1462 | haiku_child_files_info (struct target_ops *ignore) |
| | 1463 | { |
| | 1464 | struct inferior *inf = current_inferior (); |
| | 1465 | printf_unfiltered ("\tUsing the running image of %s %s.\n", |
| | 1466 | inf->attach_flag ? "attached" : "child", |
| | 1467 | target_pid_to_str (inferior_ptid)); |
| | 1468 | } |
| | 1469 | |
| | 1470 | static void |
| | 1471 | haiku_child_kill_inferior (struct target_ops * ops) |
| | 1472 | { |
| | 1473 | status_t err; |
| | 1474 | thread_id teamID = ptid_get_pid(inferior_ptid); |
| | 1475 | |
| | 1476 | TRACE(("haiku_child_kill_inferior()\n")); |
| | 1477 | |
| | 1478 | err = kill_team(teamID); |
| | 1479 | if (err != B_OK) { |
| | 1480 | printf_unfiltered ("Failed to kill team %ld: %s\n", teamID, |
| | 1481 | strerror(err)); |
| | 1482 | return; |
| | 1483 | } |
| | 1484 | |
| | 1485 | target_mourn_inferior(); |
| | 1486 | } |
| | 1487 | |
| | 1488 | static void |
| | 1489 | haiku_child_stop_inferior (ptid_t ptid) |
| | 1490 | { |
| | 1491 | status_t err; |
| | 1492 | thread_id threadID = ptid_get_tid(inferior_ptid); |
| | 1493 | |
| | 1494 | TRACE(("haiku_child_stop_inferior()\n")); |
| | 1495 | |
| | 1496 | err = debug_thread(threadID); |
| | 1497 | if (err != B_OK) { |
| | 1498 | printf_unfiltered ("Failed to stop thread %ld: %s\n", threadID, |
| | 1499 | strerror(err)); |
| | 1500 | return; |
| | 1501 | } |
| | 1502 | } |
| | 1503 | |
| | 1504 | static void |
| | 1505 | haiku_init_debug_create_inferior(int pid) |
| | 1506 | { |
| | 1507 | extern int stop_after_trap; |
| | 1508 | struct inferior *inf = current_inferior (); |
| | 1509 | struct thread_info *tp; |
| | 1510 | |
| | 1511 | // fix inferior_ptid -- fork_inferior() sets the process ID only |
| | 1512 | inferior_ptid = ptid_build (pid, 0, pid); |
| | 1513 | // team ID == team main thread ID under Haiku |
| | 1514 | |
| | 1515 | haiku_init_child_debugging(pid, false); |
| | 1516 | |
| | 1517 | target_terminal_ours (); |
| | 1518 | |
| | 1519 | // eat the initial `debugged' event caused by wait_for_inferior() |
| | 1520 | // TODO: Maybe we should just dequeue the event and continue the thread instead |
| | 1521 | // of using gdb's mechanism, since I don't know what undesired side-effects |
| | 1522 | // they may have. |
| | 1523 | clear_proceed_status (); |
| | 1524 | init_wait_for_inferior (); |
| | 1525 | |
| | 1526 | inf->stop_soon = STOP_QUIETLY; |
| | 1527 | while (true) { |
| | 1528 | thread_debug_info *thread; |
| | 1529 | stop_after_trap = 1; |
| | 1530 | wait_for_inferior(0); |
| | 1531 | tp = inferior_thread (); |
| | 1532 | |
| | 1533 | if (tp->stop_signal == TARGET_SIGNAL_TRAP) { |
| | 1534 | thread = haiku_find_thread(&sTeamDebugInfo, pid); |
| | 1535 | |
| | 1536 | if (thread && thread->stopped |
| | 1537 | && thread->last_event->message |
| | 1538 | == B_DEBUGGER_MESSAGE_IMAGE_CREATED |
| | 1539 | && thread->last_event->data.image_created.info.type |
| | 1540 | == B_APP_IMAGE |
| | 1541 | && thread->reprocess_event == 0) { |
| | 1542 | // This is the trap for the last (second, if started via shell) |
| | 1543 | // `load app image' event. Be done. |
| | 1544 | break; |
| | 1545 | } |
| | 1546 | } |
| | 1547 | |
| | 1548 | resume(0, tp->stop_signal); |
| | 1549 | } |
| | 1550 | inf->stop_soon = NO_STOP_QUIETLY; |
| | 1551 | |
| | 1552 | // load shared library symbols |
| | 1553 | target_terminal_ours_for_output (); |
| | 1554 | #ifdef SOLIB_ADD |
| | 1555 | SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add); |
| | 1556 | #else |
| | 1557 | solib_add (NULL, 0, ¤t_target, auto_solib_add); |
| | 1558 | #endif |
| | 1559 | target_terminal_inferior (); |
| | 1560 | |
| | 1561 | // startup_inferior (START_INFERIOR_TRAPS_EXPECTED); |
| | 1562 | |
| | 1563 | //while (1) { |
| | 1564 | // stop_after_trap = 1; |
| | 1565 | // wait_for_inferior (); |
| | 1566 | // if (debugThread && stop_signal != TARGET_SIGNAL_TRAP) |
| | 1567 | // resume (0, stop_signal); |
| | 1568 | // else |
| | 1569 | // break; |
| | 1570 | //} |
| | 1571 | //stop_after_trap = 0; |
| | 1572 | |
| | 1573 | |
| | 1574 | |
| | 1575 | // while (1) { |
| | 1576 | // thread_debug_info *thread; |
| | 1577 | // |
| | 1578 | // stop_after_trap = 1; |
| | 1579 | // wait_for_inferior (); |
| | 1580 | //// TODO: Catch deadly events, so that we won't block here. |
| | 1581 | // |
| | 1582 | // thread = haiku_find_thread(&sTeamDebugInfo, pid); |
| | 1583 | //TRACE(("haiku_init_debug_create_inferior(): wait_for_inferior() returned: " |
| | 1584 | //"thread: %p (%ld)\n", thread, (thread ? thread->thread : -1))); |
| | 1585 | // if (thread && thread->stopped |
| | 1586 | // && thread->last_event->message |
| | 1587 | // == B_DEBUGGER_MESSAGE_IMAGE_CREATED |
| | 1588 | // && thread->last_event->data.image_created.info.type |
| | 1589 | // == B_APP_IMAGE) { |
| | 1590 | //TRACE(("haiku_init_debug_create_inferior(): Got an `app image created' " |
| | 1591 | //"message\n")); |
| | 1592 | // break; |
| | 1593 | // } |
| | 1594 | // |
| | 1595 | // resume (0, stop_signal); |
| | 1596 | // } |
| | 1597 | } |
| | 1598 | |
| | 1599 | static void |
| | 1600 | haiku_child_create_inferior (struct target_ops *ops, char *exec_file, |
| | 1601 | char *allargs, char **env, int from_tty) |
| | 1602 | { |
| | 1603 | TRACE(("haiku_child_create_inferior(`%s', `%s', %p, %d)\n", exec_file, |
| | 1604 | allargs, env, from_tty)); |
| | 1605 | |
| | 1606 | fork_inferior (exec_file, allargs, env, wait_for_debugger, |
| | 1607 | haiku_init_debug_create_inferior, NULL, NULL); |
| | 1608 | |
| | 1609 | |
| | 1610 | observer_notify_inferior_created (¤t_target, from_tty); |
| | 1611 | proceed ((CORE_ADDR) - 1, TARGET_SIGNAL_0, 0); |
| | 1612 | |
| | 1613 | |
| | 1614 | // TODO: Anything more to do here? |
| | 1615 | } |
| | 1616 | |
| | 1617 | static void |
| | 1618 | haiku_child_mourn_inferior (struct target_ops *ops) |
| | 1619 | { |
| | 1620 | TRACE(("haiku_child_mourn_inferior()\n")); |
| | 1621 | |
| | 1622 | haiku_cleanup_team_debug_info(); |
| | 1623 | unpush_target (sHaikuTarget); |
| | 1624 | generic_mourn_inferior (); |
| | 1625 | } |
| | 1626 | |
| | 1627 | static int |
| | 1628 | haiku_child_can_run (void) |
| | 1629 | { |
| | 1630 | return 1; |
| | 1631 | } |
| | 1632 | |
| | 1633 | static int |
| | 1634 | haiku_child_thread_alive (struct target_ops *ops, ptid_t ptid) |
| | 1635 | { |
| | 1636 | thread_info info; |
| | 1637 | |
| | 1638 | TRACE(("haiku_child_thread_alive(`%s')\n", haiku_pid_to_str(ops, ptid))); |
| | 1639 | |
| | 1640 | return (get_thread_info(ptid_get_tid(ptid), &info) == B_OK); |
| | 1641 | } |
| | 1642 | |
| | 1643 | static char * |
| | 1644 | haiku_pid_to_str (struct target_ops *ops, ptid_t ptid) |
| | 1645 | { |
| | 1646 | static char buffer[B_OS_NAME_LENGTH + 64 + 64]; |
| | 1647 | team_info teamInfo; |
| | 1648 | thread_info threadInfo; |
| | 1649 | status_t error; |
| | 1650 | |
| | 1651 | // get the team info for the target team |
| | 1652 | error = get_team_info(ptid_get_pid(ptid), &teamInfo); |
| | 1653 | if (error != B_OK) { |
| | 1654 | sprintf(buffer, "invalid team ID %d", ptid_get_pid(ptid)); |
| | 1655 | return buffer; |
| | 1656 | } |
| | 1657 | |
| | 1658 | // get the thread info for the target thread |
| | 1659 | error = get_thread_info(ptid_get_tid(ptid), &threadInfo); |
| | 1660 | if (error != B_OK) { |
| | 1661 | sprintf(buffer, "team %.*s (%ld) invalid thread ID %ld", |
| | 1662 | (int)sizeof(teamInfo.args), teamInfo.args, teamInfo.team, |
| | 1663 | ptid_get_tid(ptid)); |
| | 1664 | return buffer; |
| | 1665 | } |
| | 1666 | |
| | 1667 | sprintf(buffer, "team %.*s (%ld) thread %s (%ld)", |
| | 1668 | (int)sizeof(teamInfo.args), teamInfo.args, teamInfo.team, |
| | 1669 | threadInfo.name, threadInfo.thread); |
| | 1670 | |
| | 1671 | return buffer; |
| | 1672 | } |
| | 1673 | |
| | 1674 | char * |
| | 1675 | haiku_child_pid_to_exec_file (int pid) |
| | 1676 | { |
| | 1677 | static char buffer[B_PATH_NAME_LENGTH]; |
| | 1678 | |
| | 1679 | // The only way to get the path to the application's executable seems to |
| | 1680 | // be to get an image_info of its image, which also contains a path. |
| | 1681 | // Several images may belong to the team (libraries, add-ons), but only |
| | 1682 | // the one in question should be typed B_APP_IMAGE. |
| | 1683 | image_info info; |
| | 1684 | int32 cookie = 0; |
| | 1685 | |
| | 1686 | TRACE(("haiku_child_pid_to_exec_file(%ld)\n", pid)); |
| | 1687 | while (get_next_image_info(0, &cookie, &info) == B_OK) { |
| | 1688 | if (info.type == B_APP_IMAGE) { |
| | 1689 | strncpy(buffer, info.name, B_PATH_NAME_LENGTH - 1); |
| | 1690 | buffer[B_PATH_NAME_LENGTH - 1] = 0; |
| | 1691 | return buffer; |
| | 1692 | } |
| | 1693 | } |
| | 1694 | |
| | 1695 | return NULL; |
| | 1696 | } |
| | 1697 | |
| | 1698 | |
| | 1699 | void |
| | 1700 | _initialize_haiku_nat (void) |
| | 1701 | { |
| | 1702 | // child operations |
| | 1703 | struct target_ops *t = XZALLOC (struct target_ops); |
| | 1704 | t->to_shortname = "child"; |
| | 1705 | t->to_longname = "Haiku child process"; |
| | 1706 | t->to_doc = "Haiku child process (started by the \"run\" command)."; |
| | 1707 | t->to_open = haiku_child_open; |
| | 1708 | t->to_close = haiku_child_close; |
| | 1709 | t->to_attach = haiku_child_attach; |
| | 1710 | t->to_detach = haiku_child_detach; |
| | 1711 | t->to_resume = haiku_child_resume; |
| | 1712 | t->to_wait = haiku_child_wait; |
| | 1713 | t->to_fetch_registers = haiku_child_fetch_inferior_registers; |
| | 1714 | t->to_store_registers = haiku_child_store_inferior_registers; |
| | 1715 | t->to_prepare_to_store = haiku_child_prepare_to_store; |
| | 1716 | t->deprecated_xfer_memory = haiku_child_deprecated_xfer_memory; |
| | 1717 | t->to_xfer_partial = haiku_child_xfer_partial; |
| | 1718 | t->to_files_info = haiku_child_files_info; |
| | 1719 | |
| | 1720 | t->to_insert_breakpoint = memory_insert_breakpoint; |
| | 1721 | t->to_remove_breakpoint = memory_remove_breakpoint; |
| | 1722 | // TODO: We can do better. If we wanted to. |
| | 1723 | |
| | 1724 | // TODO: The following terminal calls are not documented or not yet |
| | 1725 | // understood by me. |
| | 1726 | t->to_terminal_init = terminal_init_inferior; |
| | 1727 | t->to_terminal_inferior = terminal_inferior; |
| | 1728 | t->to_terminal_ours_for_output = terminal_ours_for_output; |
| | 1729 | t->to_terminal_ours = terminal_ours; |
| | 1730 | t->to_terminal_save_ours = terminal_save_ours; |
| | 1731 | t->to_terminal_info = child_terminal_info; |
| | 1732 | |
| | 1733 | t->to_kill = haiku_child_kill_inferior; |
| | 1734 | t->to_stop = haiku_child_stop_inferior; |
| | 1735 | t->to_create_inferior = haiku_child_create_inferior; |
| | 1736 | t->to_mourn_inferior = haiku_child_mourn_inferior; |
| | 1737 | t->to_can_run = haiku_child_can_run; |
| | 1738 | t->to_thread_alive = haiku_child_thread_alive; |
| | 1739 | // How about to_find_new_threads? Perhaps not necessary, as we could be informed |
| | 1740 | // about thread creation/deletion. |
| | 1741 | t->to_pid_to_str = haiku_pid_to_str; |
| | 1742 | t->to_pid_to_exec_file = haiku_child_pid_to_exec_file; |
| | 1743 | |
| | 1744 | t->to_stratum = process_stratum; |
| | 1745 | t->to_has_all_memory = default_child_has_all_memory; |
| | 1746 | t->to_has_memory = default_child_has_memory; |
| | 1747 | t->to_has_stack = default_child_has_stack; |
| | 1748 | t->to_has_registers = default_child_has_registers; |
| | 1749 | t->to_has_execution = default_child_has_execution; |
| | 1750 | t->to_magic = OPS_MAGIC; |
| | 1751 | |
| | 1752 | sHaikuTarget = t; |
| | 1753 | |
| | 1754 | add_target (t); |
| | 1755 | } |