1/* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2010-2012, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7#include "DebuggerInterface.h" 8 9#include <new> 10 11#include <stdio.h> 12 13#include <Locker.h> 14 15#include <AutoLocker.h> 16#include <commpage_defs.h> 17#include <OS.h> 18#include <system_info.h> 19#include <util/DoublyLinkedList.h> 20#include <util/KMessage.h> 21 22#include "debug_utils.h" 23 24#include "ArchitectureX86.h" 25#include "ArchitectureX8664.h" 26#include "CpuState.h" 27#include "DebugEvent.h" 28#include "ImageInfo.h" 29#include "SymbolInfo.h" 30#include "ThreadInfo.h" 31 32 33// number of debug contexts the pool does initially create 34static const int kInitialDebugContextCount = 3; 35 36// maximum number of debug contexts in the pool 37static const int kMaxDebugContextCount = 10; 38 39 40struct DebuggerInterface::DebugContext : debug_context, 41 DoublyLinkedListLinkImpl<DebugContext> { 42 DebugContext() 43 { 44 team = -1; 45 nub_port = -1; 46 reply_port = -1; 47 } 48 49 ~DebugContext() 50 { 51 if (reply_port >= 0) 52 destroy_debug_context(this); 53 } 54 55 status_t Init(team_id team, port_id nubPort) 56 { 57 return init_debug_context(this, team, nubPort); 58 } 59 60 void Close() 61 { 62 if (reply_port >= 0) { 63 destroy_debug_context(this); 64 team = -1; 65 nub_port = -1; 66 reply_port = -1; 67 } 68 } 69}; 70 71 72struct DebuggerInterface::DebugContextPool { 73 DebugContextPool(team_id team, port_id nubPort) 74 : 75 fLock("debug context pool"), 76 fTeam(team), 77 fNubPort(nubPort), 78 fBlockSem(-1), 79 fContextCount(0), 80 fWaiterCount(0), 81 fClosed(false) 82 { 83 } 84 85 ~DebugContextPool() 86 { 87 AutoLocker<BLocker> locker(fLock); 88 89 while (DebugContext* context = fFreeContexts.RemoveHead()) 90 delete context; 91 92 if (fBlockSem >= 0) 93 delete_sem(fBlockSem); 94 } 95 96 status_t Init() 97 { 98 status_t error = fLock.InitCheck(); 99 if (error != B_OK) 100 return error; 101 102 fBlockSem = create_sem(0, "debug context pool block"); 103 if (fBlockSem < 0) 104 return fBlockSem; 105 106 for (int i = 0; i < kInitialDebugContextCount; i++) { 107 DebugContext* context; 108 error = _CreateDebugContext(context); 109 if (error != B_OK) 110 return error; 111 112 fFreeContexts.Add(context); 113 } 114 115 return B_OK; 116 } 117 118 void Close() 119 { 120 AutoLocker<BLocker> locker(fLock); 121 fClosed = true; 122 123 for (DebugContextList::Iterator it = fFreeContexts.GetIterator(); 124 DebugContext* context = it.Next();) { 125 context->Close(); 126 } 127 128 for (DebugContextList::Iterator it = fUsedContexts.GetIterator(); 129 DebugContext* context = it.Next();) { 130 context->Close(); 131 } 132 } 133 134 DebugContext* GetContext() 135 { 136 AutoLocker<BLocker> locker(fLock); 137 DebugContext* context = fFreeContexts.RemoveHead(); 138 139 if (context == NULL) { 140 if (fContextCount >= kMaxDebugContextCount 141 || _CreateDebugContext(context) != B_OK) { 142 // wait for a free context 143 while (context == NULL) { 144 fWaiterCount++; 145 locker.Unlock(); 146 while (acquire_sem(fBlockSem) != B_OK); 147 locker.Lock(); 148 context = fFreeContexts.RemoveHead(); 149 } 150 } 151 } 152 153 fUsedContexts.Add(context); 154 155 return context; 156 } 157 158 void PutContext(DebugContext* context) 159 { 160 AutoLocker<BLocker> locker(fLock); 161 fUsedContexts.Remove(context); 162 fFreeContexts.Add(context); 163 164 if (fWaiterCount > 0) 165 release_sem(fBlockSem); 166 } 167 168private: 169 typedef DoublyLinkedList<DebugContext> DebugContextList; 170 171private: 172 status_t _CreateDebugContext(DebugContext*& _context) 173 { 174 DebugContext* context = new(std::nothrow) DebugContext; 175 if (context == NULL) 176 return B_NO_MEMORY; 177 178 if (!fClosed) { 179 status_t error = context->Init(fTeam, fNubPort); 180 if (error != B_OK) { 181 delete context; 182 return error; 183 } 184 } 185 186 fContextCount++; 187 188 _context = context; 189 return B_OK; 190 } 191 192private: 193 BLocker fLock; 194 team_id fTeam; 195 port_id fNubPort; 196 sem_id fBlockSem; 197 int32 fContextCount; 198 int32 fWaiterCount; 199 DebugContextList fFreeContexts; 200 DebugContextList fUsedContexts; 201 bool fClosed; 202}; 203 204 205struct DebuggerInterface::DebugContextGetter { 206 DebugContextGetter(DebugContextPool* pool) 207 : 208 fPool(pool), 209 fContext(pool->GetContext()) 210 { 211 } 212 213 ~DebugContextGetter() 214 { 215 fPool->PutContext(fContext); 216 } 217 218 DebugContext* Context() const 219 { 220 return fContext; 221 } 222 223private: 224 DebugContextPool* fPool; 225 DebugContext* fContext; 226}; 227 228// #pragma mark - DebuggerInterface 229 230DebuggerInterface::DebuggerInterface(team_id teamID) 231 : 232 fTeamID(teamID), 233 fDebuggerPort(-1), 234 fNubPort(-1), 235 fDebugContextPool(NULL), 236 fArchitecture(NULL) 237{ 238} 239 240 241DebuggerInterface::~DebuggerInterface() 242{ 243 if (fArchitecture != NULL) 244 fArchitecture->ReleaseReference(); 245 246 Close(false); 247 248 delete fDebugContextPool; 249} 250 251 252status_t 253DebuggerInterface::Init() 254{ 255 // create the architecture 256 // TODO: this probably needs to be rethought a bit, 257 // since especially when we eventually support remote debugging, 258 // the architecture will depend on the target machine, not the host 259#if defined(ARCH_x86) 260 fArchitecture = new(std::nothrow) ArchitectureX86(this); 261#elif defined(ARCH_x86_64) 262 fArchitecture = new(std::nothrow) ArchitectureX8664(this); 263#else 264 return B_UNSUPPORTED; 265#endif 266 267 if (fArchitecture == NULL) 268 return B_NO_MEMORY; 269 270 status_t error = fArchitecture->Init(); 271 if (error != B_OK) 272 return error; 273 274 // create debugger port 275 char buffer[128]; 276 snprintf(buffer, sizeof(buffer), "team %" B_PRId32 " debugger", fTeamID); 277 fDebuggerPort = create_port(100, buffer); 278 if (fDebuggerPort < 0) 279 return fDebuggerPort; 280 281 // install as team debugger 282 fNubPort = install_team_debugger(fTeamID, fDebuggerPort); 283 if (fNubPort < 0) 284 return fNubPort; 285 286 error = __start_watching_system(fTeamID, B_WATCH_SYSTEM_THREAD_PROPERTIES, 287 fDebuggerPort, 0); 288 if (error != B_OK) 289 return error; 290// TODO: Stop watching in Close()! 291 292 // create debug context pool 293 fDebugContextPool = new(std::nothrow) DebugContextPool(fTeamID, fNubPort); 294 if (fDebugContextPool == NULL) 295 return B_NO_MEMORY; 296 297 error = fDebugContextPool->Init(); 298 if (error != B_OK) 299 return error; 300 301 return B_OK; 302} 303 304 305void 306DebuggerInterface::Close(bool killTeam) 307{ 308 if (killTeam) 309 kill_team(fTeamID); 310 else if (fNubPort >= 0) 311 remove_team_debugger(fTeamID); 312 313 if (fDebuggerPort >= 0) 314 delete_port(fDebuggerPort); 315} 316 317 318status_t 319DebuggerInterface::GetNextDebugEvent(DebugEvent*& _event) 320{ 321 while (true) { 322 char buffer[2048]; 323 int32 messageCode; 324 ssize_t size = read_port(fDebuggerPort, &messageCode, buffer, 325 sizeof(buffer)); 326 if (size < 0) { 327 if (size == B_INTERRUPTED) 328 continue; 329 330 return size; 331 } 332 333 if (messageCode <= B_DEBUGGER_MESSAGE_HANDED_OVER) { 334 debug_debugger_message_data message; 335 memcpy(&message, buffer, size); 336 if (message.origin.team != fTeamID) 337 continue; 338 339 bool ignore = false; 340 status_t error = _CreateDebugEvent(messageCode, message, ignore, 341 _event); 342 if (error != B_OK) 343 return error; 344 345 if (ignore) { 346 if (message.origin.thread >= 0 && message.origin.nub_port >= 0) 347 continue_thread(message.origin.nub_port, 348 message.origin.thread); 349 continue; 350 } 351 352 return B_OK; 353 } 354 355 KMessage message; 356 size = message.SetTo(buffer); 357 if (size != B_OK) 358 return size; 359 return _GetNextSystemWatchEvent(_event, message); 360 } 361 362 return B_OK; 363} 364 365 366status_t 367DebuggerInterface::SetTeamDebuggingFlags(uint32 flags) 368{ 369 set_team_debugging_flags(fNubPort, flags); 370 return B_OK; 371} 372 373 374status_t 375DebuggerInterface::ContinueThread(thread_id thread) 376{ 377 continue_thread(fNubPort, thread); 378 return B_OK; 379} 380 381 382status_t 383DebuggerInterface::StopThread(thread_id thread) 384{ 385 return debug_thread(thread); 386} 387 388 389status_t 390DebuggerInterface::SingleStepThread(thread_id thread) 391{ 392 debug_nub_continue_thread continueMessage; 393 continueMessage.thread = thread; 394 continueMessage.handle_event = B_THREAD_DEBUG_HANDLE_EVENT; 395 continueMessage.single_step = true; 396 397 return write_port(fNubPort, B_DEBUG_MESSAGE_CONTINUE_THREAD, 398 &continueMessage, sizeof(continueMessage)); 399} 400 401 402status_t 403DebuggerInterface::InstallBreakpoint(target_addr_t address) 404{ 405 DebugContextGetter contextGetter(fDebugContextPool); 406 407 debug_nub_set_breakpoint message; 408 message.reply_port = contextGetter.Context()->reply_port; 409 message.address = (void*)(addr_t)address; 410 411 debug_nub_set_breakpoint_reply reply; 412 413 status_t error = send_debug_message(contextGetter.Context(), 414 B_DEBUG_MESSAGE_SET_BREAKPOINT, &message, sizeof(message), &reply, 415 sizeof(reply)); 416 return error == B_OK ? reply.error : error; 417} 418 419 420status_t 421DebuggerInterface::UninstallBreakpoint(target_addr_t address) 422{ 423 debug_nub_clear_breakpoint message; 424 message.address = (void*)(addr_t)address; 425 426 return write_port(fNubPort, B_DEBUG_MESSAGE_CLEAR_BREAKPOINT, 427 &message, sizeof(message)); 428} 429 430 431status_t 432DebuggerInterface::InstallWatchpoint(target_addr_t address, uint32 type, 433 int32 length) 434{ 435 DebugContextGetter contextGetter(fDebugContextPool); 436 437 debug_nub_set_watchpoint message; 438 message.reply_port = contextGetter.Context()->reply_port; 439 message.address = (void*)(addr_t)address; 440 message.type = type; 441 message.length = length; 442 443 debug_nub_set_watchpoint_reply reply; 444 445 status_t error = send_debug_message(contextGetter.Context(), 446 B_DEBUG_MESSAGE_SET_WATCHPOINT, &message, sizeof(message), &reply, 447 sizeof(reply)); 448 return error == B_OK ? reply.error : error; 449} 450 451 452status_t 453DebuggerInterface::UninstallWatchpoint(target_addr_t address) 454{ 455 DebugContextGetter contextGetter(fDebugContextPool); 456 457 debug_nub_clear_watchpoint message; 458 message.address = (void*)(addr_t)address; 459 460 return write_port(fNubPort, B_DEBUG_MESSAGE_CLEAR_WATCHPOINT, 461 &message, sizeof(message)); 462} 463 464 465status_t 466DebuggerInterface::GetThreadInfos(BObjectList<ThreadInfo>& infos) 467{ 468 thread_info threadInfo; 469 int32 cookie = 0; 470 while (get_next_thread_info(fTeamID, &cookie, &threadInfo) == B_OK) { 471 ThreadInfo* info = new(std::nothrow) ThreadInfo(threadInfo.team, 472 threadInfo.thread, threadInfo.name); 473 if (info == NULL || !infos.AddItem(info)) { 474 delete info; 475 return B_NO_MEMORY; 476 } 477 } 478 479 return B_OK; 480} 481 482 483status_t 484DebuggerInterface::GetImageInfos(BObjectList<ImageInfo>& infos) 485{ 486 // get the team's images 487 image_info imageInfo; 488 int32 cookie = 0; 489 while (get_next_image_info(fTeamID, &cookie, &imageInfo) == B_OK) { 490 ImageInfo* info = new(std::nothrow) ImageInfo(fTeamID, imageInfo.id, 491 imageInfo.name, imageInfo.type, (addr_t)imageInfo.text, 492 imageInfo.text_size, (addr_t)imageInfo.data, imageInfo.data_size); 493 if (info == NULL || !infos.AddItem(info)) { 494 delete info; 495 return B_NO_MEMORY; 496 } 497 } 498 499 // Also add the "commpage" image, which belongs to the kernel, but is used 500 // by userland teams. 501 cookie = 0; 502 while (get_next_image_info(B_SYSTEM_TEAM, &cookie, &imageInfo) == B_OK) { 503 if ((addr_t)imageInfo.text >= USER_COMMPAGE_ADDR 504 && (addr_t)imageInfo.text < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) { 505 ImageInfo* info = new(std::nothrow) ImageInfo(B_SYSTEM_TEAM, 506 imageInfo.id, imageInfo.name, imageInfo.type, 507 (addr_t)imageInfo.text, imageInfo.text_size, 508 (addr_t)imageInfo.data, imageInfo.data_size); 509 if (info == NULL || !infos.AddItem(info)) { 510 delete info; 511 return B_NO_MEMORY; 512 } 513 break; 514 } 515 } 516 517 return B_OK; 518} 519 520 521status_t 522DebuggerInterface::GetSymbolInfos(team_id team, image_id image, 523 BObjectList<SymbolInfo>& infos) 524{ 525 // create a lookup context 526// TODO: It's too expensive to create a lookup context for each image! 527 debug_symbol_lookup_context* lookupContext; 528 status_t error = debug_create_symbol_lookup_context(team, &lookupContext); 529 if (error != B_OK) 530 return error; 531 532 // create a symbol iterator 533 debug_symbol_iterator* iterator; 534 error = debug_create_image_symbol_iterator( 535 lookupContext, image, &iterator); 536 if (error != B_OK) { 537 debug_delete_symbol_lookup_context(lookupContext); 538 return error; 539 } 540 541 // get the symbols 542 char name[1024]; 543 int32 type; 544 void* address; 545 size_t size; 546 while (debug_next_image_symbol(iterator, name, sizeof(name), &type, 547 &address, &size) == B_OK) { 548 SymbolInfo* info = new(std::nothrow) SymbolInfo( 549 (target_addr_t)(addr_t)address, size, type, name); 550 if (info == NULL) 551 break; 552 if (!infos.AddItem(info)) { 553 delete info; 554 break; 555 } 556 } 557 558 // delete the symbol iterator and lookup context 559 debug_delete_symbol_iterator(iterator); 560 debug_delete_symbol_lookup_context(lookupContext); 561 562 return B_OK; 563} 564 565 566status_t 567DebuggerInterface::GetSymbolInfo(team_id team, image_id image, const char* name, 568 int32 symbolType, SymbolInfo& info) 569{ 570 // create a lookup context 571 // TODO: It's a bit expensive to create a lookup context just for one 572 // symbol! 573 debug_symbol_lookup_context* lookupContext; 574 status_t error = debug_create_symbol_lookup_context(team, &lookupContext); 575 if (error != B_OK) 576 return error; 577 578 // try to get the symbol 579 void* foundAddress; 580 size_t foundSize; 581 int32 foundType; 582 error = debug_get_symbol(lookupContext, image, name, symbolType, 583 &foundAddress, &foundSize, &foundType); 584 if (error == B_OK) { 585 info.SetTo((target_addr_t)(addr_t)foundAddress, foundSize, foundType, 586 name); 587 } 588 589 // delete the lookup context 590 debug_delete_symbol_lookup_context(lookupContext); 591 592 return error; 593} 594 595 596status_t 597DebuggerInterface::GetThreadInfo(thread_id thread, ThreadInfo& info) 598{ 599 thread_info threadInfo; 600 status_t error = get_thread_info(thread, &threadInfo); 601 if (error != B_OK) 602 return error; 603 604 info.SetTo(threadInfo.team, threadInfo.thread, threadInfo.name); 605 return B_OK; 606} 607 608 609status_t 610DebuggerInterface::GetCpuState(thread_id thread, CpuState*& _state) 611{ 612 DebugContextGetter contextGetter(fDebugContextPool); 613 614 debug_nub_get_cpu_state message; 615 message.reply_port = contextGetter.Context()->reply_port; 616 message.thread = thread; 617 618 debug_nub_get_cpu_state_reply reply; 619 620 status_t error = send_debug_message(contextGetter.Context(), 621 B_DEBUG_MESSAGE_GET_CPU_STATE, &message, sizeof(message), &reply, 622 sizeof(reply)); 623 if (error != B_OK) 624 return error; 625 if (reply.error != B_OK) 626 return reply.error; 627 628 return fArchitecture->CreateCpuState(&reply.cpu_state, 629 sizeof(debug_cpu_state), _state); 630} 631 632 633ssize_t 634DebuggerInterface::ReadMemory(target_addr_t address, void* buffer, size_t size) 635{ 636 DebugContextGetter contextGetter(fDebugContextPool); 637 638 return debug_read_memory(contextGetter.Context(), 639 (const void*)(addr_t)address, buffer, size); 640} 641 642 643ssize_t 644DebuggerInterface::WriteMemory(target_addr_t address, void* buffer, 645 size_t size) 646{ 647 DebugContextGetter contextGetter(fDebugContextPool); 648 649 return debug_write_memory(contextGetter.Context(), 650 (const void*)(addr_t)address, buffer, size); 651} 652 653 654status_t 655DebuggerInterface::_CreateDebugEvent(int32 messageCode, 656 const debug_debugger_message_data& message, bool& _ignore, 657 DebugEvent*& _event) 658{ 659 DebugEvent* event = NULL; 660 661 switch (messageCode) { 662 case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: 663 event = new(std::nothrow) ThreadDebuggedEvent(message.origin.team, 664 message.origin.thread); 665 break; 666 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: 667 event = new(std::nothrow) DebuggerCallEvent(message.origin.team, 668 message.origin.thread, 669 (target_addr_t)message.debugger_call.message); 670 break; 671 case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: 672 { 673 CpuState* state = NULL; 674 status_t error = fArchitecture->CreateCpuState( 675 &message.breakpoint_hit.cpu_state, 676 sizeof(debug_cpu_state), state); 677 if (error != B_OK) 678 return error; 679 680 event = new(std::nothrow) BreakpointHitEvent(message.origin.team, 681 message.origin.thread, state); 682 state->ReleaseReference(); 683 break; 684 } 685 case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: 686 { 687 CpuState* state = NULL; 688 status_t error = fArchitecture->CreateCpuState( 689 &message.watchpoint_hit.cpu_state, 690 sizeof(debug_cpu_state), state); 691 if (error != B_OK) 692 return error; 693 694 event = new(std::nothrow) WatchpointHitEvent(message.origin.team, 695 message.origin.thread, state); 696 state->ReleaseReference(); 697 break; 698 } 699 case B_DEBUGGER_MESSAGE_SINGLE_STEP: 700 { 701 CpuState* state = NULL; 702 status_t error = fArchitecture->CreateCpuState( 703 &message.single_step.cpu_state, 704 sizeof(debug_cpu_state), state); 705 if (error != B_OK) 706 return error; 707 708 event = new(std::nothrow) SingleStepEvent(message.origin.team, 709 message.origin.thread, state); 710 state->ReleaseReference(); 711 break; 712 } 713 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: 714 event = new(std::nothrow) ExceptionOccurredEvent( 715 message.origin.team, message.origin.thread, 716 message.exception_occurred.exception); 717 break; 718 case B_DEBUGGER_MESSAGE_TEAM_DELETED: 719 if (message.origin.team != fTeamID) { 720 _ignore = true; 721 return B_OK; 722 } 723 event = new(std::nothrow) TeamDeletedEvent(message.origin.team, 724 message.origin.thread); 725 break; 726 case B_DEBUGGER_MESSAGE_TEAM_EXEC: 727 if (message.origin.team != fTeamID) { 728 _ignore = true; 729 return B_OK; 730 } 731 event = new(std::nothrow) TeamExecEvent(message.origin.team, 732 message.origin.thread); 733 break; 734 case B_DEBUGGER_MESSAGE_THREAD_CREATED: 735 event = new(std::nothrow) ThreadCreatedEvent(message.origin.team, 736 message.origin.thread, message.thread_created.new_thread); 737 break; 738 case B_DEBUGGER_MESSAGE_THREAD_DELETED: 739 event = new(std::nothrow) ThreadDeletedEvent(message.origin.team, 740 message.origin.thread); 741 break; 742 case B_DEBUGGER_MESSAGE_IMAGE_CREATED: 743 { 744 const image_info& info = message.image_created.info; 745 event = new(std::nothrow) ImageCreatedEvent(message.origin.team, 746 message.origin.thread, 747 ImageInfo(fTeamID, info.id, info.name, info.type, 748 (addr_t)info.text, info.text_size, (addr_t)info.data, 749 info.data_size)); 750 break; 751 } 752 case B_DEBUGGER_MESSAGE_IMAGE_DELETED: 753 { 754 const image_info& info = message.image_deleted.info; 755 event = new(std::nothrow) ImageDeletedEvent(message.origin.team, 756 message.origin.thread, 757 ImageInfo(fTeamID, info.id, info.name, info.type, 758 (addr_t)info.text, info.text_size, (addr_t)info.data, 759 info.data_size)); 760 break; 761 } 762 default: 763 printf("DebuggerInterface for team %" B_PRId32 ": unknown message " 764 "from kernel: %" B_PRId32 "\n", fTeamID, messageCode); 765 // fall through... 766 case B_DEBUGGER_MESSAGE_TEAM_CREATED: 767 case B_DEBUGGER_MESSAGE_PRE_SYSCALL: 768 case B_DEBUGGER_MESSAGE_POST_SYSCALL: 769 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: 770 case B_DEBUGGER_MESSAGE_PROFILER_UPDATE: 771 case B_DEBUGGER_MESSAGE_HANDED_OVER: 772 _ignore = true; 773 return B_OK; 774 } 775 776 if (event == NULL) 777 return B_NO_MEMORY; 778 779 if (message.origin.thread >= 0 && message.origin.nub_port >= 0) 780 event->SetThreadStopped(true); 781 782 _ignore = false; 783 _event = event; 784 785 return B_OK; 786} 787 788 789status_t 790DebuggerInterface::_GetNextSystemWatchEvent(DebugEvent*& _event, 791 KMessage& message) 792{ 793 status_t error = B_OK; 794 if (message.What() != B_SYSTEM_OBJECT_UPDATE) 795 return B_BAD_DATA; 796 797 int32 opcode = 0; 798 if (message.FindInt32("opcode", &opcode) != B_OK) 799 return B_BAD_DATA; 800 801 DebugEvent* event = NULL; 802 switch (opcode) 803 { 804 case B_THREAD_NAME_CHANGED: 805 { 806 int32 threadID = -1; 807 if (message.FindInt32("thread", &threadID) != B_OK) 808 break; 809 810 thread_info info; 811 error = get_thread_info(threadID, &info); 812 if (error != B_OK) 813 break; 814 815 event = new(std::nothrow) ThreadRenamedEvent(fTeamID, 816 threadID, threadID, info.name); 817 break; 818 } 819 820 default: 821 { 822 error = B_BAD_DATA; 823 break; 824 } 825 } 826 827 if (event != NULL) 828 _event = event; 829 830 return error; 831} 832