handle_event.c revision 44965c7a1d3106439b20e8f36307f506df3df44b
1#define _GNU_SOURCE 2#include "config.h" 3 4#include <stdio.h> 5#include <string.h> 6#include <stdlib.h> 7#include <signal.h> 8#include <assert.h> 9#include <sys/time.h> 10#include <errno.h> 11 12#ifdef __powerpc__ 13#include <sys/ptrace.h> 14#endif 15 16#include "common.h" 17#include "breakpoint.h" 18#include "proc.h" 19#include "library.h" 20 21static void handle_signal(Event *event); 22static void handle_exit(Event *event); 23static void handle_exit_signal(Event *event); 24static void handle_syscall(Event *event); 25static void handle_arch_syscall(Event *event); 26static void handle_sysret(Event *event); 27static void handle_arch_sysret(Event *event); 28static void handle_clone(Event *event); 29static void handle_exec(Event *event); 30static void handle_breakpoint(Event *event); 31static void handle_new(Event *event); 32 33static void callstack_push_syscall(Process *proc, int sysnum); 34static void callstack_push_symfunc(Process *proc, 35 struct library_symbol *sym); 36static void callstack_pop(Process *proc); 37 38static char * shortsignal(Process *proc, int signum); 39static char * sysname(Process *proc, int sysnum); 40static char * arch_sysname(Process *proc, int sysnum); 41 42static Event * 43call_handler(Process * proc, Event * event) 44{ 45 assert(proc != NULL); 46 47 struct event_handler *handler = proc->event_handler; 48 if (handler == NULL) 49 return event; 50 51 return (*handler->on_event) (handler, event); 52} 53 54void 55handle_event(Event *event) 56{ 57 if (exiting == 1) { 58 debug(1, "ltrace about to exit"); 59 os_ltrace_exiting(); 60 exiting = 2; 61 } 62 debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", 63 event->proc ? event->proc->pid : -1, event->type); 64 65 /* If the thread group or an individual task define an 66 overriding event handler, give them a chance to kick in. 67 We will end up calling both handlers, if the first one 68 doesn't sink the event. */ 69 if (event->proc != NULL) { 70 event = call_handler(event->proc, event); 71 if (event == NULL) 72 /* It was handled. */ 73 return; 74 75 /* Note: the previous handler has a chance to alter 76 * the event. */ 77 if (event->proc != NULL 78 && event->proc->leader != NULL 79 && event->proc != event->proc->leader) { 80 event = call_handler(event->proc->leader, event); 81 if (event == NULL) 82 return; 83 } 84 } 85 86 switch (event->type) { 87 case EVENT_NONE: 88 debug(1, "event: none"); 89 return; 90 case EVENT_SIGNAL: 91 debug(1, "event: signal (%s [%d])", 92 shortsignal(event->proc, event->e_un.signum), 93 event->e_un.signum); 94 handle_signal(event); 95 return; 96 case EVENT_EXIT: 97 debug(1, "event: exit (%d)", event->e_un.ret_val); 98 handle_exit(event); 99 return; 100 case EVENT_EXIT_SIGNAL: 101 debug(1, "event: exit signal (%s [%d])", 102 shortsignal(event->proc, event->e_un.signum), 103 event->e_un.signum); 104 handle_exit_signal(event); 105 return; 106 case EVENT_SYSCALL: 107 debug(1, "event: syscall (%s [%d])", 108 sysname(event->proc, event->e_un.sysnum), 109 event->e_un.sysnum); 110 handle_syscall(event); 111 return; 112 case EVENT_SYSRET: 113 debug(1, "event: sysret (%s [%d])", 114 sysname(event->proc, event->e_un.sysnum), 115 event->e_un.sysnum); 116 handle_sysret(event); 117 return; 118 case EVENT_ARCH_SYSCALL: 119 debug(1, "event: arch_syscall (%s [%d])", 120 arch_sysname(event->proc, event->e_un.sysnum), 121 event->e_un.sysnum); 122 handle_arch_syscall(event); 123 return; 124 case EVENT_ARCH_SYSRET: 125 debug(1, "event: arch_sysret (%s [%d])", 126 arch_sysname(event->proc, event->e_un.sysnum), 127 event->e_un.sysnum); 128 handle_arch_sysret(event); 129 return; 130 case EVENT_CLONE: 131 case EVENT_VFORK: 132 debug(1, "event: clone (%u)", event->e_un.newpid); 133 handle_clone(event); 134 return; 135 case EVENT_EXEC: 136 debug(1, "event: exec()"); 137 handle_exec(event); 138 return; 139 case EVENT_BREAKPOINT: 140 debug(1, "event: breakpoint"); 141 handle_breakpoint(event); 142 return; 143 case EVENT_NEW: 144 debug(1, "event: new process"); 145 handle_new(event); 146 return; 147 default: 148 fprintf(stderr, "Error! unknown event?\n"); 149 exit(1); 150 } 151} 152 153typedef struct Pending_New Pending_New; 154struct Pending_New { 155 pid_t pid; 156 Pending_New * next; 157}; 158static Pending_New * pending_news = NULL; 159 160static int 161pending_new(pid_t pid) { 162 Pending_New * p; 163 164 debug(DEBUG_FUNCTION, "pending_new(%d)", pid); 165 166 p = pending_news; 167 while (p) { 168 if (p->pid == pid) { 169 return 1; 170 } 171 p = p->next; 172 } 173 return 0; 174} 175 176static void 177pending_new_insert(pid_t pid) { 178 Pending_New * p; 179 180 debug(DEBUG_FUNCTION, "pending_new_insert(%d)", pid); 181 182 p = malloc(sizeof(Pending_New)); 183 if (!p) { 184 perror("malloc()"); 185 exit(1); 186 } 187 p->pid = pid; 188 p->next = pending_news; 189 pending_news = p; 190} 191 192static void 193pending_new_remove(pid_t pid) { 194 Pending_New *p, *pred; 195 196 debug(DEBUG_FUNCTION, "pending_new_remove(%d)", pid); 197 198 p = pending_news; 199 if (p->pid == pid) { 200 pending_news = p->next; 201 free(p); 202 } else { 203 while (p) { 204 if (p->pid == pid) { 205 pred->next = p->next; 206 free(p); 207 } 208 pred = p; 209 p = p->next; 210 } 211 } 212} 213 214#if 0 215static int 216clone_breakpoints(Process * proc, Process * orig_proc) 217{ 218 /* When copying breakpoints, we also have to copy the 219 * referenced symbols, and link them properly. */ 220 Dict * map = dict_init(&dict_key2hash_int, &dict_key_cmp_int); 221 struct library_symbol * it = proc->list_of_symbols; 222 proc->list_of_symbols = NULL; 223 for (; it != NULL; it = it->next) { 224 struct library_symbol * libsym = clone_library_symbol(it); 225 if (libsym == NULL) { 226 int save_errno; 227 err: 228 save_errno = errno; 229 destroy_library_symbol_chain(proc->list_of_symbols); 230 dict_clear(map); 231 errno = save_errno; 232 return -1; 233 } 234 libsym->next = proc->list_of_symbols; 235 proc->list_of_symbols = libsym; 236 if (dict_enter(map, it, libsym) != 0) 237 goto err; 238 } 239 240 proc->breakpoints = dict_clone2(orig_proc->breakpoints, 241 address_clone, breakpoint_clone, map); 242 if (proc->breakpoints == NULL) 243 goto err; 244 245 dict_clear(map); 246 return 0; 247} 248#endif 249 250static void 251handle_clone(Event *event) 252{ 253 debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); 254 255 struct Process *proc = malloc(sizeof(*proc)); 256 if (proc == NULL) { 257 fail: 258 free(proc); 259 /* XXX proper error handling here, please. */ 260 perror("malloc()"); 261 exit(1); 262 } 263 264 if (process_clone(proc, event->proc, event->e_un.newpid) < 0) 265 goto fail; 266 proc->parent = event->proc; 267 268 /* We save register values to the arch pointer, and these need 269 to be per-thread. */ 270 proc->arch_ptr = NULL; 271 272 if (pending_new(proc->pid)) { 273 pending_new_remove(proc->pid); 274 /* XXX this used to be destroy_event_handler call, but 275 * I don't think we want to call that on a shared 276 * state. */ 277 proc->event_handler = NULL; 278 if (event->proc->state == STATE_ATTACHED && options.follow) 279 proc->state = STATE_ATTACHED; 280 else 281 proc->state = STATE_IGNORED; 282 continue_process(proc->pid); 283 } else { 284 proc->state = STATE_BEING_CREATED; 285 } 286 287 if (event->type == EVENT_VFORK) 288 continue_after_vfork(proc); 289 else 290 continue_process(event->proc->pid); 291} 292 293static void 294handle_new(Event * event) { 295 Process * proc; 296 297 debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); 298 299 proc = pid2proc(event->e_un.newpid); 300 if (!proc) { 301 pending_new_insert(event->e_un.newpid); 302 } else { 303 assert(proc->state == STATE_BEING_CREATED); 304 if (options.follow) { 305 proc->state = STATE_ATTACHED; 306 } else { 307 proc->state = STATE_IGNORED; 308 } 309 continue_process(proc->pid); 310 } 311} 312 313static char * 314shortsignal(Process *proc, int signum) { 315 static char *signalent0[] = { 316#include "signalent.h" 317 }; 318 static char *signalent1[] = { 319#include "signalent1.h" 320 }; 321 static char **signalents[] = { signalent0, signalent1 }; 322 int nsignals[] = { sizeof signalent0 / sizeof signalent0[0], 323 sizeof signalent1 / sizeof signalent1[0] 324 }; 325 326 debug(DEBUG_FUNCTION, "shortsignal(pid=%d, signum=%d)", proc->pid, signum); 327 328 if (proc->personality > sizeof signalents / sizeof signalents[0]) 329 abort(); 330 if (signum < 0 || signum >= nsignals[proc->personality]) { 331 return "UNKNOWN_SIGNAL"; 332 } else { 333 return signalents[proc->personality][signum]; 334 } 335} 336 337static char * 338sysname(Process *proc, int sysnum) { 339 static char result[128]; 340 static char *syscalent0[] = { 341#include "syscallent.h" 342 }; 343 static char *syscalent1[] = { 344#include "syscallent1.h" 345 }; 346 static char **syscalents[] = { syscalent0, syscalent1 }; 347 int nsyscals[] = { sizeof syscalent0 / sizeof syscalent0[0], 348 sizeof syscalent1 / sizeof syscalent1[0] 349 }; 350 351 debug(DEBUG_FUNCTION, "sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); 352 353 if (proc->personality > sizeof syscalents / sizeof syscalents[0]) 354 abort(); 355 if (sysnum < 0 || sysnum >= nsyscals[proc->personality]) { 356 sprintf(result, "SYS_%d", sysnum); 357 return result; 358 } else { 359 sprintf(result, "SYS_%s", 360 syscalents[proc->personality][sysnum]); 361 return result; 362 } 363} 364 365static char * 366arch_sysname(Process *proc, int sysnum) { 367 static char result[128]; 368 static char *arch_syscalent[] = { 369#include "arch_syscallent.h" 370 }; 371 int nsyscals = sizeof arch_syscalent / sizeof arch_syscalent[0]; 372 373 debug(DEBUG_FUNCTION, "arch_sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); 374 375 if (sysnum < 0 || sysnum >= nsyscals) { 376 sprintf(result, "ARCH_%d", sysnum); 377 return result; 378 } else { 379 sprintf(result, "ARCH_%s", 380 arch_syscalent[sysnum]); 381 return result; 382 } 383} 384 385static void 386handle_signal(Event *event) { 387 debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); 388 if (event->proc->state != STATE_IGNORED && !options.no_signals) { 389 output_line(event->proc, "--- %s (%s) ---", 390 shortsignal(event->proc, event->e_un.signum), 391 strsignal(event->e_un.signum)); 392 } 393 continue_after_signal(event->proc->pid, event->e_un.signum); 394} 395 396static void 397handle_exit(Event *event) { 398 debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); 399 if (event->proc->state != STATE_IGNORED) { 400 output_line(event->proc, "+++ exited (status %d) +++", 401 event->e_un.ret_val); 402 } 403 remove_process(event->proc); 404 free(event->proc); 405} 406 407static void 408handle_exit_signal(Event *event) { 409 debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); 410 if (event->proc->state != STATE_IGNORED) { 411 output_line(event->proc, "+++ killed by %s +++", 412 shortsignal(event->proc, event->e_un.signum)); 413 } 414 remove_process(event->proc); 415 free(event->proc); 416} 417 418static struct library_symbol * 419temporary_syscall_symbol(const char *name) 420{ 421 struct library *syscalls = malloc(sizeof(*syscalls)); 422 struct library_symbol *syscall = malloc(sizeof(*syscall)); 423 if (syscalls == NULL || syscall == NULL) { 424 free(syscalls); 425 free(syscall); 426 return NULL; 427 } 428 library_init(syscalls, (enum library_type)-1); 429 library_set_soname(syscalls, "SYS", 0); 430 library_symbol_init(syscall, 0, name, 0, LS_TOPLT_NONE); 431 library_add_symbol(syscalls, syscall); 432 return syscall; 433} 434 435static void 436output_syscall_left(struct Process *proc, const char *name) 437{ 438 struct library_symbol *syscall = temporary_syscall_symbol(name); 439 output_left(LT_TOF_SYSCALL, proc, syscall); 440 struct library *lib = syscall->lib; 441 library_destroy(lib); 442 free(lib); 443} 444 445static void 446output_syscall_right(struct Process *proc, const char *name) 447{ 448 struct library_symbol *syscall = temporary_syscall_symbol(name); 449 output_right(LT_TOF_SYSCALLR, proc, syscall); 450 struct library *lib = syscall->lib; 451 library_destroy(lib); 452 free(lib); 453} 454 455static void 456handle_syscall(Event *event) { 457 debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); 458 if (event->proc->state != STATE_IGNORED) { 459 callstack_push_syscall(event->proc, event->e_un.sysnum); 460 if (options.syscalls) 461 output_syscall_left(event->proc, 462 sysname(event->proc, 463 event->e_un.sysnum)); 464 } 465 continue_after_syscall(event->proc, event->e_un.sysnum, 0); 466} 467 468static void 469handle_exec(Event * event) { 470 Process * proc = event->proc; 471 472 debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); 473 if (proc->state == STATE_IGNORED) { 474 untrace_pid(proc->pid); 475 remove_process(proc); 476 free(proc); 477 return; 478 } 479 output_line(proc, "--- Called exec() ---"); 480 proc->mask_32bit = 0; 481 proc->personality = 0; 482 proc->arch_ptr = NULL; 483 free(proc->filename); 484 proc->filename = pid2name(proc->pid); 485 breakpoints_init(proc, 0); 486 proc->callstack_depth = 0; 487 continue_process(proc->pid); 488} 489 490static void 491handle_arch_syscall(Event *event) { 492 debug(DEBUG_FUNCTION, "handle_arch_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); 493 if (event->proc->state != STATE_IGNORED) { 494 callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); 495 if (options.syscalls) { 496 output_syscall_left(event->proc, 497 arch_sysname(event->proc, 498 event->e_un.sysnum)); 499 } 500 } 501 continue_process(event->proc->pid); 502} 503 504struct timeval current_time_spent; 505 506static void 507calc_time_spent(Process *proc) { 508 struct timeval tv; 509 struct timezone tz; 510 struct timeval diff; 511 struct callstack_element *elem; 512 513 debug(DEBUG_FUNCTION, "calc_time_spent(pid=%d)", proc->pid); 514 elem = &proc->callstack[proc->callstack_depth - 1]; 515 516 gettimeofday(&tv, &tz); 517 518 diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec; 519 if (tv.tv_usec >= elem->time_spent.tv_usec) { 520 diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec; 521 } else { 522 diff.tv_sec++; 523 diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec; 524 } 525 current_time_spent = diff; 526} 527 528static void 529handle_sysret(Event *event) { 530 debug(DEBUG_FUNCTION, "handle_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); 531 if (event->proc->state != STATE_IGNORED) { 532 if (opt_T || options.summary) { 533 calc_time_spent(event->proc); 534 } 535 if (options.syscalls) 536 output_syscall_right(event->proc, 537 sysname(event->proc, 538 event->e_un.sysnum)); 539 540 assert(event->proc->callstack_depth > 0); 541 unsigned d = event->proc->callstack_depth - 1; 542 assert(event->proc->callstack[d].is_syscall); 543 callstack_pop(event->proc); 544 } 545 continue_after_syscall(event->proc, event->e_un.sysnum, 1); 546} 547 548static void 549handle_arch_sysret(Event *event) { 550 debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); 551 if (event->proc->state != STATE_IGNORED) { 552 if (opt_T || options.summary) { 553 calc_time_spent(event->proc); 554 } 555 if (options.syscalls) 556 output_syscall_right(event->proc, 557 arch_sysname(event->proc, 558 event->e_un.sysnum)); 559 callstack_pop(event->proc); 560 } 561 continue_process(event->proc->pid); 562} 563 564static void 565output_right_tos(struct Process *proc) 566{ 567 size_t d = proc->callstack_depth; 568 struct callstack_element *elem = &proc->callstack[d - 1]; 569 if (proc->state != STATE_IGNORED) 570 output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc->name); 571} 572 573static void 574handle_breakpoint(Event *event) 575{ 576 int i, j; 577 struct breakpoint *sbp; 578 Process *leader = event->proc->leader; 579 void *brk_addr = event->e_un.brk_addr; 580 581 /* The leader has terminated. */ 582 if (leader == NULL) { 583 continue_process(event->proc->pid); 584 return; 585 } 586 587 debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", 588 event->proc->pid, brk_addr); 589 debug(2, "event: breakpoint (%p)", brk_addr); 590 591 for (i = event->proc->callstack_depth - 1; i >= 0; i--) { 592 if (brk_addr == event->proc->callstack[i].return_addr) { 593#if defined(__mips__) 594 void *addr = NULL; 595 struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; 596 struct library_symbol *new_sym; 597 assert(sym); 598 addr = sym2addr(event->proc, sym); 599 sbp = dict_find_entry(leader->breakpoints, addr); 600 if (sbp) { 601 if (addr != sbp->addr) { 602 insert_breakpoint(event->proc, addr, sym); 603 } 604 } else { 605 new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); 606 memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); 607 new_sym->next = leader->list_of_symbols; 608 leader->list_of_symbols = new_sym; 609 insert_breakpoint(event->proc, addr, new_sym); 610 } 611#endif 612 for (j = event->proc->callstack_depth - 1; j > i; j--) { 613 callstack_pop(event->proc); 614 } 615 if (event->proc->state != STATE_IGNORED) { 616 if (opt_T || options.summary) { 617 calc_time_spent(event->proc); 618 } 619 } 620 event->proc->return_addr = brk_addr; 621 622 output_right_tos(event->proc); 623 callstack_pop(event->proc); 624 625 /* Pop also any other entries that seem like 626 * they are linked to the current one: they 627 * have the same return address, but were made 628 * for different symbols. This should only 629 * happen for entry point tracing, i.e. for -x 630 * everywhere, or -x and -e on PPC64. */ 631 while (event->proc->callstack_depth > 0) { 632 struct callstack_element *prev; 633 size_t d = event->proc->callstack_depth; 634 prev = &event->proc->callstack[d - 1]; 635 636 if (prev->c_un.libfunc == libsym 637 || prev->return_addr != brk_addr) 638 break; 639 640 output_right_tos(event->proc); 641 callstack_pop(event->proc); 642 } 643 644 sbp = address2bpstruct(leader, brk_addr); 645 continue_after_breakpoint(event->proc, sbp); 646 return; 647 } 648 } 649 650 if ((sbp = address2bpstruct(leader, brk_addr))) { 651 breakpoint_on_hit(sbp, event->proc); 652 653 if (event->proc->state != STATE_IGNORED 654 && sbp->libsym != NULL) { 655 event->proc->stack_pointer = get_stack_pointer(event->proc); 656 event->proc->return_addr = 657 get_return_addr(event->proc, event->proc->stack_pointer); 658 callstack_push_symfunc(event->proc, sbp->libsym); 659 output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); 660 } 661 662 breakpoint_on_continue(sbp, event->proc); 663 return; 664 } 665 666 if (event->proc->state != STATE_IGNORED) 667 output_line(event->proc, "unexpected breakpoint at %p", 668 brk_addr); 669 670 continue_process(event->proc->pid); 671} 672 673static void 674callstack_push_syscall(Process *proc, int sysnum) { 675 struct callstack_element *elem; 676 677 debug(DEBUG_FUNCTION, "callstack_push_syscall(pid=%d, sysnum=%d)", proc->pid, sysnum); 678 /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ 679 if (proc->callstack_depth == MAX_CALLDEPTH - 1) { 680 fprintf(stderr, "%s: Error: call nesting too deep!\n", __func__); 681 abort(); 682 return; 683 } 684 685 elem = &proc->callstack[proc->callstack_depth]; 686 elem->is_syscall = 1; 687 elem->c_un.syscall = sysnum; 688 elem->return_addr = NULL; 689 690 proc->callstack_depth++; 691 if (opt_T || options.summary) { 692 struct timezone tz; 693 gettimeofday(&elem->time_spent, &tz); 694 } 695} 696 697static void 698callstack_push_symfunc(Process *proc, struct library_symbol *sym) { 699 struct callstack_element *elem; 700 701 debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); 702 /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ 703 if (proc->callstack_depth == MAX_CALLDEPTH - 1) { 704 fprintf(stderr, "%s: Error: call nesting too deep!\n", __func__); 705 abort(); 706 return; 707 } 708 709 elem = &proc->callstack[proc->callstack_depth++]; 710 elem->is_syscall = 0; 711 elem->c_un.libfunc = sym; 712 713 elem->return_addr = proc->return_addr; 714 if (elem->return_addr) 715 insert_breakpoint(proc, elem->return_addr, NULL); 716 717 /* handle functions like atexit() on mips which have no return */ 718 if (opt_T || options.summary) { 719 struct timezone tz; 720 gettimeofday(&elem->time_spent, &tz); 721 } 722} 723 724static void 725callstack_pop(Process *proc) { 726 struct callstack_element *elem; 727 assert(proc->callstack_depth > 0); 728 729 debug(DEBUG_FUNCTION, "callstack_pop(pid=%d)", proc->pid); 730 elem = &proc->callstack[proc->callstack_depth - 1]; 731 if (!elem->is_syscall && elem->return_addr) { 732 assert(proc->leader != NULL); 733 delete_breakpoint(proc, elem->return_addr); 734 } 735 if (elem->arch_ptr != NULL) { 736 free(elem->arch_ptr); 737 elem->arch_ptr = NULL; 738 } 739 proc->callstack_depth--; 740} 741