1 //===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/lldb-python.h" 11 12 #include "CommandObjectThread.h" 13 14 // C Includes 15 // C++ Includes 16 // Other libraries and framework includes 17 // Project includes 18 #include "lldb/lldb-private.h" 19 #include "lldb/Core/State.h" 20 #include "lldb/Core/SourceManager.h" 21 #include "lldb/Host/Host.h" 22 #include "lldb/Interpreter/CommandInterpreter.h" 23 #include "lldb/Interpreter/CommandReturnObject.h" 24 #include "lldb/Interpreter/Options.h" 25 #include "lldb/Symbol/CompileUnit.h" 26 #include "lldb/Symbol/Function.h" 27 #include "lldb/Symbol/LineTable.h" 28 #include "lldb/Symbol/LineEntry.h" 29 #include "lldb/Target/Process.h" 30 #include "lldb/Target/RegisterContext.h" 31 #include "lldb/Target/Target.h" 32 #include "lldb/Target/Thread.h" 33 #include "lldb/Target/ThreadPlan.h" 34 #include "lldb/Target/ThreadPlanStepInstruction.h" 35 #include "lldb/Target/ThreadPlanStepOut.h" 36 #include "lldb/Target/ThreadPlanStepRange.h" 37 #include "lldb/Target/ThreadPlanStepInRange.h" 38 39 40 using namespace lldb; 41 using namespace lldb_private; 42 43 44 //------------------------------------------------------------------------- 45 // CommandObjectThreadBacktrace 46 //------------------------------------------------------------------------- 47 48 class CommandObjectThreadBacktrace : public CommandObjectParsed 49 { 50 public: 51 52 class CommandOptions : public Options 53 { 54 public: 55 56 CommandOptions (CommandInterpreter &interpreter) : 57 Options(interpreter) 58 { 59 // Keep default values of all options in one place: OptionParsingStarting () 60 OptionParsingStarting (); 61 } 62 63 virtual 64 ~CommandOptions () 65 { 66 } 67 68 virtual Error 69 SetOptionValue (uint32_t option_idx, const char *option_arg) 70 { 71 Error error; 72 const int short_option = m_getopt_table[option_idx].val; 73 74 switch (short_option) 75 { 76 case 'c': 77 { 78 bool success; 79 int32_t input_count = Args::StringToSInt32 (option_arg, -1, 0, &success); 80 if (!success) 81 error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); 82 if (input_count < -1) 83 m_count = UINT32_MAX; 84 else 85 m_count = input_count; 86 } 87 break; 88 case 's': 89 { 90 bool success; 91 m_start = Args::StringToUInt32 (option_arg, 0, 0, &success); 92 if (!success) 93 error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); 94 } 95 break; 96 default: 97 error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); 98 break; 99 100 } 101 return error; 102 } 103 104 void 105 OptionParsingStarting () 106 { 107 m_count = UINT32_MAX; 108 m_start = 0; 109 } 110 111 const OptionDefinition* 112 GetDefinitions () 113 { 114 return g_option_table; 115 } 116 117 // Options table: Required for subclasses of Options. 118 119 static OptionDefinition g_option_table[]; 120 121 // Instance variables to hold the values for command options. 122 uint32_t m_count; 123 uint32_t m_start; 124 }; 125 126 CommandObjectThreadBacktrace (CommandInterpreter &interpreter) : 127 CommandObjectParsed (interpreter, 128 "thread backtrace", 129 "Show the stack for one or more threads. If no threads are specified, show the currently selected thread. Use the thread-index \"all\" to see all threads.", 130 NULL, 131 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), 132 m_options(interpreter) 133 { 134 CommandArgumentEntry arg; 135 CommandArgumentData thread_idx_arg; 136 137 // Define the first (and only) variant of this arg. 138 thread_idx_arg.arg_type = eArgTypeThreadIndex; 139 thread_idx_arg.arg_repetition = eArgRepeatStar; 140 141 // There is only one variant this argument could be; put it into the argument entry. 142 arg.push_back (thread_idx_arg); 143 144 // Push the data for the first argument into the m_arguments vector. 145 m_arguments.push_back (arg); 146 } 147 148 ~CommandObjectThreadBacktrace() 149 { 150 } 151 152 virtual Options * 153 GetOptions () 154 { 155 return &m_options; 156 } 157 158 protected: 159 virtual bool 160 DoExecute (Args& command, CommandReturnObject &result) 161 { 162 result.SetStatus (eReturnStatusSuccessFinishResult); 163 Stream &strm = result.GetOutputStream(); 164 165 // Don't show source context when doing backtraces. 166 const uint32_t num_frames_with_source = 0; 167 if (command.GetArgumentCount() == 0) 168 { 169 ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); 170 Thread *thread = exe_ctx.GetThreadPtr(); 171 if (thread) 172 { 173 // Thread::GetStatus() returns the number of frames shown. 174 if (thread->GetStatus (strm, 175 m_options.m_start, 176 m_options.m_count, 177 num_frames_with_source)) 178 { 179 result.SetStatus (eReturnStatusSuccessFinishResult); 180 } 181 } 182 else 183 { 184 result.AppendError ("invalid thread"); 185 result.SetStatus (eReturnStatusFailed); 186 } 187 } 188 else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) 189 { 190 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 191 Mutex::Locker locker (process->GetThreadList().GetMutex()); 192 uint32_t num_threads = process->GetThreadList().GetSize(); 193 for (uint32_t i = 0; i < num_threads; i++) 194 { 195 ThreadSP thread_sp = process->GetThreadList().GetThreadAtIndex(i); 196 if (!thread_sp->GetStatus (strm, 197 m_options.m_start, 198 m_options.m_count, 199 num_frames_with_source)) 200 { 201 result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", i); 202 result.SetStatus (eReturnStatusFailed); 203 return false; 204 } 205 206 if (i < num_threads - 1) 207 result.AppendMessage(""); 208 209 } 210 } 211 else 212 { 213 uint32_t num_args = command.GetArgumentCount(); 214 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 215 Mutex::Locker locker (process->GetThreadList().GetMutex()); 216 std::vector<ThreadSP> thread_sps; 217 218 for (uint32_t i = 0; i < num_args; i++) 219 { 220 bool success; 221 222 uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); 223 if (!success) 224 { 225 result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); 226 result.SetStatus (eReturnStatusFailed); 227 return false; 228 } 229 230 thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); 231 232 if (!thread_sps[i]) 233 { 234 result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); 235 result.SetStatus (eReturnStatusFailed); 236 return false; 237 } 238 239 } 240 241 for (uint32_t i = 0; i < num_args; i++) 242 { 243 if (!thread_sps[i]->GetStatus (strm, 244 m_options.m_start, 245 m_options.m_count, 246 num_frames_with_source)) 247 { 248 result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); 249 result.SetStatus (eReturnStatusFailed); 250 return false; 251 } 252 253 if (i < num_args - 1) 254 result.AppendMessage(""); 255 } 256 } 257 return result.Succeeded(); 258 } 259 260 CommandOptions m_options; 261 }; 262 263 OptionDefinition 264 CommandObjectThreadBacktrace::CommandOptions::g_option_table[] = 265 { 266 { LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "How many frames to display (-1 for all)"}, 267 { LLDB_OPT_SET_1, false, "start", 's', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace"}, 268 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 269 }; 270 271 enum StepScope 272 { 273 eStepScopeSource, 274 eStepScopeInstruction 275 }; 276 277 class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed 278 { 279 public: 280 281 class CommandOptions : public Options 282 { 283 public: 284 285 CommandOptions (CommandInterpreter &interpreter) : 286 Options (interpreter) 287 { 288 // Keep default values of all options in one place: OptionParsingStarting () 289 OptionParsingStarting (); 290 } 291 292 virtual 293 ~CommandOptions () 294 { 295 } 296 297 virtual Error 298 SetOptionValue (uint32_t option_idx, const char *option_arg) 299 { 300 Error error; 301 const int short_option = m_getopt_table[option_idx].val; 302 303 switch (short_option) 304 { 305 case 'a': 306 { 307 bool success; 308 m_avoid_no_debug = Args::StringToBoolean (option_arg, true, &success); 309 if (!success) 310 error.SetErrorStringWithFormat("invalid boolean value for option '%c'", short_option); 311 } 312 break; 313 314 case 'm': 315 { 316 OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; 317 m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, error); 318 } 319 break; 320 321 case 'r': 322 { 323 m_avoid_regexp.clear(); 324 m_avoid_regexp.assign(option_arg); 325 } 326 break; 327 328 case 't': 329 { 330 m_step_in_target.clear(); 331 m_step_in_target.assign(option_arg); 332 333 } 334 break; 335 default: 336 error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); 337 break; 338 339 } 340 return error; 341 } 342 343 void 344 OptionParsingStarting () 345 { 346 m_avoid_no_debug = true; 347 m_run_mode = eOnlyDuringStepping; 348 m_avoid_regexp.clear(); 349 m_step_in_target.clear(); 350 } 351 352 const OptionDefinition* 353 GetDefinitions () 354 { 355 return g_option_table; 356 } 357 358 // Options table: Required for subclasses of Options. 359 360 static OptionDefinition g_option_table[]; 361 362 // Instance variables to hold the values for command options. 363 bool m_avoid_no_debug; 364 RunMode m_run_mode; 365 std::string m_avoid_regexp; 366 std::string m_step_in_target; 367 }; 368 369 CommandObjectThreadStepWithTypeAndScope (CommandInterpreter &interpreter, 370 const char *name, 371 const char *help, 372 const char *syntax, 373 uint32_t flags, 374 StepType step_type, 375 StepScope step_scope) : 376 CommandObjectParsed (interpreter, name, help, syntax, flags), 377 m_step_type (step_type), 378 m_step_scope (step_scope), 379 m_options (interpreter) 380 { 381 CommandArgumentEntry arg; 382 CommandArgumentData thread_id_arg; 383 384 // Define the first (and only) variant of this arg. 385 thread_id_arg.arg_type = eArgTypeThreadID; 386 thread_id_arg.arg_repetition = eArgRepeatOptional; 387 388 // There is only one variant this argument could be; put it into the argument entry. 389 arg.push_back (thread_id_arg); 390 391 // Push the data for the first argument into the m_arguments vector. 392 m_arguments.push_back (arg); 393 } 394 395 virtual 396 ~CommandObjectThreadStepWithTypeAndScope () 397 { 398 } 399 400 virtual 401 Options * 402 GetOptions () 403 { 404 return &m_options; 405 } 406 407 protected: 408 virtual bool 409 DoExecute (Args& command, CommandReturnObject &result) 410 { 411 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 412 bool synchronous_execution = m_interpreter.GetSynchronous(); 413 414 if (process == NULL) 415 { 416 result.AppendError ("need a valid process to step"); 417 result.SetStatus (eReturnStatusFailed); 418 419 } 420 else 421 { 422 const uint32_t num_threads = process->GetThreadList().GetSize(); 423 Thread *thread = NULL; 424 425 if (command.GetArgumentCount() == 0) 426 { 427 thread = process->GetThreadList().GetSelectedThread().get(); 428 if (thread == NULL) 429 { 430 result.AppendError ("no selected thread in process"); 431 result.SetStatus (eReturnStatusFailed); 432 return false; 433 } 434 } 435 else 436 { 437 const char *thread_idx_cstr = command.GetArgumentAtIndex(0); 438 uint32_t step_thread_idx = Args::StringToUInt32 (thread_idx_cstr, LLDB_INVALID_INDEX32); 439 if (step_thread_idx == LLDB_INVALID_INDEX32) 440 { 441 result.AppendErrorWithFormat ("invalid thread index '%s'.\n", thread_idx_cstr); 442 result.SetStatus (eReturnStatusFailed); 443 return false; 444 } 445 thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get(); 446 if (thread == NULL) 447 { 448 result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", 449 step_thread_idx, num_threads); 450 result.SetStatus (eReturnStatusFailed); 451 return false; 452 } 453 } 454 455 const bool abort_other_plans = false; 456 const lldb::RunMode stop_other_threads = m_options.m_run_mode; 457 458 // This is a bit unfortunate, but not all the commands in this command object support 459 // only while stepping, so I use the bool for them. 460 bool bool_stop_other_threads; 461 if (m_options.m_run_mode == eAllThreads) 462 bool_stop_other_threads = false; 463 else if (m_options.m_run_mode == eOnlyDuringStepping) 464 { 465 if (m_step_type == eStepTypeOut) 466 bool_stop_other_threads = false; 467 else 468 bool_stop_other_threads = true; 469 } 470 else 471 bool_stop_other_threads = true; 472 473 ThreadPlan *new_plan = NULL; 474 475 if (m_step_type == eStepTypeInto) 476 { 477 StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); 478 479 if (frame->HasDebugInformation ()) 480 { 481 new_plan = thread->QueueThreadPlanForStepInRange (abort_other_plans, 482 frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, 483 frame->GetSymbolContext(eSymbolContextEverything), 484 m_options.m_step_in_target.c_str(), 485 stop_other_threads, 486 m_options.m_avoid_no_debug); 487 if (new_plan && !m_options.m_avoid_regexp.empty()) 488 { 489 ThreadPlanStepInRange *step_in_range_plan = static_cast<ThreadPlanStepInRange *> (new_plan); 490 step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str()); 491 } 492 } 493 else 494 new_plan = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); 495 496 } 497 else if (m_step_type == eStepTypeOver) 498 { 499 StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); 500 501 if (frame->HasDebugInformation()) 502 new_plan = thread->QueueThreadPlanForStepOverRange (abort_other_plans, 503 frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, 504 frame->GetSymbolContext(eSymbolContextEverything), 505 stop_other_threads); 506 else 507 new_plan = thread->QueueThreadPlanForStepSingleInstruction (true, 508 abort_other_plans, 509 bool_stop_other_threads); 510 511 } 512 else if (m_step_type == eStepTypeTrace) 513 { 514 new_plan = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); 515 } 516 else if (m_step_type == eStepTypeTraceOver) 517 { 518 new_plan = thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads); 519 } 520 else if (m_step_type == eStepTypeOut) 521 { 522 new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans, 523 NULL, 524 false, 525 bool_stop_other_threads, 526 eVoteYes, 527 eVoteNoOpinion, 528 thread->GetSelectedFrameIndex()); 529 } 530 else 531 { 532 result.AppendError ("step type is not supported"); 533 result.SetStatus (eReturnStatusFailed); 534 return false; 535 } 536 537 // If we got a new plan, then set it to be a master plan (User level Plans should be master plans 538 // so that they can be interruptible). Then resume the process. 539 540 if (new_plan != NULL) 541 { 542 new_plan->SetIsMasterPlan (true); 543 new_plan->SetOkayToDiscard (false); 544 545 process->GetThreadList().SetSelectedThreadByID (thread->GetID()); 546 process->Resume (); 547 548 549 if (synchronous_execution) 550 { 551 StateType state = process->WaitForProcessToStop (NULL); 552 553 //EventSP event_sp; 554 //StateType state = process->WaitForStateChangedEvents (NULL, event_sp); 555 //while (! StateIsStoppedState (state)) 556 // { 557 // state = process->WaitForStateChangedEvents (NULL, event_sp); 558 // } 559 process->GetThreadList().SetSelectedThreadByID (thread->GetID()); 560 result.SetDidChangeProcessState (true); 561 result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); 562 result.SetStatus (eReturnStatusSuccessFinishNoResult); 563 } 564 else 565 { 566 result.SetStatus (eReturnStatusSuccessContinuingNoResult); 567 } 568 } 569 else 570 { 571 result.AppendError ("Couldn't find thread plan to implement step type."); 572 result.SetStatus (eReturnStatusFailed); 573 } 574 } 575 return result.Succeeded(); 576 } 577 578 protected: 579 StepType m_step_type; 580 StepScope m_step_scope; 581 CommandOptions m_options; 582 }; 583 584 static OptionEnumValueElement 585 g_tri_running_mode[] = 586 { 587 { eOnlyThisThread, "this-thread", "Run only this thread"}, 588 { eAllThreads, "all-threads", "Run all threads"}, 589 { eOnlyDuringStepping, "while-stepping", "Run only this thread while stepping"}, 590 { 0, NULL, NULL } 591 }; 592 593 static OptionEnumValueElement 594 g_duo_running_mode[] = 595 { 596 { eOnlyThisThread, "this-thread", "Run only this thread"}, 597 { eAllThreads, "all-threads", "Run all threads"}, 598 { 0, NULL, NULL } 599 }; 600 601 OptionDefinition 602 CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] = 603 { 604 { LLDB_OPT_SET_1, false, "avoid-no-debug", 'a', required_argument, NULL, 0, eArgTypeBoolean, "A boolean value that sets whether step-in will step over functions with no debug information."}, 605 { LLDB_OPT_SET_1, false, "run-mode", 'm', required_argument, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread."}, 606 { LLDB_OPT_SET_1, false, "step-over-regexp",'r', required_argument, NULL, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in."}, 607 { LLDB_OPT_SET_1, false, "step-in-target", 't', required_argument, NULL, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into."}, 608 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 609 }; 610 611 612 //------------------------------------------------------------------------- 613 // CommandObjectThreadContinue 614 //------------------------------------------------------------------------- 615 616 class CommandObjectThreadContinue : public CommandObjectParsed 617 { 618 public: 619 620 CommandObjectThreadContinue (CommandInterpreter &interpreter) : 621 CommandObjectParsed (interpreter, 622 "thread continue", 623 "Continue execution of one or more threads in an active process.", 624 NULL, 625 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) 626 { 627 CommandArgumentEntry arg; 628 CommandArgumentData thread_idx_arg; 629 630 // Define the first (and only) variant of this arg. 631 thread_idx_arg.arg_type = eArgTypeThreadIndex; 632 thread_idx_arg.arg_repetition = eArgRepeatPlus; 633 634 // There is only one variant this argument could be; put it into the argument entry. 635 arg.push_back (thread_idx_arg); 636 637 // Push the data for the first argument into the m_arguments vector. 638 m_arguments.push_back (arg); 639 } 640 641 642 virtual 643 ~CommandObjectThreadContinue () 644 { 645 } 646 647 virtual bool 648 DoExecute (Args& command, CommandReturnObject &result) 649 { 650 bool synchronous_execution = m_interpreter.GetSynchronous (); 651 652 if (!m_interpreter.GetDebugger().GetSelectedTarget().get()) 653 { 654 result.AppendError ("invalid target, create a debug target using the 'target create' command"); 655 result.SetStatus (eReturnStatusFailed); 656 return false; 657 } 658 659 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 660 if (process == NULL) 661 { 662 result.AppendError ("no process exists. Cannot continue"); 663 result.SetStatus (eReturnStatusFailed); 664 return false; 665 } 666 667 StateType state = process->GetState(); 668 if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended)) 669 { 670 Mutex::Locker locker (process->GetThreadList().GetMutex()); 671 const uint32_t num_threads = process->GetThreadList().GetSize(); 672 const size_t argc = command.GetArgumentCount(); 673 if (argc > 0) 674 { 675 std::vector<Thread *> resume_threads; 676 for (uint32_t i=0; i<argc; ++i) 677 { 678 bool success; 679 const int base = 0; 680 uint32_t thread_idx = Args::StringToUInt32 (command.GetArgumentAtIndex(i), LLDB_INVALID_INDEX32, base, &success); 681 if (success) 682 { 683 Thread *thread = process->GetThreadList().FindThreadByIndexID(thread_idx).get(); 684 685 if (thread) 686 { 687 resume_threads.push_back(thread); 688 } 689 else 690 { 691 result.AppendErrorWithFormat("invalid thread index %u.\n", thread_idx); 692 result.SetStatus (eReturnStatusFailed); 693 return false; 694 } 695 } 696 else 697 { 698 result.AppendErrorWithFormat ("invalid thread index argument: \"%s\".\n", command.GetArgumentAtIndex(i)); 699 result.SetStatus (eReturnStatusFailed); 700 return false; 701 } 702 } 703 704 if (resume_threads.empty()) 705 { 706 result.AppendError ("no valid thread indexes were specified"); 707 result.SetStatus (eReturnStatusFailed); 708 return false; 709 } 710 else 711 { 712 if (resume_threads.size() == 1) 713 result.AppendMessageWithFormat ("Resuming thread: "); 714 else 715 result.AppendMessageWithFormat ("Resuming threads: "); 716 717 for (uint32_t idx=0; idx<num_threads; ++idx) 718 { 719 Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); 720 std::vector<Thread *>::iterator this_thread_pos = find(resume_threads.begin(), resume_threads.end(), thread); 721 722 if (this_thread_pos != resume_threads.end()) 723 { 724 resume_threads.erase(this_thread_pos); 725 if (resume_threads.size() > 0) 726 result.AppendMessageWithFormat ("%u, ", thread->GetIndexID()); 727 else 728 result.AppendMessageWithFormat ("%u ", thread->GetIndexID()); 729 730 thread->SetResumeState (eStateRunning); 731 } 732 else 733 { 734 thread->SetResumeState (eStateSuspended); 735 } 736 } 737 result.AppendMessageWithFormat ("in process %" PRIu64 "\n", process->GetID()); 738 } 739 } 740 else 741 { 742 Thread *current_thread = process->GetThreadList().GetSelectedThread().get(); 743 if (current_thread == NULL) 744 { 745 result.AppendError ("the process doesn't have a current thread"); 746 result.SetStatus (eReturnStatusFailed); 747 return false; 748 } 749 // Set the actions that the threads should each take when resuming 750 for (uint32_t idx=0; idx<num_threads; ++idx) 751 { 752 Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); 753 if (thread == current_thread) 754 { 755 result.AppendMessageWithFormat ("Resuming thread 0x%4.4" PRIx64 " in process %" PRIu64 "\n", thread->GetID(), process->GetID()); 756 thread->SetResumeState (eStateRunning); 757 } 758 else 759 { 760 thread->SetResumeState (eStateSuspended); 761 } 762 } 763 } 764 765 Error error (process->Resume()); 766 if (error.Success()) 767 { 768 result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); 769 if (synchronous_execution) 770 { 771 state = process->WaitForProcessToStop (NULL); 772 773 result.SetDidChangeProcessState (true); 774 result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); 775 result.SetStatus (eReturnStatusSuccessFinishNoResult); 776 } 777 else 778 { 779 result.SetStatus (eReturnStatusSuccessContinuingNoResult); 780 } 781 } 782 else 783 { 784 result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString()); 785 result.SetStatus (eReturnStatusFailed); 786 } 787 } 788 else 789 { 790 result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n", 791 StateAsCString(state)); 792 result.SetStatus (eReturnStatusFailed); 793 } 794 795 return result.Succeeded(); 796 } 797 798 }; 799 800 //------------------------------------------------------------------------- 801 // CommandObjectThreadUntil 802 //------------------------------------------------------------------------- 803 804 class CommandObjectThreadUntil : public CommandObjectParsed 805 { 806 public: 807 808 class CommandOptions : public Options 809 { 810 public: 811 uint32_t m_thread_idx; 812 uint32_t m_frame_idx; 813 814 CommandOptions (CommandInterpreter &interpreter) : 815 Options (interpreter), 816 m_thread_idx(LLDB_INVALID_THREAD_ID), 817 m_frame_idx(LLDB_INVALID_FRAME_ID) 818 { 819 // Keep default values of all options in one place: OptionParsingStarting () 820 OptionParsingStarting (); 821 } 822 823 virtual 824 ~CommandOptions () 825 { 826 } 827 828 virtual Error 829 SetOptionValue (uint32_t option_idx, const char *option_arg) 830 { 831 Error error; 832 const int short_option = m_getopt_table[option_idx].val; 833 834 switch (short_option) 835 { 836 case 't': 837 { 838 m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32); 839 if (m_thread_idx == LLDB_INVALID_INDEX32) 840 { 841 error.SetErrorStringWithFormat ("invalid thread index '%s'", option_arg); 842 } 843 } 844 break; 845 case 'f': 846 { 847 m_frame_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_FRAME_ID); 848 if (m_frame_idx == LLDB_INVALID_FRAME_ID) 849 { 850 error.SetErrorStringWithFormat ("invalid frame index '%s'", option_arg); 851 } 852 } 853 break; 854 case 'm': 855 { 856 OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; 857 lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, error); 858 859 if (error.Success()) 860 { 861 if (run_mode == eAllThreads) 862 m_stop_others = false; 863 else 864 m_stop_others = true; 865 } 866 } 867 break; 868 default: 869 error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); 870 break; 871 872 } 873 return error; 874 } 875 876 void 877 OptionParsingStarting () 878 { 879 m_thread_idx = LLDB_INVALID_THREAD_ID; 880 m_frame_idx = 0; 881 m_stop_others = false; 882 } 883 884 const OptionDefinition* 885 GetDefinitions () 886 { 887 return g_option_table; 888 } 889 890 uint32_t m_step_thread_idx; 891 bool m_stop_others; 892 893 // Options table: Required for subclasses of Options. 894 895 static OptionDefinition g_option_table[]; 896 897 // Instance variables to hold the values for command options. 898 }; 899 900 CommandObjectThreadUntil (CommandInterpreter &interpreter) : 901 CommandObjectParsed (interpreter, 902 "thread until", 903 "Run the current or specified thread until it reaches a given line number or leaves the current function.", 904 NULL, 905 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), 906 m_options (interpreter) 907 { 908 CommandArgumentEntry arg; 909 CommandArgumentData line_num_arg; 910 911 // Define the first (and only) variant of this arg. 912 line_num_arg.arg_type = eArgTypeLineNum; 913 line_num_arg.arg_repetition = eArgRepeatPlain; 914 915 // There is only one variant this argument could be; put it into the argument entry. 916 arg.push_back (line_num_arg); 917 918 // Push the data for the first argument into the m_arguments vector. 919 m_arguments.push_back (arg); 920 } 921 922 923 virtual 924 ~CommandObjectThreadUntil () 925 { 926 } 927 928 virtual 929 Options * 930 GetOptions () 931 { 932 return &m_options; 933 } 934 935 protected: 936 virtual bool 937 DoExecute (Args& command, CommandReturnObject &result) 938 { 939 bool synchronous_execution = m_interpreter.GetSynchronous (); 940 941 Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); 942 if (target == NULL) 943 { 944 result.AppendError ("invalid target, create a debug target using the 'target create' command"); 945 result.SetStatus (eReturnStatusFailed); 946 return false; 947 } 948 949 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 950 if (process == NULL) 951 { 952 result.AppendError ("need a valid process to step"); 953 result.SetStatus (eReturnStatusFailed); 954 955 } 956 else 957 { 958 Thread *thread = NULL; 959 uint32_t line_number; 960 961 if (command.GetArgumentCount() != 1) 962 { 963 result.AppendErrorWithFormat ("No line number provided:\n%s", GetSyntax()); 964 result.SetStatus (eReturnStatusFailed); 965 return false; 966 } 967 968 line_number = Args::StringToUInt32 (command.GetArgumentAtIndex(0), UINT32_MAX); 969 if (line_number == UINT32_MAX) 970 { 971 result.AppendErrorWithFormat ("invalid line number: '%s'.\n", command.GetArgumentAtIndex(0)); 972 result.SetStatus (eReturnStatusFailed); 973 return false; 974 } 975 976 if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) 977 { 978 thread = process->GetThreadList().GetSelectedThread().get(); 979 } 980 else 981 { 982 thread = process->GetThreadList().FindThreadByIndexID(m_options.m_thread_idx).get(); 983 } 984 985 if (thread == NULL) 986 { 987 const uint32_t num_threads = process->GetThreadList().GetSize(); 988 result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", 989 m_options.m_thread_idx, 990 num_threads); 991 result.SetStatus (eReturnStatusFailed); 992 return false; 993 } 994 995 const bool abort_other_plans = false; 996 997 StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); 998 if (frame == NULL) 999 { 1000 1001 result.AppendErrorWithFormat ("Frame index %u is out of range for thread %u.\n", 1002 m_options.m_frame_idx, 1003 m_options.m_thread_idx); 1004 result.SetStatus (eReturnStatusFailed); 1005 return false; 1006 } 1007 1008 ThreadPlan *new_plan = NULL; 1009 1010 if (frame->HasDebugInformation ()) 1011 { 1012 // Finally we got here... Translate the given line number to a bunch of addresses: 1013 SymbolContext sc(frame->GetSymbolContext (eSymbolContextCompUnit)); 1014 LineTable *line_table = NULL; 1015 if (sc.comp_unit) 1016 line_table = sc.comp_unit->GetLineTable(); 1017 1018 if (line_table == NULL) 1019 { 1020 result.AppendErrorWithFormat ("Failed to resolve the line table for frame %u of thread index %u.\n", 1021 m_options.m_frame_idx, m_options.m_thread_idx); 1022 result.SetStatus (eReturnStatusFailed); 1023 return false; 1024 } 1025 1026 LineEntry function_start; 1027 uint32_t index_ptr = 0, end_ptr; 1028 std::vector<addr_t> address_list; 1029 1030 // Find the beginning & end index of the 1031 AddressRange fun_addr_range = sc.function->GetAddressRange(); 1032 Address fun_start_addr = fun_addr_range.GetBaseAddress(); 1033 line_table->FindLineEntryByAddress (fun_start_addr, function_start, &index_ptr); 1034 1035 Address fun_end_addr(fun_start_addr.GetSection(), 1036 fun_start_addr.GetOffset() + fun_addr_range.GetByteSize()); 1037 line_table->FindLineEntryByAddress (fun_end_addr, function_start, &end_ptr); 1038 1039 bool all_in_function = true; 1040 1041 while (index_ptr <= end_ptr) 1042 { 1043 LineEntry line_entry; 1044 const bool exact = false; 1045 index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, exact, &line_entry); 1046 if (index_ptr == UINT32_MAX) 1047 break; 1048 1049 addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(target); 1050 if (address != LLDB_INVALID_ADDRESS) 1051 { 1052 if (fun_addr_range.ContainsLoadAddress (address, target)) 1053 address_list.push_back (address); 1054 else 1055 all_in_function = false; 1056 } 1057 index_ptr++; 1058 } 1059 1060 if (address_list.size() == 0) 1061 { 1062 if (all_in_function) 1063 result.AppendErrorWithFormat ("No line entries matching until target.\n"); 1064 else 1065 result.AppendErrorWithFormat ("Until target outside of the current function.\n"); 1066 1067 result.SetStatus (eReturnStatusFailed); 1068 return false; 1069 } 1070 1071 new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, 1072 &address_list.front(), 1073 address_list.size(), 1074 m_options.m_stop_others, 1075 m_options.m_frame_idx); 1076 // User level plans should be master plans so they can be interrupted (e.g. by hitting a breakpoint) 1077 // and other plans executed by the user (stepping around the breakpoint) and then a "continue" 1078 // will resume the original plan. 1079 new_plan->SetIsMasterPlan (true); 1080 new_plan->SetOkayToDiscard(false); 1081 } 1082 else 1083 { 1084 result.AppendErrorWithFormat ("Frame index %u of thread %u has no debug information.\n", 1085 m_options.m_frame_idx, 1086 m_options.m_thread_idx); 1087 result.SetStatus (eReturnStatusFailed); 1088 return false; 1089 1090 } 1091 1092 process->GetThreadList().SetSelectedThreadByID (m_options.m_thread_idx); 1093 Error error (process->Resume ()); 1094 if (error.Success()) 1095 { 1096 result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); 1097 if (synchronous_execution) 1098 { 1099 StateType state = process->WaitForProcessToStop (NULL); 1100 1101 result.SetDidChangeProcessState (true); 1102 result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); 1103 result.SetStatus (eReturnStatusSuccessFinishNoResult); 1104 } 1105 else 1106 { 1107 result.SetStatus (eReturnStatusSuccessContinuingNoResult); 1108 } 1109 } 1110 else 1111 { 1112 result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); 1113 result.SetStatus (eReturnStatusFailed); 1114 } 1115 1116 } 1117 return result.Succeeded(); 1118 } 1119 1120 CommandOptions m_options; 1121 1122 }; 1123 1124 OptionDefinition 1125 CommandObjectThreadUntil::CommandOptions::g_option_table[] = 1126 { 1127 { LLDB_OPT_SET_1, false, "frame", 'f', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame index for until operation - defaults to 0"}, 1128 { LLDB_OPT_SET_1, false, "thread", 't', required_argument, NULL, 0, eArgTypeThreadIndex, "Thread index for the thread for until operation"}, 1129 { LLDB_OPT_SET_1, false, "run-mode",'m', required_argument, g_duo_running_mode, 0, eArgTypeRunMode,"Determine how to run other threads while stepping this one"}, 1130 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 1131 }; 1132 1133 1134 //------------------------------------------------------------------------- 1135 // CommandObjectThreadSelect 1136 //------------------------------------------------------------------------- 1137 1138 class CommandObjectThreadSelect : public CommandObjectParsed 1139 { 1140 public: 1141 1142 CommandObjectThreadSelect (CommandInterpreter &interpreter) : 1143 CommandObjectParsed (interpreter, 1144 "thread select", 1145 "Select a thread as the currently active thread.", 1146 NULL, 1147 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) 1148 { 1149 CommandArgumentEntry arg; 1150 CommandArgumentData thread_idx_arg; 1151 1152 // Define the first (and only) variant of this arg. 1153 thread_idx_arg.arg_type = eArgTypeThreadIndex; 1154 thread_idx_arg.arg_repetition = eArgRepeatPlain; 1155 1156 // There is only one variant this argument could be; put it into the argument entry. 1157 arg.push_back (thread_idx_arg); 1158 1159 // Push the data for the first argument into the m_arguments vector. 1160 m_arguments.push_back (arg); 1161 } 1162 1163 1164 virtual 1165 ~CommandObjectThreadSelect () 1166 { 1167 } 1168 1169 protected: 1170 virtual bool 1171 DoExecute (Args& command, CommandReturnObject &result) 1172 { 1173 Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); 1174 if (process == NULL) 1175 { 1176 result.AppendError ("no process"); 1177 result.SetStatus (eReturnStatusFailed); 1178 return false; 1179 } 1180 else if (command.GetArgumentCount() != 1) 1181 { 1182 result.AppendErrorWithFormat("'%s' takes exactly one thread index argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); 1183 result.SetStatus (eReturnStatusFailed); 1184 return false; 1185 } 1186 1187 uint32_t index_id = Args::StringToUInt32(command.GetArgumentAtIndex(0), 0, 0); 1188 1189 Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); 1190 if (new_thread == NULL) 1191 { 1192 result.AppendErrorWithFormat ("invalid thread #%s.\n", command.GetArgumentAtIndex(0)); 1193 result.SetStatus (eReturnStatusFailed); 1194 return false; 1195 } 1196 1197 process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); 1198 result.SetStatus (eReturnStatusSuccessFinishNoResult); 1199 1200 return result.Succeeded(); 1201 } 1202 1203 }; 1204 1205 1206 //------------------------------------------------------------------------- 1207 // CommandObjectThreadList 1208 //------------------------------------------------------------------------- 1209 1210 class CommandObjectThreadList : public CommandObjectParsed 1211 { 1212 public: 1213 1214 1215 CommandObjectThreadList (CommandInterpreter &interpreter): 1216 CommandObjectParsed (interpreter, 1217 "thread list", 1218 "Show a summary of all current threads in a process.", 1219 "thread list", 1220 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) 1221 { 1222 } 1223 1224 ~CommandObjectThreadList() 1225 { 1226 } 1227 1228 protected: 1229 bool 1230 DoExecute (Args& command, CommandReturnObject &result) 1231 { 1232 Stream &strm = result.GetOutputStream(); 1233 result.SetStatus (eReturnStatusSuccessFinishNoResult); 1234 ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); 1235 Process *process = exe_ctx.GetProcessPtr(); 1236 if (process) 1237 { 1238 const bool only_threads_with_stop_reason = false; 1239 const uint32_t start_frame = 0; 1240 const uint32_t num_frames = 0; 1241 const uint32_t num_frames_with_source = 0; 1242 process->GetStatus(strm); 1243 process->GetThreadStatus (strm, 1244 only_threads_with_stop_reason, 1245 start_frame, 1246 num_frames, 1247 num_frames_with_source); 1248 } 1249 else 1250 { 1251 result.AppendError ("no current location or status available"); 1252 result.SetStatus (eReturnStatusFailed); 1253 } 1254 return result.Succeeded(); 1255 } 1256 }; 1257 1258 class CommandObjectThreadReturn : public CommandObjectRaw 1259 { 1260 public: 1261 CommandObjectThreadReturn (CommandInterpreter &interpreter) : 1262 CommandObjectRaw (interpreter, 1263 "thread return", 1264 "Return from the currently selected frame, short-circuiting execution of the frames below it, with an optional return value.", 1265 "thread return", 1266 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) 1267 { 1268 CommandArgumentEntry arg; 1269 CommandArgumentData expression_arg; 1270 1271 // Define the first (and only) variant of this arg. 1272 expression_arg.arg_type = eArgTypeExpression; 1273 expression_arg.arg_repetition = eArgRepeatPlain; 1274 1275 // There is only one variant this argument could be; put it into the argument entry. 1276 arg.push_back (expression_arg); 1277 1278 // Push the data for the first argument into the m_arguments vector. 1279 m_arguments.push_back (arg); 1280 1281 1282 } 1283 1284 ~CommandObjectThreadReturn() 1285 { 1286 } 1287 1288 protected: 1289 1290 bool DoExecute 1291 ( 1292 const char *command, 1293 CommandReturnObject &result 1294 ) 1295 { 1296 // If there is a command string, pass it to the expression parser: 1297 ExecutionContext exe_ctx = m_interpreter.GetExecutionContext(); 1298 if (!(exe_ctx.HasProcessScope() && exe_ctx.HasThreadScope() && exe_ctx.HasFrameScope())) 1299 { 1300 result.AppendError("Must have selected process, thread and frame for thread return."); 1301 result.SetStatus (eReturnStatusFailed); 1302 return false; 1303 } 1304 1305 ValueObjectSP return_valobj_sp; 1306 1307 StackFrameSP frame_sp = exe_ctx.GetFrameSP(); 1308 uint32_t frame_idx = frame_sp->GetFrameIndex(); 1309 1310 if (frame_sp->IsInlined()) 1311 { 1312 result.AppendError("Don't know how to return from inlined frames."); 1313 result.SetStatus (eReturnStatusFailed); 1314 return false; 1315 } 1316 1317 if (command && command[0] != '\0') 1318 { 1319 Target *target = exe_ctx.GetTargetPtr(); 1320 EvaluateExpressionOptions options; 1321 1322 options.SetUnwindOnError(true); 1323 options.SetUseDynamic(eNoDynamicValues); 1324 1325 ExecutionResults exe_results = eExecutionSetupError; 1326 exe_results = target->EvaluateExpression (command, 1327 frame_sp.get(), 1328 return_valobj_sp, 1329 options); 1330 if (exe_results != eExecutionCompleted) 1331 { 1332 if (return_valobj_sp) 1333 result.AppendErrorWithFormat("Error evaluating result expression: %s", return_valobj_sp->GetError().AsCString()); 1334 else 1335 result.AppendErrorWithFormat("Unknown error evaluating result expression."); 1336 result.SetStatus (eReturnStatusFailed); 1337 return false; 1338 1339 } 1340 } 1341 1342 Error error; 1343 ThreadSP thread_sp = exe_ctx.GetThreadSP(); 1344 const bool broadcast = true; 1345 error = thread_sp->ReturnFromFrame (frame_sp, return_valobj_sp, broadcast); 1346 if (!error.Success()) 1347 { 1348 result.AppendErrorWithFormat("Error returning from frame %d of thread %d: %s.", frame_idx, thread_sp->GetIndexID(), error.AsCString()); 1349 result.SetStatus (eReturnStatusFailed); 1350 return false; 1351 } 1352 1353 result.SetStatus (eReturnStatusSuccessFinishResult); 1354 return true; 1355 } 1356 1357 }; 1358 1359 //------------------------------------------------------------------------- 1360 // CommandObjectMultiwordThread 1361 //------------------------------------------------------------------------- 1362 1363 CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter &interpreter) : 1364 CommandObjectMultiword (interpreter, 1365 "thread", 1366 "A set of commands for operating on one or more threads within a running process.", 1367 "thread <subcommand> [<subcommand-options>]") 1368 { 1369 LoadSubCommand ("backtrace", CommandObjectSP (new CommandObjectThreadBacktrace (interpreter))); 1370 LoadSubCommand ("continue", CommandObjectSP (new CommandObjectThreadContinue (interpreter))); 1371 LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadList (interpreter))); 1372 LoadSubCommand ("return", CommandObjectSP (new CommandObjectThreadReturn (interpreter))); 1373 LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter))); 1374 LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter))); 1375 LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( 1376 interpreter, 1377 "thread step-in", 1378 "Source level single step in specified thread (current thread, if none specified).", 1379 NULL, 1380 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, 1381 eStepTypeInto, 1382 eStepScopeSource))); 1383 1384 LoadSubCommand ("step-out", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( 1385 interpreter, 1386 "thread step-out", 1387 "Finish executing the function of the currently selected frame and return to its call site in specified thread (current thread, if none specified).", 1388 NULL, 1389 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, 1390 eStepTypeOut, 1391 eStepScopeSource))); 1392 1393 LoadSubCommand ("step-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( 1394 interpreter, 1395 "thread step-over", 1396 "Source level single step in specified thread (current thread, if none specified), stepping over calls.", 1397 NULL, 1398 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, 1399 eStepTypeOver, 1400 eStepScopeSource))); 1401 1402 LoadSubCommand ("step-inst", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( 1403 interpreter, 1404 "thread step-inst", 1405 "Single step one instruction in specified thread (current thread, if none specified).", 1406 NULL, 1407 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, 1408 eStepTypeTrace, 1409 eStepScopeInstruction))); 1410 1411 LoadSubCommand ("step-inst-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( 1412 interpreter, 1413 "thread step-inst-over", 1414 "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.", 1415 NULL, 1416 eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, 1417 eStepTypeTraceOver, 1418 eStepScopeInstruction))); 1419 } 1420 1421 CommandObjectMultiwordThread::~CommandObjectMultiwordThread () 1422 { 1423 } 1424 1425 1426