1 //===-- CommandObjectSource.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 "CommandObjectSource.h" 11 12 // C Includes 13 // C++ Includes 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Interpreter/Args.h" 17 #include "lldb/Core/Debugger.h" 18 #include "lldb/Core/FileLineResolver.h" 19 #include "lldb/Core/ModuleSpec.h" 20 #include "lldb/Core/SourceManager.h" 21 #include "lldb/Interpreter/CommandInterpreter.h" 22 #include "lldb/Interpreter/CommandReturnObject.h" 23 #include "lldb/Host/FileSpec.h" 24 #include "lldb/Symbol/CompileUnit.h" 25 #include "lldb/Symbol/Function.h" 26 #include "lldb/Target/Process.h" 27 #include "lldb/Target/TargetList.h" 28 #include "lldb/Interpreter/CommandCompletions.h" 29 #include "lldb/Interpreter/Options.h" 30 31 using namespace lldb; 32 using namespace lldb_private; 33 34 //------------------------------------------------------------------------- 35 // CommandObjectSourceInfo 36 //------------------------------------------------------------------------- 37 38 class CommandObjectSourceInfo : public CommandObjectParsed 39 { 40 41 class CommandOptions : public Options 42 { 43 public: 44 CommandOptions (CommandInterpreter &interpreter) : 45 Options(interpreter) 46 { 47 } 48 49 ~CommandOptions () 50 { 51 } 52 53 Error 54 SetOptionValue (uint32_t option_idx, const char *option_arg) 55 { 56 Error error; 57 const char short_option = g_option_table[option_idx].short_option; 58 switch (short_option) 59 { 60 case 'l': 61 start_line = Args::StringToUInt32 (option_arg, 0); 62 if (start_line == 0) 63 error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); 64 break; 65 66 case 'f': 67 file_name = option_arg; 68 break; 69 70 default: 71 error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); 72 break; 73 } 74 75 return error; 76 } 77 78 void 79 OptionParsingStarting () 80 { 81 file_spec.Clear(); 82 file_name.clear(); 83 start_line = 0; 84 } 85 86 const OptionDefinition* 87 GetDefinitions () 88 { 89 return g_option_table; 90 } 91 static OptionDefinition g_option_table[]; 92 93 // Instance variables to hold the values for command options. 94 FileSpec file_spec; 95 std::string file_name; 96 uint32_t start_line; 97 98 }; 99 100 public: 101 CommandObjectSourceInfo(CommandInterpreter &interpreter) : 102 CommandObjectParsed (interpreter, 103 "source info", 104 "Display information about the source lines from the current executable's debug info.", 105 "source info [<cmd-options>]"), 106 m_options (interpreter) 107 { 108 } 109 110 ~CommandObjectSourceInfo () 111 { 112 } 113 114 115 Options * 116 GetOptions () 117 { 118 return &m_options; 119 } 120 121 protected: 122 bool 123 DoExecute (Args& command, CommandReturnObject &result) 124 { 125 result.AppendError ("Not yet implemented"); 126 result.SetStatus (eReturnStatusFailed); 127 return false; 128 } 129 130 CommandOptions m_options; 131 }; 132 133 OptionDefinition 134 CommandObjectSourceInfo::CommandOptions::g_option_table[] = 135 { 136 { LLDB_OPT_SET_1, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, 137 { LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, 138 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 139 }; 140 141 #pragma mark CommandObjectSourceList 142 //------------------------------------------------------------------------- 143 // CommandObjectSourceList 144 //------------------------------------------------------------------------- 145 146 class CommandObjectSourceList : public CommandObjectParsed 147 { 148 149 class CommandOptions : public Options 150 { 151 public: 152 CommandOptions (CommandInterpreter &interpreter) : 153 Options(interpreter) 154 { 155 } 156 157 ~CommandOptions () 158 { 159 } 160 161 Error 162 SetOptionValue (uint32_t option_idx, const char *option_arg) 163 { 164 Error error; 165 const char short_option = g_option_table[option_idx].short_option; 166 switch (short_option) 167 { 168 case 'l': 169 start_line = Args::StringToUInt32 (option_arg, 0); 170 if (start_line == 0) 171 error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); 172 break; 173 174 case 'c': 175 num_lines = Args::StringToUInt32 (option_arg, 0); 176 if (num_lines == 0) 177 error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); 178 break; 179 180 case 'f': 181 file_name = option_arg; 182 break; 183 184 case 'n': 185 symbol_name = option_arg; 186 break; 187 188 case 's': 189 modules.push_back (std::string (option_arg)); 190 break; 191 192 case 'b': 193 show_bp_locs = true; 194 break; 195 default: 196 error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); 197 break; 198 } 199 200 return error; 201 } 202 203 void 204 OptionParsingStarting () 205 { 206 file_spec.Clear(); 207 file_name.clear(); 208 symbol_name.clear(); 209 start_line = 0; 210 num_lines = 10; 211 show_bp_locs = false; 212 modules.clear(); 213 } 214 215 const OptionDefinition* 216 GetDefinitions () 217 { 218 return g_option_table; 219 } 220 static OptionDefinition g_option_table[]; 221 222 // Instance variables to hold the values for command options. 223 FileSpec file_spec; 224 std::string file_name; 225 std::string symbol_name; 226 uint32_t start_line; 227 uint32_t num_lines; 228 STLStringArray modules; 229 bool show_bp_locs; 230 }; 231 232 public: 233 CommandObjectSourceList(CommandInterpreter &interpreter) : 234 CommandObjectParsed (interpreter, 235 "source list", 236 "Display source code (as specified) based on the current executable's debug info.", 237 NULL), 238 m_options (interpreter) 239 { 240 CommandArgumentEntry arg; 241 CommandArgumentData file_arg; 242 243 // Define the first (and only) variant of this arg. 244 file_arg.arg_type = eArgTypeFilename; 245 file_arg.arg_repetition = eArgRepeatOptional; 246 247 // There is only one variant this argument could be; put it into the argument entry. 248 arg.push_back (file_arg); 249 250 // Push the data for the first argument into the m_arguments vector. 251 m_arguments.push_back (arg); 252 } 253 254 ~CommandObjectSourceList () 255 { 256 } 257 258 259 Options * 260 GetOptions () 261 { 262 return &m_options; 263 } 264 265 virtual const char * 266 GetRepeatCommand (Args ¤t_command_args, uint32_t index) 267 { 268 return m_cmd_name.c_str(); 269 } 270 271 protected: 272 bool 273 DoExecute (Args& command, CommandReturnObject &result) 274 { 275 const int argc = command.GetArgumentCount(); 276 277 if (argc != 0) 278 { 279 result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); 280 result.SetStatus (eReturnStatusFailed); 281 return false; 282 } 283 284 ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); 285 Target *target = exe_ctx.GetTargetPtr(); 286 287 if (target == NULL) 288 target = m_interpreter.GetDebugger().GetSelectedTarget().get(); 289 290 if (target == NULL) 291 { 292 result.AppendError ("invalid target, create a debug target using the 'target create' command"); 293 result.SetStatus (eReturnStatusFailed); 294 return false; 295 } 296 297 if (!m_options.symbol_name.empty()) 298 { 299 // Displaying the source for a symbol: 300 SymbolContextList sc_list; 301 ConstString name(m_options.symbol_name.c_str()); 302 bool include_symbols = false; 303 bool include_inlines = true; 304 bool append = true; 305 size_t num_matches = 0; 306 307 if (m_options.modules.size() > 0) 308 { 309 ModuleList matching_modules; 310 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++) 311 { 312 FileSpec module_file_spec(m_options.modules[i].c_str(), false); 313 if (module_file_spec) 314 { 315 ModuleSpec module_spec (module_file_spec); 316 matching_modules.Clear(); 317 target->GetImages().FindModules (module_spec, matching_modules); 318 num_matches += matching_modules.FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); 319 } 320 } 321 } 322 else 323 { 324 num_matches = target->GetImages().FindFunctions (name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); 325 } 326 327 SymbolContext sc; 328 329 if (num_matches == 0) 330 { 331 result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", m_options.symbol_name.c_str()); 332 result.SetStatus (eReturnStatusFailed); 333 return false; 334 } 335 336 sc_list.GetContextAtIndex (0, sc); 337 FileSpec start_file; 338 uint32_t start_line; 339 uint32_t end_line; 340 FileSpec end_file; 341 if (sc.function != NULL) 342 { 343 sc.function->GetStartLineSourceInfo (start_file, start_line); 344 if (start_line == 0) 345 { 346 result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str()); 347 result.SetStatus (eReturnStatusFailed); 348 return false; 349 } 350 sc.function->GetEndLineSourceInfo (end_file, end_line); 351 } 352 else 353 { 354 result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); 355 result.SetStatus (eReturnStatusFailed); 356 return false; 357 } 358 359 if (num_matches > 1) 360 { 361 // This could either be because there are multiple functions of this name, in which case 362 // we'll have to specify this further... Or it could be because there are multiple inlined instances 363 // of one function. So run through the matches and if they all have the same file & line then we can just 364 // list one. 365 366 bool found_multiple = false; 367 368 for (size_t i = 1; i < num_matches; i++) 369 { 370 SymbolContext scratch_sc; 371 sc_list.GetContextAtIndex (i, scratch_sc); 372 if (scratch_sc.function != NULL) 373 { 374 FileSpec scratch_file; 375 uint32_t scratch_line; 376 scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line); 377 if (scratch_file != start_file 378 || scratch_line != start_line) 379 { 380 found_multiple = true; 381 break; 382 } 383 } 384 } 385 if (found_multiple) 386 { 387 StreamString s; 388 for (size_t i = 0; i < num_matches; i++) 389 { 390 SymbolContext scratch_sc; 391 sc_list.GetContextAtIndex (i, scratch_sc); 392 if (scratch_sc.function != NULL) 393 { 394 s.Printf("\n%lu: ", i); 395 scratch_sc.function->Dump (&s, true); 396 } 397 } 398 result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n", 399 m_options.symbol_name.c_str(), 400 s.GetData()); 401 result.SetStatus (eReturnStatusFailed); 402 return false; 403 } 404 } 405 406 407 // This is a little hacky, but the first line table entry for a function points to the "{" that 408 // starts the function block. It would be nice to actually get the function 409 // declaration in there too. So back up a bit, but not further than what you're going to display. 410 size_t lines_to_back_up = m_options.num_lines >= 10 ? 5 : m_options.num_lines/2; 411 uint32_t line_no; 412 if (start_line <= lines_to_back_up) 413 line_no = 1; 414 else 415 line_no = start_line - lines_to_back_up; 416 417 // For fun, if the function is shorter than the number of lines we're supposed to display, 418 // only display the function... 419 if (end_line != 0) 420 { 421 if (m_options.num_lines > end_line - line_no) 422 m_options.num_lines = end_line - line_no; 423 } 424 425 char path_buf[PATH_MAX]; 426 start_file.GetPath(path_buf, sizeof(path_buf)); 427 428 if (m_options.show_bp_locs) 429 { 430 const bool show_inlines = true; 431 m_breakpoint_locations.Reset (start_file, 0, show_inlines); 432 SearchFilter target_search_filter (exe_ctx.GetTargetSP()); 433 target_search_filter.Search (m_breakpoint_locations); 434 } 435 else 436 m_breakpoint_locations.Clear(); 437 438 result.AppendMessageWithFormat("File: %s.\n", path_buf); 439 target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, 440 line_no, 441 0, 442 m_options.num_lines, 443 "", 444 &result.GetOutputStream(), 445 GetBreakpointLocations ()); 446 447 result.SetStatus (eReturnStatusSuccessFinishResult); 448 return true; 449 450 } 451 else if (m_options.file_name.empty()) 452 { 453 // Last valid source manager context, or the current frame if no 454 // valid last context in source manager. 455 // One little trick here, if you type the exact same list command twice in a row, it is 456 // more likely because you typed it once, then typed it again 457 if (m_options.start_line == 0) 458 { 459 if (target->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream(), 460 GetBreakpointLocations ())) 461 { 462 result.SetStatus (eReturnStatusSuccessFinishResult); 463 } 464 } 465 else 466 { 467 if (m_options.show_bp_locs) 468 { 469 SourceManager::FileSP last_file_sp (target->GetSourceManager().GetLastFile ()); 470 if (last_file_sp) 471 { 472 const bool show_inlines = true; 473 m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines); 474 SearchFilter target_search_filter (target->shared_from_this()); 475 target_search_filter.Search (m_breakpoint_locations); 476 } 477 } 478 else 479 m_breakpoint_locations.Clear(); 480 481 if (target->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile( 482 m_options.start_line, // Line to display 483 0, // Lines before line to display 484 m_options.num_lines, // Lines after line to display 485 "", // Don't mark "line" 486 &result.GetOutputStream(), 487 GetBreakpointLocations ())) 488 { 489 result.SetStatus (eReturnStatusSuccessFinishResult); 490 } 491 492 } 493 } 494 else 495 { 496 const char *filename = m_options.file_name.c_str(); 497 498 bool check_inlines = false; 499 SymbolContextList sc_list; 500 size_t num_matches = 0; 501 502 if (m_options.modules.size() > 0) 503 { 504 ModuleList matching_modules; 505 for (unsigned i = 0, e = m_options.modules.size(); i != e; i++) 506 { 507 FileSpec module_file_spec(m_options.modules[i].c_str(), false); 508 if (module_file_spec) 509 { 510 ModuleSpec module_spec (module_file_spec); 511 matching_modules.Clear(); 512 target->GetImages().FindModules (module_spec, matching_modules); 513 num_matches += matching_modules.ResolveSymbolContextForFilePath (filename, 514 0, 515 check_inlines, 516 eSymbolContextModule | eSymbolContextCompUnit, 517 sc_list); 518 } 519 } 520 } 521 else 522 { 523 num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename, 524 0, 525 check_inlines, 526 eSymbolContextModule | eSymbolContextCompUnit, 527 sc_list); 528 } 529 530 if (num_matches == 0) 531 { 532 result.AppendErrorWithFormat("Could not find source file \"%s\".\n", 533 m_options.file_name.c_str()); 534 result.SetStatus (eReturnStatusFailed); 535 return false; 536 } 537 538 if (num_matches > 1) 539 { 540 SymbolContext sc; 541 bool got_multiple = false; 542 FileSpec *test_cu_spec = NULL; 543 544 for (unsigned i = 0; i < num_matches; i++) 545 { 546 sc_list.GetContextAtIndex(i, sc); 547 if (sc.comp_unit) 548 { 549 if (test_cu_spec) 550 { 551 if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit)) 552 got_multiple = true; 553 break; 554 } 555 else 556 test_cu_spec = sc.comp_unit; 557 } 558 } 559 if (got_multiple) 560 { 561 result.AppendErrorWithFormat("Multiple source files found matching: \"%s.\"\n", 562 m_options.file_name.c_str()); 563 result.SetStatus (eReturnStatusFailed); 564 return false; 565 } 566 } 567 568 SymbolContext sc; 569 if (sc_list.GetContextAtIndex(0, sc)) 570 { 571 if (sc.comp_unit) 572 { 573 if (m_options.show_bp_locs) 574 { 575 const bool show_inlines = true; 576 m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); 577 SearchFilter target_search_filter (target->shared_from_this()); 578 target_search_filter.Search (m_breakpoint_locations); 579 } 580 else 581 m_breakpoint_locations.Clear(); 582 583 target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, 584 m_options.start_line, 585 0, 586 m_options.num_lines, 587 "", 588 &result.GetOutputStream(), 589 GetBreakpointLocations ()); 590 591 result.SetStatus (eReturnStatusSuccessFinishResult); 592 } 593 else 594 { 595 result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", 596 m_options.file_name.c_str()); 597 result.SetStatus (eReturnStatusFailed); 598 return false; 599 } 600 } 601 } 602 return result.Succeeded(); 603 } 604 605 const SymbolContextList * 606 GetBreakpointLocations () 607 { 608 if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) 609 return &m_breakpoint_locations.GetFileLineMatches(); 610 return NULL; 611 } 612 CommandOptions m_options; 613 FileLineResolver m_breakpoint_locations; 614 615 }; 616 617 OptionDefinition 618 CommandObjectSourceList::CommandOptions::g_option_table[] = 619 { 620 { LLDB_OPT_SET_ALL, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "The number of source lines to display."}, 621 { LLDB_OPT_SET_ALL, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."}, 622 { LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints."}, 623 { LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, 624 { LLDB_OPT_SET_1, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, 625 { LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, 626 { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } 627 }; 628 629 #pragma mark CommandObjectMultiwordSource 630 631 //------------------------------------------------------------------------- 632 // CommandObjectMultiwordSource 633 //------------------------------------------------------------------------- 634 635 CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) : 636 CommandObjectMultiword (interpreter, 637 "source", 638 "A set of commands for accessing source file information", 639 "source <subcommand> [<subcommand-options>]") 640 { 641 LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); 642 LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); 643 } 644 645 CommandObjectMultiwordSource::~CommandObjectMultiwordSource () 646 { 647 } 648 649