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