1 //===-- SBThread.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/API/SBThread.h" 11 12 #include "lldb/API/SBSymbolContext.h" 13 #include "lldb/API/SBFileSpec.h" 14 #include "lldb/API/SBStream.h" 15 #include "lldb/Breakpoint/BreakpointLocation.h" 16 #include "lldb/Core/Debugger.h" 17 #include "lldb/Core/Stream.h" 18 #include "lldb/Core/StreamFile.h" 19 #include "lldb/Interpreter/CommandInterpreter.h" 20 #include "lldb/Target/Thread.h" 21 #include "lldb/Target/Process.h" 22 #include "lldb/Symbol/SymbolContext.h" 23 #include "lldb/Symbol/CompileUnit.h" 24 #include "lldb/Target/StopInfo.h" 25 #include "lldb/Target/Target.h" 26 #include "lldb/Target/ThreadPlan.h" 27 #include "lldb/Target/ThreadPlanStepInstruction.h" 28 #include "lldb/Target/ThreadPlanStepOut.h" 29 #include "lldb/Target/ThreadPlanStepRange.h" 30 #include "lldb/Target/ThreadPlanStepInRange.h" 31 32 33 #include "lldb/API/SBAddress.h" 34 #include "lldb/API/SBFrame.h" 35 #include "lldb/API/SBSourceManager.h" 36 #include "lldb/API/SBDebugger.h" 37 #include "lldb/API/SBProcess.h" 38 39 using namespace lldb; 40 using namespace lldb_private; 41 42 //---------------------------------------------------------------------- 43 // Constructors 44 //---------------------------------------------------------------------- 45 SBThread::SBThread () : 46 m_opaque_sp () 47 { 48 } 49 50 SBThread::SBThread (const ThreadSP& lldb_object_sp) : 51 m_opaque_sp (lldb_object_sp) 52 { 53 } 54 55 SBThread::SBThread (const SBThread &rhs) : 56 m_opaque_sp (rhs.m_opaque_sp) 57 { 58 } 59 60 //---------------------------------------------------------------------- 61 // Assignment operator 62 //---------------------------------------------------------------------- 63 64 const lldb::SBThread & 65 SBThread::operator = (const SBThread &rhs) 66 { 67 if (this != &rhs) 68 m_opaque_sp = rhs.m_opaque_sp; 69 return *this; 70 } 71 72 //---------------------------------------------------------------------- 73 // Destructor 74 //---------------------------------------------------------------------- 75 SBThread::~SBThread() 76 { 77 } 78 79 bool 80 SBThread::IsValid() const 81 { 82 return m_opaque_sp != NULL; 83 } 84 85 void 86 SBThread::Clear () 87 { 88 m_opaque_sp.reset(); 89 } 90 91 92 StopReason 93 SBThread::GetStopReason() 94 { 95 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 96 97 StopReason reason = eStopReasonInvalid; 98 if (m_opaque_sp) 99 { 100 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 101 StopInfoSP stop_info_sp = m_opaque_sp->GetStopInfo (); 102 if (stop_info_sp) 103 reason = stop_info_sp->GetStopReason(); 104 } 105 106 if (log) 107 log->Printf ("SBThread(%p)::GetStopReason () => %s", m_opaque_sp.get(), 108 Thread::StopReasonAsCString (reason)); 109 110 return reason; 111 } 112 113 size_t 114 SBThread::GetStopReasonDataCount () 115 { 116 if (m_opaque_sp) 117 { 118 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 119 StopInfoSP stop_info_sp = m_opaque_sp->GetStopInfo (); 120 if (stop_info_sp) 121 { 122 StopReason reason = stop_info_sp->GetStopReason(); 123 switch (reason) 124 { 125 case eStopReasonInvalid: 126 case eStopReasonNone: 127 case eStopReasonTrace: 128 case eStopReasonPlanComplete: 129 // There is no data for these stop reasons. 130 return 0; 131 132 case eStopReasonBreakpoint: 133 { 134 break_id_t site_id = stop_info_sp->GetValue(); 135 lldb::BreakpointSiteSP bp_site_sp (m_opaque_sp->GetProcess().GetBreakpointSiteList().FindByID (site_id)); 136 if (bp_site_sp) 137 return bp_site_sp->GetNumberOfOwners () * 2; 138 else 139 return 0; // Breakpoint must have cleared itself... 140 } 141 break; 142 143 case eStopReasonWatchpoint: 144 assert (!"implement watchpoint support in SBThread::GetStopReasonDataCount ()"); 145 return 0; // We don't have watchpoint support yet... 146 147 case eStopReasonSignal: 148 return 1; 149 150 case eStopReasonException: 151 return 1; 152 } 153 } 154 } 155 return 0; 156 } 157 158 uint64_t 159 SBThread::GetStopReasonDataAtIndex (uint32_t idx) 160 { 161 if (m_opaque_sp) 162 { 163 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 164 StopInfoSP stop_info_sp = m_opaque_sp->GetStopInfo (); 165 if (stop_info_sp) 166 { 167 StopReason reason = stop_info_sp->GetStopReason(); 168 switch (reason) 169 { 170 case eStopReasonInvalid: 171 case eStopReasonNone: 172 case eStopReasonTrace: 173 case eStopReasonPlanComplete: 174 // There is no data for these stop reasons. 175 return 0; 176 177 case eStopReasonBreakpoint: 178 { 179 break_id_t site_id = stop_info_sp->GetValue(); 180 lldb::BreakpointSiteSP bp_site_sp (m_opaque_sp->GetProcess().GetBreakpointSiteList().FindByID (site_id)); 181 if (bp_site_sp) 182 { 183 uint32_t bp_index = idx / 2; 184 BreakpointLocationSP bp_loc_sp (bp_site_sp->GetOwnerAtIndex (bp_index)); 185 if (bp_loc_sp) 186 { 187 if (bp_index & 1) 188 { 189 // Odd idx, return the breakpoint location ID 190 return bp_loc_sp->GetID(); 191 } 192 else 193 { 194 // Even idx, return the breakpoint ID 195 return bp_loc_sp->GetBreakpoint().GetID(); 196 } 197 } 198 } 199 return LLDB_INVALID_BREAK_ID; 200 } 201 break; 202 203 case eStopReasonWatchpoint: 204 assert (!"implement watchpoint support in SBThread::GetStopReasonDataCount ()"); 205 return 0; // We don't have watchpoint support yet... 206 207 case eStopReasonSignal: 208 return stop_info_sp->GetValue(); 209 210 case eStopReasonException: 211 return stop_info_sp->GetValue(); 212 } 213 } 214 } 215 return 0; 216 } 217 218 size_t 219 SBThread::GetStopDescription (char *dst, size_t dst_len) 220 { 221 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 222 223 if (m_opaque_sp) 224 { 225 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 226 StopInfoSP stop_info_sp = m_opaque_sp->GetStopInfo (); 227 if (stop_info_sp) 228 { 229 const char *stop_desc = stop_info_sp->GetDescription(); 230 if (stop_desc) 231 { 232 if (log) 233 log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => \"%s\"", 234 m_opaque_sp.get(), stop_desc); 235 if (dst) 236 return ::snprintf (dst, dst_len, "%s", stop_desc); 237 else 238 { 239 // NULL dst passed in, return the length needed to contain the description 240 return ::strlen (stop_desc) + 1; // Include the NULL byte for size 241 } 242 } 243 else 244 { 245 size_t stop_desc_len = 0; 246 switch (stop_info_sp->GetStopReason()) 247 { 248 case eStopReasonTrace: 249 case eStopReasonPlanComplete: 250 { 251 static char trace_desc[] = "step"; 252 stop_desc = trace_desc; 253 stop_desc_len = sizeof(trace_desc); // Include the NULL byte for size 254 } 255 break; 256 257 case eStopReasonBreakpoint: 258 { 259 static char bp_desc[] = "breakpoint hit"; 260 stop_desc = bp_desc; 261 stop_desc_len = sizeof(bp_desc); // Include the NULL byte for size 262 } 263 break; 264 265 case eStopReasonWatchpoint: 266 { 267 static char wp_desc[] = "watchpoint hit"; 268 stop_desc = wp_desc; 269 stop_desc_len = sizeof(wp_desc); // Include the NULL byte for size 270 } 271 break; 272 273 case eStopReasonSignal: 274 { 275 stop_desc = m_opaque_sp->GetProcess().GetUnixSignals ().GetSignalAsCString (stop_info_sp->GetValue()); 276 if (stop_desc == NULL || stop_desc[0] == '\0') 277 { 278 static char signal_desc[] = "signal"; 279 stop_desc = signal_desc; 280 stop_desc_len = sizeof(signal_desc); // Include the NULL byte for size 281 } 282 } 283 break; 284 285 case eStopReasonException: 286 { 287 char exc_desc[] = "exception"; 288 stop_desc = exc_desc; 289 stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size 290 } 291 break; 292 293 default: 294 break; 295 } 296 297 if (stop_desc && stop_desc[0]) 298 { 299 if (log) 300 log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => '%s'", 301 m_opaque_sp.get(), stop_desc); 302 303 if (dst) 304 return ::snprintf (dst, dst_len, "%s", stop_desc) + 1; // Include the NULL byte 305 306 if (stop_desc_len == 0) 307 stop_desc_len = ::strlen (stop_desc) + 1; // Include the NULL byte 308 309 return stop_desc_len; 310 } 311 } 312 } 313 } 314 if (dst) 315 *dst = 0; 316 return 0; 317 } 318 319 void 320 SBThread::SetThread (const ThreadSP& lldb_object_sp) 321 { 322 m_opaque_sp = lldb_object_sp; 323 } 324 325 326 lldb::tid_t 327 SBThread::GetThreadID () const 328 { 329 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 330 331 lldb::tid_t tid = LLDB_INVALID_THREAD_ID; 332 if (m_opaque_sp) 333 tid = m_opaque_sp->GetID(); 334 335 if (log) 336 log->Printf ("SBThread(%p)::GetThreadID () => 0x%4.4x", m_opaque_sp.get(), tid); 337 338 return tid; 339 } 340 341 uint32_t 342 SBThread::GetIndexID () const 343 { 344 if (m_opaque_sp) 345 return m_opaque_sp->GetIndexID(); 346 return LLDB_INVALID_INDEX32; 347 } 348 const char * 349 SBThread::GetName () const 350 { 351 const char *name = NULL; 352 if (m_opaque_sp) 353 { 354 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 355 name = m_opaque_sp->GetName(); 356 } 357 358 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 359 if (log) 360 log->Printf ("SBThread(%p)::GetName () => %s", m_opaque_sp.get(), name ? name : "NULL"); 361 362 return name; 363 } 364 365 const char * 366 SBThread::GetQueueName () const 367 { 368 const char *name = NULL; 369 if (m_opaque_sp) 370 { 371 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 372 name = m_opaque_sp->GetQueueName(); 373 } 374 375 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 376 if (log) 377 log->Printf ("SBThread(%p)::GetQueueName () => %s", m_opaque_sp.get(), name ? name : "NULL"); 378 379 return name; 380 } 381 382 383 void 384 SBThread::StepOver (lldb::RunMode stop_other_threads) 385 { 386 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 387 388 if (log) 389 log->Printf ("SBThread(%p)::StepOver (stop_other_threads='%s')", m_opaque_sp.get(), 390 Thread::RunModeAsCString (stop_other_threads)); 391 392 if (m_opaque_sp) 393 { 394 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 395 bool abort_other_plans = true; 396 StackFrameSP frame_sp(m_opaque_sp->GetStackFrameAtIndex (0)); 397 398 if (frame_sp) 399 { 400 if (frame_sp->HasDebugInformation ()) 401 { 402 SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); 403 m_opaque_sp->QueueThreadPlanForStepRange (abort_other_plans, 404 eStepTypeOver, 405 sc.line_entry.range, 406 sc, 407 stop_other_threads, 408 false); 409 410 } 411 else 412 { 413 m_opaque_sp->QueueThreadPlanForStepSingleInstruction (true, 414 abort_other_plans, 415 stop_other_threads); 416 } 417 } 418 419 Process &process = m_opaque_sp->GetProcess(); 420 // Why do we need to set the current thread by ID here??? 421 process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID()); 422 Error error (process.Resume()); 423 if (error.Success()) 424 { 425 // If we are doing synchronous mode, then wait for the 426 // process to stop yet again! 427 if (process.GetTarget().GetDebugger().GetAsyncExecution () == false) 428 process.WaitForProcessToStop (NULL); 429 } 430 } 431 } 432 433 void 434 SBThread::StepInto (lldb::RunMode stop_other_threads) 435 { 436 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 437 438 if (log) 439 log->Printf ("SBThread(%p)::StepInto (stop_other_threads='%s')", m_opaque_sp.get(), 440 Thread::RunModeAsCString (stop_other_threads)); 441 442 if (m_opaque_sp) 443 { 444 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 445 bool abort_other_plans = true; 446 447 StackFrameSP frame_sp(m_opaque_sp->GetStackFrameAtIndex (0)); 448 449 if (frame_sp && frame_sp->HasDebugInformation ()) 450 { 451 bool avoid_code_without_debug_info = true; 452 SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); 453 m_opaque_sp->QueueThreadPlanForStepRange (abort_other_plans, 454 eStepTypeInto, 455 sc.line_entry.range, 456 sc, 457 stop_other_threads, 458 avoid_code_without_debug_info); 459 } 460 else 461 { 462 m_opaque_sp->QueueThreadPlanForStepSingleInstruction (false, 463 abort_other_plans, 464 stop_other_threads); 465 } 466 467 Process &process = m_opaque_sp->GetProcess(); 468 // Why do we need to set the current thread by ID here??? 469 process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID()); 470 Error error (process.Resume()); 471 if (error.Success()) 472 { 473 // If we are doing synchronous mode, then wait for the 474 // process to stop yet again! 475 if (process.GetTarget().GetDebugger().GetAsyncExecution () == false) 476 process.WaitForProcessToStop (NULL); 477 } 478 } 479 } 480 481 void 482 SBThread::StepOut () 483 { 484 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 485 486 if (log) 487 log->Printf ("SBThread(%p)::StepOut ()", m_opaque_sp.get()); 488 489 if (m_opaque_sp) 490 { 491 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 492 bool abort_other_plans = true; 493 bool stop_other_threads = true; 494 495 m_opaque_sp->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, stop_other_threads, eVoteYes, eVoteNoOpinion); 496 497 Process &process = m_opaque_sp->GetProcess(); 498 process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID()); 499 Error error (process.Resume()); 500 if (error.Success()) 501 { 502 // If we are doing synchronous mode, then wait for the 503 // process to stop yet again! 504 if (process.GetTarget().GetDebugger().GetAsyncExecution () == false) 505 process.WaitForProcessToStop (NULL); 506 } 507 } 508 } 509 510 void 511 SBThread::StepInstruction (bool step_over) 512 { 513 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 514 515 if (log) 516 log->Printf ("SBThread(%p)::StepInstruction (step_over=%i)", m_opaque_sp.get(), step_over); 517 518 if (m_opaque_sp) 519 { 520 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 521 m_opaque_sp->QueueThreadPlanForStepSingleInstruction (step_over, true, true); 522 Process &process = m_opaque_sp->GetProcess(); 523 process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID()); 524 Error error (process.Resume()); 525 if (error.Success()) 526 { 527 // If we are doing synchronous mode, then wait for the 528 // process to stop yet again! 529 if (process.GetTarget().GetDebugger().GetAsyncExecution () == false) 530 process.WaitForProcessToStop (NULL); 531 } 532 } 533 } 534 535 void 536 SBThread::RunToAddress (lldb::addr_t addr) 537 { 538 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 539 540 if (log) 541 log->Printf ("SBThread(%p)::RunToAddress (addr=0x%llx)", m_opaque_sp.get(), addr); 542 543 if (m_opaque_sp) 544 { 545 bool abort_other_plans = true; 546 bool stop_other_threads = true; 547 548 Address target_addr (NULL, addr); 549 550 m_opaque_sp->QueueThreadPlanForRunToAddress (abort_other_plans, target_addr, stop_other_threads); 551 Process &process = m_opaque_sp->GetProcess(); 552 process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID()); 553 Error error (process.Resume()); 554 if (error.Success()) 555 { 556 // If we are doing synchronous mode, then wait for the 557 // process to stop yet again! 558 if (process.GetTarget().GetDebugger().GetAsyncExecution () == false) 559 process.WaitForProcessToStop (NULL); 560 } 561 } 562 563 } 564 565 SBProcess 566 SBThread::GetProcess () 567 { 568 569 SBProcess process; 570 if (m_opaque_sp) 571 { 572 // Have to go up to the target so we can get a shared pointer to our process... 573 process.SetProcess(m_opaque_sp->GetProcess().GetTarget().GetProcessSP()); 574 } 575 576 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 577 if (log) 578 { 579 SBStream sstr; 580 process.GetDescription (sstr); 581 log->Printf ("SBThread(%p)::GetProcess () => SBProcess(%p): %s", m_opaque_sp.get(), 582 process.get(), sstr.GetData()); 583 } 584 585 return process; 586 } 587 588 uint32_t 589 SBThread::GetNumFrames () 590 { 591 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 592 593 uint32_t num_frames = 0; 594 if (m_opaque_sp) 595 { 596 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 597 num_frames = m_opaque_sp->GetStackFrameCount(); 598 } 599 600 if (log) 601 log->Printf ("SBThread(%p)::GetNumFrames () => %u", m_opaque_sp.get(), num_frames); 602 603 return num_frames; 604 } 605 606 SBFrame 607 SBThread::GetFrameAtIndex (uint32_t idx) 608 { 609 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 610 611 SBFrame sb_frame; 612 if (m_opaque_sp) 613 { 614 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 615 sb_frame.SetFrame (m_opaque_sp->GetStackFrameAtIndex (idx)); 616 } 617 618 if (log) 619 { 620 SBStream sstr; 621 sb_frame.GetDescription (sstr); 622 log->Printf ("SBThread(%p)::GetFrameAtIndex (idx=%d) => SBFrame(%p): %s", 623 m_opaque_sp.get(), idx, sb_frame.get(), sstr.GetData()); 624 } 625 626 return sb_frame; 627 } 628 629 lldb::SBFrame 630 SBThread::GetSelectedFrame () 631 { 632 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 633 634 SBFrame sb_frame; 635 if (m_opaque_sp) 636 { 637 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 638 sb_frame.SetFrame (m_opaque_sp->GetSelectedFrame ()); 639 } 640 641 if (log) 642 { 643 SBStream sstr; 644 sb_frame.GetDescription (sstr); 645 log->Printf ("SBThread(%p)::GetSelectedFrame () => SBFrame(%p): %s", 646 m_opaque_sp.get(), sb_frame.get(), sstr.GetData()); 647 } 648 649 return sb_frame; 650 } 651 652 lldb::SBFrame 653 SBThread::SetSelectedFrame (uint32_t idx) 654 { 655 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); 656 657 SBFrame sb_frame; 658 if (m_opaque_sp) 659 { 660 Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex()); 661 lldb::StackFrameSP frame_sp (m_opaque_sp->GetStackFrameAtIndex (idx)); 662 if (frame_sp) 663 { 664 m_opaque_sp->SetSelectedFrame (frame_sp.get()); 665 sb_frame.SetFrame (frame_sp); 666 } 667 } 668 669 if (log) 670 { 671 SBStream sstr; 672 sb_frame.GetDescription (sstr); 673 log->Printf ("SBThread(%p)::SetSelectedFrame (idx=%u) => SBFrame(%p): %s", 674 m_opaque_sp.get(), idx, sb_frame.get(), sstr.GetData()); 675 } 676 return sb_frame; 677 } 678 679 680 bool 681 SBThread::operator == (const SBThread &rhs) const 682 { 683 return m_opaque_sp.get() == rhs.m_opaque_sp.get(); 684 } 685 686 bool 687 SBThread::operator != (const SBThread &rhs) const 688 { 689 return m_opaque_sp.get() != rhs.m_opaque_sp.get(); 690 } 691 692 lldb_private::Thread * 693 SBThread::get () 694 { 695 return m_opaque_sp.get(); 696 } 697 698 const lldb_private::Thread * 699 SBThread::operator->() const 700 { 701 return m_opaque_sp.get(); 702 } 703 704 const lldb_private::Thread & 705 SBThread::operator*() const 706 { 707 return *m_opaque_sp; 708 } 709 710 lldb_private::Thread * 711 SBThread::operator->() 712 { 713 return m_opaque_sp.get(); 714 } 715 716 lldb_private::Thread & 717 SBThread::operator*() 718 { 719 return *m_opaque_sp; 720 } 721 722 bool 723 SBThread::GetDescription (SBStream &description) const 724 { 725 if (m_opaque_sp) 726 { 727 StreamString strm; 728 description.Printf("SBThread: tid = 0x%4.4x", m_opaque_sp->GetID()); 729 } 730 else 731 description.Printf ("No value"); 732 733 return true; 734 } 735