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