2010-06-09 00:52:24 +08:00
//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
# include "CommandObjectSource.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
2010-06-23 09:19:29 +08:00
# include "lldb/Core/Debugger.h"
Added a new option to the "source list" command that allows us to see where
line tables specify breakpoints can be set in the source. When dumping the
source, the number of breakpoints that can be set on a source line are shown
as a prefix:
(lldb) source list -f test.c -l1 -c222 -b
1 #include <stdio.h>
2 #include <sys/fcntl.h>
3 #include <unistd.h>
4 int
5 sleep_loop (const int num_secs)
[2] 6 {
7 int i;
[1] 8 for (i=0; i<num_secs; ++i)
9 {
[1] 10 printf("%d of %i - sleep(1);\n", i, num_secs);
[1] 11 sleep(1);
12 }
13 return 0;
[1] 14 }
15
16 int
17 main (int argc, char const* argv[])
[1] 18 {
[1] 19 printf("Process: %i\n\n", getpid());
[1] 20 puts("Press any key to continue..."); getchar();
[1] 21 sleep_loop (20);
22 return 12;
[1] 23 }
Above we can see there are two breakpoints for line 6 and one breakpoint for
lines 8, 10, 11, 14, 18, 19, 20, 21 and 23. All other lines have no line table
entries for them. This helps visualize the data provided in the debug
information without having to manually dump all line tables. It also includes
all inline breakpoint that may result for a given file which can also be very
handy to see.
llvm-svn: 129747
2011-04-19 12:19:37 +08:00
# include "lldb/Core/FileLineResolver.h"
2012-12-07 08:19:47 +08:00
# include "lldb/Core/Module.h"
<rdar://problem/11757916>
Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes:
- Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file".
- modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly
- Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was.
- modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile()
Cleaned up header includes a bit as well.
llvm-svn: 162860
2012-08-30 05:13:06 +08:00
# include "lldb/Core/ModuleSpec.h"
Added a new option to the "source list" command that allows us to see where
line tables specify breakpoints can be set in the source. When dumping the
source, the number of breakpoints that can be set on a source line are shown
as a prefix:
(lldb) source list -f test.c -l1 -c222 -b
1 #include <stdio.h>
2 #include <sys/fcntl.h>
3 #include <unistd.h>
4 int
5 sleep_loop (const int num_secs)
[2] 6 {
7 int i;
[1] 8 for (i=0; i<num_secs; ++i)
9 {
[1] 10 printf("%d of %i - sleep(1);\n", i, num_secs);
[1] 11 sleep(1);
12 }
13 return 0;
[1] 14 }
15
16 int
17 main (int argc, char const* argv[])
[1] 18 {
[1] 19 printf("Process: %i\n\n", getpid());
[1] 20 puts("Press any key to continue..."); getchar();
[1] 21 sleep_loop (20);
22 return 12;
[1] 23 }
Above we can see there are two breakpoints for line 6 and one breakpoint for
lines 8, 10, 11, 14, 18, 19, 20, 21 and 23. All other lines have no line table
entries for them. This helps visualize the data provided in the debug
information without having to manually dump all line tables. It also includes
all inline breakpoint that may result for a given file which can also be very
handy to see.
llvm-svn: 129747
2011-04-19 12:19:37 +08:00
# include "lldb/Core/SourceManager.h"
2017-03-23 07:33:16 +08:00
# include "lldb/Host/OptionParser.h"
2016-09-07 04:57:50 +08:00
# include "lldb/Interpreter/CommandCompletions.h"
# include "lldb/Interpreter/CommandInterpreter.h"
# include "lldb/Interpreter/CommandReturnObject.h"
2018-04-10 17:03:59 +08:00
# include "lldb/Interpreter/OptionArgParser.h"
2016-09-07 04:57:50 +08:00
# include "lldb/Interpreter/Options.h"
<rdar://problem/11757916>
Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes:
- Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file".
- modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly
- Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was.
- modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile()
Cleaned up header includes a bit as well.
llvm-svn: 162860
2012-08-30 05:13:06 +08:00
# include "lldb/Symbol/CompileUnit.h"
# include "lldb/Symbol/Function.h"
2013-07-12 00:40:56 +08:00
# include "lldb/Symbol/Symbol.h"
2010-06-09 00:52:24 +08:00
# include "lldb/Target/Process.h"
2013-12-06 09:12:00 +08:00
# include "lldb/Target/SectionLoadList.h"
2016-01-06 03:51:51 +08:00
# include "lldb/Target/StackFrame.h"
2010-06-09 00:52:24 +08:00
# include "lldb/Target/TargetList.h"
2017-03-23 02:40:07 +08:00
# include "lldb/Utility/FileSpec.h"
2010-06-09 00:52:24 +08:00
using namespace lldb ;
using namespace lldb_private ;
2016-01-06 03:51:51 +08:00
# pragma mark CommandObjectSourceInfo
//----------------------------------------------------------------------
// CommandObjectSourceInfo - debug line entries dumping command
//----------------------------------------------------------------------
2010-06-09 00:52:24 +08:00
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
static OptionDefinition g_source_info_options [ ] = {
// clang-format off
{ LLDB_OPT_SET_ALL , false , " count " , ' c ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeCount , " The number of line entries to display. " } ,
{ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 , false , " shlib " , ' s ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eModuleCompletion , eArgTypeShlibName , " Look up the source in the given module or shared library (can be specified more than once). " } ,
{ LLDB_OPT_SET_1 , false , " file " , ' f ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eSourceFileCompletion , eArgTypeFilename , " The file from which to display source. " } ,
{ LLDB_OPT_SET_1 , false , " line " , ' l ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeLineNum , " The line number at which to start the displaying lines. " } ,
{ LLDB_OPT_SET_1 , false , " end-line " , ' e ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeLineNum , " The line number at which to stop displaying lines. " } ,
{ LLDB_OPT_SET_2 , false , " name " , ' n ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eSymbolCompletion , eArgTypeSymbol , " The name of a function whose source to display. " } ,
{ LLDB_OPT_SET_3 , false , " address " , ' a ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeAddressOrExpression , " Lookup the address and display the source information for the corresponding file and line. " } ,
// clang-format on
} ;
2016-09-07 04:57:50 +08:00
class CommandObjectSourceInfo : public CommandObjectParsed {
class CommandOptions : public Options {
public :
CommandOptions ( ) : Options ( ) { }
~ CommandOptions ( ) override = default ;
2017-05-12 12:51:55 +08:00
Status SetOptionValue ( uint32_t option_idx , llvm : : StringRef option_arg ,
ExecutionContext * execution_context ) override {
Status error ;
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
const int short_option = GetDefinitions ( ) [ option_idx ] . short_option ;
2016-09-07 04:57:50 +08:00
switch ( short_option ) {
case ' l ' :
2016-11-13 00:56:47 +08:00
if ( option_arg . getAsInteger ( 0 , start_line ) )
2016-09-07 04:57:50 +08:00
error . SetErrorStringWithFormat ( " invalid line number: '%s' " ,
2016-11-13 00:56:47 +08:00
option_arg . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
break ;
case ' e ' :
2016-11-13 00:56:47 +08:00
if ( option_arg . getAsInteger ( 0 , end_line ) )
2016-09-07 04:57:50 +08:00
error . SetErrorStringWithFormat ( " invalid line number: '%s' " ,
2016-11-13 00:56:47 +08:00
option_arg . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
break ;
case ' c ' :
2016-11-13 00:56:47 +08:00
if ( option_arg . getAsInteger ( 0 , num_lines ) )
2016-09-07 04:57:50 +08:00
error . SetErrorStringWithFormat ( " invalid line count: '%s' " ,
2016-11-13 00:56:47 +08:00
option_arg . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
break ;
case ' f ' :
file_name = option_arg ;
break ;
case ' n ' :
symbol_name = option_arg ;
break ;
case ' a ' : {
2018-04-10 17:03:59 +08:00
address = OptionArgParser : : ToAddress ( execution_context , option_arg ,
LLDB_INVALID_ADDRESS , & error ) ;
2016-09-07 04:57:50 +08:00
} break ;
case ' s ' :
modules . push_back ( std : : string ( option_arg ) ) ;
break ;
default :
error . SetErrorStringWithFormat ( " unrecognized short option '%c' " ,
short_option ) ;
break ;
}
return error ;
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
void OptionParsingStarting ( ExecutionContext * execution_context ) override {
file_spec . Clear ( ) ;
file_name . clear ( ) ;
symbol_name . clear ( ) ;
address = LLDB_INVALID_ADDRESS ;
start_line = 0 ;
end_line = 0 ;
num_lines = 0 ;
modules . clear ( ) ;
}
2010-07-07 11:36:20 +08:00
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
llvm : : ArrayRef < OptionDefinition > GetDefinitions ( ) override {
2016-09-23 05:06:13 +08:00
return llvm : : makeArrayRef ( g_source_info_options ) ;
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
// Instance variables to hold the values for command options.
FileSpec file_spec ;
std : : string file_name ;
std : : string symbol_name ;
lldb : : addr_t address ;
uint32_t start_line ;
uint32_t end_line ;
uint32_t num_lines ;
STLStringArray modules ;
} ;
2010-07-07 11:36:20 +08:00
2016-01-06 03:51:51 +08:00
public :
2016-09-07 04:57:50 +08:00
CommandObjectSourceInfo ( CommandInterpreter & interpreter )
: CommandObjectParsed (
interpreter , " source info " ,
" Display source line information for the current target "
" process. Defaults to instruction pointer in current stack "
" frame. " ,
nullptr , eCommandRequiresTarget ) ,
m_options ( ) { }
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
~ CommandObjectSourceInfo ( ) override = default ;
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
Options * GetOptions ( ) override { return & m_options ; }
2010-07-07 11:36:20 +08:00
2012-06-09 05:56:10 +08:00
protected :
2018-05-01 00:49:04 +08:00
// Dump the line entries in each symbol context. Return the number of entries
// found. If module_list is set, only dump lines contained in one of the
// modules. If file_spec is set, only dump lines in the file. If the
// start_line option was specified, don't print lines less than start_line.
2016-09-07 04:57:50 +08:00
// If the end_line option was specified, don't print lines greater than
2018-05-01 00:49:04 +08:00
// end_line. If the num_lines option was specified, dont print more than
// num_lines entries.
2016-09-07 04:57:50 +08:00
uint32_t DumpLinesInSymbolContexts ( Stream & strm ,
const SymbolContextList & sc_list ,
const ModuleList & module_list ,
const FileSpec & file_spec ) {
uint32_t start_line = m_options . start_line ;
uint32_t end_line = m_options . end_line ;
uint32_t num_lines = m_options . num_lines ;
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
uint32_t num_matches = 0 ;
bool has_path = false ;
if ( file_spec ) {
assert ( file_spec . GetFilename ( ) . AsCString ( ) ) ;
has_path = ( file_spec . GetDirectory ( ) . AsCString ( ) ! = nullptr ) ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
// Dump all the line entries for the file in the list.
ConstString last_module_file_name ;
uint32_t num_scs = sc_list . GetSize ( ) ;
for ( uint32_t i = 0 ; i < num_scs ; + + i ) {
SymbolContext sc ;
sc_list . GetContextAtIndex ( i , sc ) ;
if ( sc . comp_unit ) {
Module * module = sc . module_sp . get ( ) ;
CompileUnit * cu = sc . comp_unit ;
const LineEntry & line_entry = sc . line_entry ;
assert ( module & & cu ) ;
// Are we looking for specific modules, files or lines?
if ( module_list . GetSize ( ) & &
module_list . GetIndexForModule ( module ) = = LLDB_INVALID_INDEX32 )
continue ;
if ( file_spec & &
! lldb_private : : FileSpec : : Equal ( file_spec , line_entry . file ,
has_path ) )
continue ;
if ( start_line > 0 & & line_entry . line < start_line )
continue ;
if ( end_line > 0 & & line_entry . line > end_line )
continue ;
if ( num_lines > 0 & & num_matches > num_lines )
continue ;
// Print a new header if the module changed.
const ConstString & module_file_name =
module - > GetFileSpec ( ) . GetFilename ( ) ;
assert ( module_file_name ) ;
if ( module_file_name ! = last_module_file_name ) {
if ( num_matches > 0 )
strm < < " \n \n " ;
strm < < " Lines found in module ` " < < module_file_name < < " \n " ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
// Dump the line entry.
line_entry . GetDescription ( & strm , lldb : : eDescriptionLevelBrief , cu ,
target , /*show_address_only=*/ false ) ;
strm < < " \n " ;
last_module_file_name = module_file_name ;
num_matches + + ;
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
return num_matches ;
}
// Dump the requested line entries for the file in the compilation unit.
2018-05-01 00:49:04 +08:00
// Return the number of entries found. If module_list is set, only dump lines
// contained in one of the modules. If the start_line option was specified,
// don't print lines less than start_line. If the end_line option was
// specified, don't print lines greater than end_line. If the num_lines
// option was specified, dont print more than num_lines entries.
2016-09-07 04:57:50 +08:00
uint32_t DumpFileLinesInCompUnit ( Stream & strm , Module * module ,
CompileUnit * cu , const FileSpec & file_spec ) {
uint32_t start_line = m_options . start_line ;
uint32_t end_line = m_options . end_line ;
uint32_t num_lines = m_options . num_lines ;
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
uint32_t num_matches = 0 ;
assert ( module ) ;
if ( cu ) {
assert ( file_spec . GetFilename ( ) . AsCString ( ) ) ;
bool has_path = ( file_spec . GetDirectory ( ) . AsCString ( ) ! = nullptr ) ;
const FileSpecList & cu_file_list = cu - > GetSupportFiles ( ) ;
size_t file_idx = cu_file_list . FindFileIndex ( 0 , file_spec , has_path ) ;
if ( file_idx ! = UINT32_MAX ) {
// Update the file to how it appears in the CU.
const FileSpec & cu_file_spec =
cu_file_list . GetFileSpecAtIndex ( file_idx ) ;
// Dump all matching lines at or above start_line for the file in the
// CU.
const ConstString & file_spec_name = file_spec . GetFilename ( ) ;
const ConstString & module_file_name =
module - > GetFileSpec ( ) . GetFilename ( ) ;
bool cu_header_printed = false ;
uint32_t line = start_line ;
while ( true ) {
LineEntry line_entry ;
2018-05-01 00:49:04 +08:00
// Find the lowest index of a line entry with a line equal to or
// higher than 'line'.
2016-09-07 04:57:50 +08:00
uint32_t start_idx = 0 ;
start_idx = cu - > FindLineEntry ( start_idx , line , & cu_file_spec ,
/*exact=*/ false , & line_entry ) ;
if ( start_idx = = UINT32_MAX )
// No more line entries for our file in this CU.
break ;
if ( end_line > 0 & & line_entry . line > end_line )
break ;
2018-05-01 00:49:04 +08:00
// Loop through to find any other entries for this line, dumping
// each.
2016-09-07 04:57:50 +08:00
line = line_entry . line ;
do {
num_matches + + ;
if ( num_lines > 0 & & num_matches > num_lines )
break ;
assert ( lldb_private : : FileSpec : : Equal ( cu_file_spec , line_entry . file ,
has_path ) ) ;
if ( ! cu_header_printed ) {
if ( num_matches > 0 )
strm < < " \n \n " ;
strm < < " Lines found for file " < < file_spec_name
< < " in compilation unit " < < cu - > GetFilename ( ) < < " in ` "
< < module_file_name < < " \n " ;
cu_header_printed = true ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
line_entry . GetDescription ( & strm , lldb : : eDescriptionLevelBrief , cu ,
target , /*show_address_only=*/ false ) ;
strm < < " \n " ;
// Anymore after this one?
start_idx + + ;
start_idx = cu - > FindLineEntry ( start_idx , line , & cu_file_spec ,
/*exact=*/ true , & line_entry ) ;
} while ( start_idx ! = UINT32_MAX ) ;
// Try the next higher line, starting over at start_idx 0.
line + + ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
return num_matches ;
}
2018-05-01 00:49:04 +08:00
// Dump the requested line entries for the file in the module. Return the
// number of entries found. If module_list is set, only dump lines contained
// in one of the modules. If the start_line option was specified, don't print
// lines less than start_line. If the end_line option was specified, don't
// print lines greater than end_line. If the num_lines option was specified,
// dont print more than num_lines entries.
2016-09-07 04:57:50 +08:00
uint32_t DumpFileLinesInModule ( Stream & strm , Module * module ,
const FileSpec & file_spec ) {
uint32_t num_matches = 0 ;
if ( module ) {
// Look through all the compilation units (CUs) in this module for ones
2018-05-01 00:49:04 +08:00
// that contain lines of code from this source file.
2016-09-07 04:57:50 +08:00
for ( size_t i = 0 ; i < module - > GetNumCompileUnits ( ) ; i + + ) {
// Look for a matching source file in this CU.
CompUnitSP cu_sp ( module - > GetCompileUnitAtIndex ( i ) ) ;
if ( cu_sp ) {
num_matches + =
DumpFileLinesInCompUnit ( strm , module , cu_sp . get ( ) , file_spec ) ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
return num_matches ;
}
// Given an address and a list of modules, append the symbol contexts of all
2018-05-01 00:49:04 +08:00
// line entries containing the address found in the modules and return the
// count of matches. If none is found, return an error in 'error_strm'.
2016-09-07 04:57:50 +08:00
size_t GetSymbolContextsForAddress ( const ModuleList & module_list ,
lldb : : addr_t addr ,
SymbolContextList & sc_list ,
StreamString & error_strm ) {
Address so_addr ;
size_t num_matches = 0 ;
assert ( module_list . GetSize ( ) > 0 ) ;
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
if ( target - > GetSectionLoadList ( ) . IsEmpty ( ) ) {
2018-05-01 00:49:04 +08:00
// The target isn't loaded yet, we need to lookup the file address in all
// modules. Note: the module list option does not apply to addresses.
2016-09-07 04:57:50 +08:00
const size_t num_modules = module_list . GetSize ( ) ;
for ( size_t i = 0 ; i < num_modules ; + + i ) {
ModuleSP module_sp ( module_list . GetModuleAtIndex ( i ) ) ;
if ( ! module_sp )
continue ;
if ( module_sp - > ResolveFileAddress ( addr , so_addr ) ) {
SymbolContext sc ;
sc . Clear ( true ) ;
if ( module_sp - > ResolveSymbolContextForAddress (
so_addr , eSymbolContextEverything , sc ) &
eSymbolContextLineEntry ) {
sc_list . AppendIfUnique ( sc , /*merge_symbol_into_function=*/ false ) ;
+ + num_matches ;
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
if ( num_matches = = 0 )
error_strm . Printf ( " Source information for file address 0x% " PRIx64
" not found in any modules. \n " ,
addr ) ;
} else {
2018-05-01 00:49:04 +08:00
// The target has some things loaded, resolve this address to a compile
// unit + file + line and display
2016-09-07 04:57:50 +08:00
if ( target - > GetSectionLoadList ( ) . ResolveLoadAddress ( addr , so_addr ) ) {
ModuleSP module_sp ( so_addr . GetModule ( ) ) ;
// Check to make sure this module is in our list.
if ( module_sp & &
module_list . GetIndexForModule ( module_sp . get ( ) ) ! =
LLDB_INVALID_INDEX32 ) {
SymbolContext sc ;
sc . Clear ( true ) ;
if ( module_sp - > ResolveSymbolContextForAddress (
so_addr , eSymbolContextEverything , sc ) &
eSymbolContextLineEntry ) {
sc_list . AppendIfUnique ( sc , /*merge_symbol_into_function=*/ false ) ;
+ + num_matches ;
} else {
StreamString addr_strm ;
so_addr . Dump ( & addr_strm , nullptr ,
Address : : DumpStyleModuleWithFileAddress ) ;
error_strm . Printf (
" Address 0x% " PRIx64 " resolves to %s, but there is "
" no source information available for this address. \n " ,
addr , addr_strm . GetData ( ) ) ;
}
} else {
StreamString addr_strm ;
so_addr . Dump ( & addr_strm , nullptr ,
Address : : DumpStyleModuleWithFileAddress ) ;
error_strm . Printf ( " Address 0x% " PRIx64
" resolves to %s, but it cannot "
" be found in any modules. \n " ,
addr , addr_strm . GetData ( ) ) ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
} else
error_strm . Printf ( " Unable to resolve address 0x% " PRIx64 " . \n " , addr ) ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
return num_matches ;
}
2018-05-01 00:49:04 +08:00
// Dump the line entries found in functions matching the name specified in
// the option.
2016-09-07 04:57:50 +08:00
bool DumpLinesInFunctions ( CommandReturnObject & result ) {
SymbolContextList sc_list_funcs ;
ConstString name ( m_options . symbol_name . c_str ( ) ) ;
SymbolContextList sc_list_lines ;
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
uint32_t addr_byte_size = target - > GetArchitecture ( ) . GetAddressByteSize ( ) ;
// Note: module_list can't be const& because FindFunctionSymbols isn't
// const.
ModuleList module_list =
( m_module_list . GetSize ( ) > 0 ) ? m_module_list : target - > GetImages ( ) ;
size_t num_matches =
module_list . FindFunctions ( name , eFunctionNameTypeAuto ,
/*include_symbols=*/ false ,
/*include_inlines=*/ true ,
/*append=*/ true , sc_list_funcs ) ;
if ( ! num_matches ) {
// If we didn't find any functions with that name, try searching for
// symbols that line up exactly with function addresses.
SymbolContextList sc_list_symbols ;
size_t num_symbol_matches = module_list . FindFunctionSymbols (
name , eFunctionNameTypeAuto , sc_list_symbols ) ;
for ( size_t i = 0 ; i < num_symbol_matches ; i + + ) {
SymbolContext sc ;
sc_list_symbols . GetContextAtIndex ( i , sc ) ;
if ( sc . symbol & & sc . symbol - > ValueIsAddress ( ) ) {
const Address & base_address = sc . symbol - > GetAddressRef ( ) ;
Function * function = base_address . CalculateSymbolContextFunction ( ) ;
if ( function ) {
sc_list_funcs . Append ( SymbolContext ( function ) ) ;
num_matches + + ;
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
}
if ( num_matches = = 0 ) {
result . AppendErrorWithFormat ( " Could not find function named \' %s \' . \n " ,
m_options . symbol_name . c_str ( ) ) ;
return false ;
}
for ( size_t i = 0 ; i < num_matches ; i + + ) {
SymbolContext sc ;
sc_list_funcs . GetContextAtIndex ( i , sc ) ;
bool context_found_for_symbol = false ;
// Loop through all the ranges in the function.
AddressRange range ;
for ( uint32_t r = 0 ;
sc . GetAddressRange ( eSymbolContextEverything , r ,
/*use_inline_block_range=*/ true , range ) ;
+ + r ) {
// Append the symbol contexts for each address in the range to
// sc_list_lines.
const Address & base_address = range . GetBaseAddress ( ) ;
const addr_t size = range . GetByteSize ( ) ;
lldb : : addr_t start_addr = base_address . GetLoadAddress ( target ) ;
if ( start_addr = = LLDB_INVALID_ADDRESS )
start_addr = base_address . GetFileAddress ( ) ;
lldb : : addr_t end_addr = start_addr + size ;
for ( lldb : : addr_t addr = start_addr ; addr < end_addr ;
addr + = addr_byte_size ) {
StreamString error_strm ;
if ( ! GetSymbolContextsForAddress ( module_list , addr , sc_list_lines ,
error_strm ) )
result . AppendWarningWithFormat ( " in symbol '%s': %s " ,
sc . GetFunctionName ( ) . AsCString ( ) ,
error_strm . GetData ( ) ) ;
else
context_found_for_symbol = true ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
if ( ! context_found_for_symbol )
result . AppendWarningWithFormat ( " Unable to find line information "
" for matching symbol '%s'. \n " ,
sc . GetFunctionName ( ) . AsCString ( ) ) ;
}
if ( sc_list_lines . GetSize ( ) = = 0 ) {
result . AppendErrorWithFormat ( " No line information could be found "
" for any symbols matching '%s'. \n " ,
name . AsCString ( ) ) ;
return false ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
FileSpec file_spec ;
if ( ! DumpLinesInSymbolContexts ( result . GetOutputStream ( ) , sc_list_lines ,
module_list , file_spec ) ) {
result . AppendErrorWithFormat (
" Unable to dump line information for symbol '%s'. \n " ,
name . AsCString ( ) ) ;
return false ;
}
return true ;
}
// Dump the line entries found for the address specified in the option.
bool DumpLinesForAddress ( CommandReturnObject & result ) {
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
SymbolContextList sc_list ;
StreamString error_strm ;
if ( ! GetSymbolContextsForAddress ( target - > GetImages ( ) , m_options . address ,
sc_list , error_strm ) ) {
result . AppendErrorWithFormat ( " %s. \n " , error_strm . GetData ( ) ) ;
return false ;
}
ModuleList module_list ;
FileSpec file_spec ;
if ( ! DumpLinesInSymbolContexts ( result . GetOutputStream ( ) , sc_list ,
module_list , file_spec ) ) {
result . AppendErrorWithFormat ( " No modules contain load address 0x% " PRIx64
" . \n " ,
m_options . address ) ;
return false ;
}
return true ;
}
// Dump the line entries found in the file specified in the option.
bool DumpLinesForFile ( CommandReturnObject & result ) {
FileSpec file_spec ( m_options . file_name , false ) ;
const char * filename = m_options . file_name . c_str ( ) ;
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
const ModuleList & module_list =
( m_module_list . GetSize ( ) > 0 ) ? m_module_list : target - > GetImages ( ) ;
bool displayed_something = false ;
const size_t num_modules = module_list . GetSize ( ) ;
for ( uint32_t i = 0 ; i < num_modules ; + + i ) {
// Dump lines for this module.
Module * module = module_list . GetModulePointerAtIndex ( i ) ;
assert ( module ) ;
if ( DumpFileLinesInModule ( result . GetOutputStream ( ) , module , file_spec ) )
displayed_something = true ;
}
if ( ! displayed_something ) {
result . AppendErrorWithFormat ( " No source filenames matched '%s'. \n " ,
filename ) ;
return false ;
}
return true ;
}
// Dump the line entries for the current frame.
bool DumpLinesForFrame ( CommandReturnObject & result ) {
StackFrame * cur_frame = m_exe_ctx . GetFramePtr ( ) ;
if ( cur_frame = = nullptr ) {
result . AppendError (
" No selected frame to use to find the default source. " ) ;
return false ;
} else if ( ! cur_frame - > HasDebugInformation ( ) ) {
result . AppendError ( " No debug info for the selected frame. " ) ;
return false ;
} else {
const SymbolContext & sc =
cur_frame - > GetSymbolContext ( eSymbolContextLineEntry ) ;
SymbolContextList sc_list ;
sc_list . Append ( sc ) ;
ModuleList module_list ;
FileSpec file_spec ;
if ( ! DumpLinesInSymbolContexts ( result . GetOutputStream ( ) , sc_list ,
module_list , file_spec ) ) {
result . AppendError (
" No source line info available for the selected frame. " ) ;
return false ;
}
}
return true ;
}
2016-01-06 03:51:51 +08:00
2016-09-07 04:57:50 +08:00
bool DoExecute ( Args & command , CommandReturnObject & result ) override {
const size_t argc = command . GetArgumentCount ( ) ;
2016-01-06 03:51:51 +08:00
2016-09-07 04:57:50 +08:00
if ( argc ! = 0 ) {
result . AppendErrorWithFormat ( " '%s' takes no arguments, only flags. \n " ,
2016-10-06 05:14:38 +08:00
GetCommandName ( ) . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
if ( target = = nullptr ) {
target = m_interpreter . GetDebugger ( ) . GetSelectedTarget ( ) . get ( ) ;
if ( target = = nullptr ) {
result . AppendError ( " invalid target, create a debug target using the "
" 'target create' command. " ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
uint32_t addr_byte_size = target - > GetArchitecture ( ) . GetAddressByteSize ( ) ;
result . GetOutputStream ( ) . SetAddressByteSize ( addr_byte_size ) ;
result . GetErrorStream ( ) . SetAddressByteSize ( addr_byte_size ) ;
// Collect the list of modules to search.
m_module_list . Clear ( ) ;
if ( ! m_options . modules . empty ( ) ) {
for ( size_t i = 0 , e = m_options . modules . size ( ) ; i < e ; + + i ) {
2016-11-03 04:34:10 +08:00
FileSpec module_file_spec ( m_options . modules [ i ] , false ) ;
2016-09-07 04:57:50 +08:00
if ( module_file_spec ) {
ModuleSpec module_spec ( module_file_spec ) ;
if ( target - > GetImages ( ) . FindModules ( module_spec , m_module_list ) = = 0 )
result . AppendWarningWithFormat ( " No module found for '%s'. \n " ,
m_options . modules [ i ] . c_str ( ) ) ;
2016-01-06 03:51:51 +08:00
}
2016-09-07 04:57:50 +08:00
}
if ( ! m_module_list . GetSize ( ) ) {
result . AppendError ( " No modules match the input. " ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
} else if ( target - > GetImages ( ) . GetSize ( ) = = 0 ) {
result . AppendError ( " The target has no associated executable images. " ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
2016-01-06 03:51:51 +08:00
2016-09-07 04:57:50 +08:00
// Check the arguments to see what lines we should dump.
if ( ! m_options . symbol_name . empty ( ) ) {
// Print lines for symbol.
if ( DumpLinesInFunctions ( result ) )
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
else
result . SetStatus ( eReturnStatusFailed ) ;
} else if ( m_options . address ! = LLDB_INVALID_ADDRESS ) {
// Print lines for an address.
if ( DumpLinesForAddress ( result ) )
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
else
result . SetStatus ( eReturnStatusFailed ) ;
} else if ( ! m_options . file_name . empty ( ) ) {
// Dump lines for a file.
if ( DumpLinesForFile ( result ) )
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
else
result . SetStatus ( eReturnStatusFailed ) ;
} else {
// Dump the line for the current frame.
if ( DumpLinesForFrame ( result ) )
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
else
result . SetStatus ( eReturnStatusFailed ) ;
2010-07-07 11:36:20 +08:00
}
2016-09-07 04:57:50 +08:00
return result . Succeeded ( ) ;
}
2012-06-09 05:56:10 +08:00
2016-09-07 04:57:50 +08:00
CommandOptions m_options ;
ModuleList m_module_list ;
2010-07-07 11:36:20 +08:00
} ;
# pragma mark CommandObjectSourceList
//-------------------------------------------------------------------------
// CommandObjectSourceList
//-------------------------------------------------------------------------
2010-06-09 00:52:24 +08:00
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
static OptionDefinition g_source_list_options [ ] = {
// clang-format off
{ LLDB_OPT_SET_ALL , false , " count " , ' c ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeCount , " The number of source lines to display. " } ,
{ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 , false , " shlib " , ' s ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eModuleCompletion , eArgTypeShlibName , " Look up the source file in the given shared library. " } ,
{ LLDB_OPT_SET_ALL , false , " show-breakpoints " , ' b ' , OptionParser : : eNoArgument , nullptr , nullptr , 0 , eArgTypeNone , " Show the line table locations from the debug information that indicate valid places to set source level breakpoints. " } ,
{ LLDB_OPT_SET_1 , false , " file " , ' f ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eSourceFileCompletion , eArgTypeFilename , " The file from which to display source. " } ,
{ LLDB_OPT_SET_1 , false , " line " , ' l ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeLineNum , " The line number at which to start the display source. " } ,
{ LLDB_OPT_SET_2 , false , " name " , ' n ' , OptionParser : : eRequiredArgument , nullptr , nullptr , CommandCompletions : : eSymbolCompletion , eArgTypeSymbol , " The name of a function whose source to display. " } ,
{ LLDB_OPT_SET_3 , false , " address " , ' a ' , OptionParser : : eRequiredArgument , nullptr , nullptr , 0 , eArgTypeAddressOrExpression , " Lookup the address and display the source information for the corresponding file and line. " } ,
{ LLDB_OPT_SET_4 , false , " reverse " , ' r ' , OptionParser : : eNoArgument , nullptr , nullptr , 0 , eArgTypeNone , " Reverse the listing to look backwards from the last displayed block of source. " } ,
// clang-format on
} ;
2016-09-07 04:57:50 +08:00
class CommandObjectSourceList : public CommandObjectParsed {
class CommandOptions : public Options {
public :
CommandOptions ( ) : Options ( ) { }
~ CommandOptions ( ) override = default ;
2017-05-12 12:51:55 +08:00
Status SetOptionValue ( uint32_t option_idx , llvm : : StringRef option_arg ,
ExecutionContext * execution_context ) override {
Status error ;
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
const int short_option = GetDefinitions ( ) [ option_idx ] . short_option ;
2016-09-07 04:57:50 +08:00
switch ( short_option ) {
case ' l ' :
2016-11-13 00:56:47 +08:00
if ( option_arg . getAsInteger ( 0 , start_line ) )
2016-09-07 04:57:50 +08:00
error . SetErrorStringWithFormat ( " invalid line number: '%s' " ,
2016-11-13 00:56:47 +08:00
option_arg . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
break ;
case ' c ' :
2016-11-13 00:56:47 +08:00
if ( option_arg . getAsInteger ( 0 , num_lines ) )
2016-09-07 04:57:50 +08:00
error . SetErrorStringWithFormat ( " invalid line count: '%s' " ,
2016-11-13 00:56:47 +08:00
option_arg . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
break ;
case ' f ' :
file_name = option_arg ;
break ;
case ' n ' :
symbol_name = option_arg ;
break ;
case ' a ' : {
2018-04-10 17:03:59 +08:00
address = OptionArgParser : : ToAddress ( execution_context , option_arg ,
LLDB_INVALID_ADDRESS , & error ) ;
2016-09-07 04:57:50 +08:00
} break ;
case ' s ' :
modules . push_back ( std : : string ( option_arg ) ) ;
break ;
case ' b ' :
show_bp_locs = true ;
break ;
case ' r ' :
reverse = true ;
break ;
default :
error . SetErrorStringWithFormat ( " unrecognized short option '%c' " ,
short_option ) ;
break ;
}
return error ;
}
2010-06-09 00:52:24 +08:00
2016-09-07 04:57:50 +08:00
void OptionParsingStarting ( ExecutionContext * execution_context ) override {
file_spec . Clear ( ) ;
file_name . clear ( ) ;
symbol_name . clear ( ) ;
address = LLDB_INVALID_ADDRESS ;
start_line = 0 ;
num_lines = 0 ;
show_bp_locs = false ;
reverse = false ;
modules . clear ( ) ;
}
2010-06-09 00:52:24 +08:00
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
llvm : : ArrayRef < OptionDefinition > GetDefinitions ( ) override {
2016-09-23 05:06:13 +08:00
return llvm : : makeArrayRef ( g_source_list_options ) ;
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
// Instance variables to hold the values for command options.
FileSpec file_spec ;
std : : string file_name ;
std : : string symbol_name ;
lldb : : addr_t address ;
uint32_t start_line ;
uint32_t num_lines ;
STLStringArray modules ;
bool show_bp_locs ;
bool reverse ;
} ;
2016-02-24 10:05:55 +08:00
2016-09-07 04:57:50 +08:00
public :
CommandObjectSourceList ( CommandInterpreter & interpreter )
: CommandObjectParsed ( interpreter , " source list " ,
" Display source code for the current target "
" process as specified by options. " ,
nullptr , eCommandRequiresTarget ) ,
m_options ( ) { }
~ CommandObjectSourceList ( ) override = default ;
Options * GetOptions ( ) override { return & m_options ; }
const char * GetRepeatCommand ( Args & current_command_args ,
uint32_t index ) override {
2018-05-01 00:49:04 +08:00
// This is kind of gross, but the command hasn't been parsed yet so we
// can't look at the option values for this invocation... I have to scan
// the arguments directly.
2016-12-09 13:46:41 +08:00
auto iter =
llvm : : find_if ( current_command_args , [ ] ( const Args : : ArgEntry & e ) {
return e . ref = = " -r " | | e . ref = = " --reverse " ;
} ) ;
if ( iter = = current_command_args . end ( ) )
2016-09-07 04:57:50 +08:00
return m_cmd_name . c_str ( ) ;
2016-12-09 13:46:41 +08:00
if ( m_reverse_name . empty ( ) ) {
m_reverse_name = m_cmd_name ;
m_reverse_name . append ( " -r " ) ;
}
return m_reverse_name . c_str ( ) ;
2016-09-07 04:57:50 +08:00
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
protected :
struct SourceInfo {
ConstString function ;
LineEntry line_entry ;
2016-07-15 06:03:10 +08:00
2016-09-07 04:57:50 +08:00
SourceInfo ( const ConstString & name , const LineEntry & line_entry )
: function ( name ) , line_entry ( line_entry ) { }
SourceInfo ( ) : function ( ) , line_entry ( ) { }
bool IsValid ( ) const { return ( bool ) function & & line_entry . IsValid ( ) ; }
bool operator = = ( const SourceInfo & rhs ) const {
return function = = rhs . function & &
line_entry . original_file = = rhs . line_entry . original_file & &
line_entry . line = = rhs . line_entry . line ;
2010-07-07 11:36:20 +08:00
}
2016-09-07 04:57:50 +08:00
bool operator ! = ( const SourceInfo & rhs ) const {
return function ! = rhs . function | |
line_entry . original_file ! = rhs . line_entry . original_file | |
line_entry . line ! = rhs . line_entry . line ;
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
bool operator < ( const SourceInfo & rhs ) const {
if ( function . GetCString ( ) < rhs . function . GetCString ( ) )
return true ;
if ( line_entry . file . GetDirectory ( ) . GetCString ( ) <
rhs . line_entry . file . GetDirectory ( ) . GetCString ( ) )
return true ;
if ( line_entry . file . GetFilename ( ) . GetCString ( ) <
rhs . line_entry . file . GetFilename ( ) . GetCString ( ) )
return true ;
if ( line_entry . line < rhs . line_entry . line )
return true ;
return false ;
2010-07-07 11:36:20 +08:00
}
2016-09-07 04:57:50 +08:00
} ;
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
size_t DisplayFunctionSource ( const SymbolContext & sc , SourceInfo & source_info ,
CommandReturnObject & result ) {
if ( ! source_info . IsValid ( ) ) {
source_info . function = sc . GetFunctionName ( ) ;
source_info . line_entry = sc . GetFunctionStartLineEntry ( ) ;
2012-06-09 05:56:10 +08:00
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
if ( sc . function ) {
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
FileSpec start_file ;
uint32_t start_line ;
uint32_t end_line ;
FileSpec end_file ;
if ( sc . block = = nullptr ) {
// Not an inlined function
sc . function - > GetStartLineSourceInfo ( start_file , start_line ) ;
if ( start_line = = 0 ) {
result . AppendErrorWithFormat ( " Could not find line information for "
" start of function: \" %s \" . \n " ,
source_info . function . GetCString ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return 0 ;
2013-05-17 08:56:10 +08:00
}
2016-09-07 04:57:50 +08:00
sc . function - > GetEndLineSourceInfo ( end_file , end_line ) ;
} else {
// We have an inlined function
start_file = source_info . line_entry . file ;
start_line = source_info . line_entry . line ;
end_line = start_line + m_options . num_lines ;
}
// This is a little hacky, but the first line table entry for a function
2018-05-01 00:49:04 +08:00
// points to the "{" that starts the function block. It would be nice to
// actually get the function declaration in there too. So back up a bit,
// but not further than what you're going to display.
2016-09-07 04:57:50 +08:00
uint32_t extra_lines ;
if ( m_options . num_lines > = 10 )
extra_lines = 5 ;
else
extra_lines = m_options . num_lines / 2 ;
uint32_t line_no ;
if ( start_line < = extra_lines )
line_no = 1 ;
else
line_no = start_line - extra_lines ;
// For fun, if the function is shorter than the number of lines we're
2018-05-01 00:49:04 +08:00
// supposed to display, only display the function...
2016-09-07 04:57:50 +08:00
if ( end_line ! = 0 ) {
if ( m_options . num_lines > end_line - line_no )
m_options . num_lines = end_line - line_no + extra_lines ;
}
m_breakpoint_locations . Clear ( ) ;
if ( m_options . show_bp_locs ) {
const bool show_inlines = true ;
m_breakpoint_locations . Reset ( start_file , 0 , show_inlines ) ;
SearchFilterForUnconstrainedSearches target_search_filter (
m_exe_ctx . GetTargetSP ( ) ) ;
target_search_filter . Search ( m_breakpoint_locations ) ;
}
result . AppendMessageWithFormat ( " File: %s \n " ,
start_file . GetPath ( ) . c_str ( ) ) ;
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
// We don't care about the column here.
const uint32_t column = 0 ;
2016-09-07 04:57:50 +08:00
return target - > GetSourceManager ( ) . DisplaySourceLinesWithLineNumbers (
2017-11-17 09:19:59 +08:00
start_file , line_no , column , 0 , m_options . num_lines , " " ,
2016-09-07 04:57:50 +08:00
& result . GetOutputStream ( ) , GetBreakpointLocations ( ) ) ;
} else {
result . AppendErrorWithFormat (
" Could not find function info for: \" %s \" . \n " ,
m_options . symbol_name . c_str ( ) ) ;
}
return 0 ;
}
2018-05-01 00:49:04 +08:00
// From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
// functions "take a possibly empty vector of strings which are names of
// modules, and run the two search functions on the subset of the full module
// list that matches the strings in the input vector". If we wanted to put
// these somewhere, there should probably be a module-filter-list that can be
// passed to the various ModuleList::Find* calls, which would either be a
// vector of string names or a ModuleSpecList.
2016-09-07 04:57:50 +08:00
size_t FindMatchingFunctions ( Target * target , const ConstString & name ,
SymbolContextList & sc_list ) {
// Displaying the source for a symbol:
bool include_inlines = true ;
bool append = true ;
bool include_symbols = false ;
size_t num_matches = 0 ;
if ( m_options . num_lines = = 0 )
m_options . num_lines = 10 ;
const size_t num_modules = m_options . modules . size ( ) ;
if ( num_modules > 0 ) {
ModuleList matching_modules ;
for ( size_t i = 0 ; i < num_modules ; + + i ) {
2016-11-03 04:34:10 +08:00
FileSpec module_file_spec ( m_options . modules [ i ] , false ) ;
2016-09-07 04:57:50 +08:00
if ( module_file_spec ) {
ModuleSpec module_spec ( module_file_spec ) ;
matching_modules . Clear ( ) ;
target - > GetImages ( ) . FindModules ( module_spec , matching_modules ) ;
num_matches + = matching_modules . FindFunctions (
name , eFunctionNameTypeAuto , include_symbols , include_inlines ,
append , sc_list ) ;
2013-05-17 08:56:10 +08:00
}
2016-09-07 04:57:50 +08:00
}
} else {
num_matches = target - > GetImages ( ) . FindFunctions (
name , eFunctionNameTypeAuto , include_symbols , include_inlines , append ,
sc_list ) ;
}
return num_matches ;
}
size_t FindMatchingFunctionSymbols ( Target * target , const ConstString & name ,
SymbolContextList & sc_list ) {
size_t num_matches = 0 ;
const size_t num_modules = m_options . modules . size ( ) ;
if ( num_modules > 0 ) {
ModuleList matching_modules ;
for ( size_t i = 0 ; i < num_modules ; + + i ) {
2016-11-03 04:34:10 +08:00
FileSpec module_file_spec ( m_options . modules [ i ] , false ) ;
2016-09-07 04:57:50 +08:00
if ( module_file_spec ) {
ModuleSpec module_spec ( module_file_spec ) ;
matching_modules . Clear ( ) ;
target - > GetImages ( ) . FindModules ( module_spec , matching_modules ) ;
num_matches + = matching_modules . FindFunctionSymbols (
name , eFunctionNameTypeAuto , sc_list ) ;
2013-05-17 08:56:10 +08:00
}
2016-09-07 04:57:50 +08:00
}
} else {
num_matches = target - > GetImages ( ) . FindFunctionSymbols (
name , eFunctionNameTypeAuto , sc_list ) ;
}
return num_matches ;
}
2013-05-17 08:56:10 +08:00
2016-09-07 04:57:50 +08:00
bool DoExecute ( Args & command , CommandReturnObject & result ) override {
const size_t argc = command . GetArgumentCount ( ) ;
if ( argc ! = 0 ) {
result . AppendErrorWithFormat ( " '%s' takes no arguments, only flags. \n " ,
2016-10-06 05:14:38 +08:00
GetCommandName ( ) . str ( ) . c_str ( ) ) ;
2016-09-07 04:57:50 +08:00
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
2013-05-17 08:56:10 +08:00
2016-09-07 04:57:50 +08:00
Target * target = m_exe_ctx . GetTargetPtr ( ) ;
if ( ! m_options . symbol_name . empty ( ) ) {
SymbolContextList sc_list ;
ConstString name ( m_options . symbol_name . c_str ( ) ) ;
// Displaying the source for a symbol. Search for function named name.
size_t num_matches = FindMatchingFunctions ( target , name , sc_list ) ;
if ( ! num_matches ) {
// If we didn't find any functions with that name, try searching for
2018-05-01 00:49:04 +08:00
// symbols that line up exactly with function addresses.
2016-09-07 04:57:50 +08:00
SymbolContextList sc_list_symbols ;
size_t num_symbol_matches =
FindMatchingFunctionSymbols ( target , name , sc_list_symbols ) ;
for ( size_t i = 0 ; i < num_symbol_matches ; i + + ) {
SymbolContext sc ;
sc_list_symbols . GetContextAtIndex ( i , sc ) ;
if ( sc . symbol & & sc . symbol - > ValueIsAddress ( ) ) {
const Address & base_address = sc . symbol - > GetAddressRef ( ) ;
Function * function = base_address . CalculateSymbolContextFunction ( ) ;
if ( function ) {
sc_list . Append ( SymbolContext ( function ) ) ;
num_matches + + ;
break ;
2013-05-17 08:56:10 +08:00
}
2016-09-07 04:57:50 +08:00
}
2013-05-17 08:56:10 +08:00
}
2016-09-07 04:57:50 +08:00
}
2013-05-17 08:56:10 +08:00
2016-09-07 04:57:50 +08:00
if ( num_matches = = 0 ) {
result . AppendErrorWithFormat ( " Could not find function named: \" %s \" . \n " ,
m_options . symbol_name . c_str ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
if ( num_matches > 1 ) {
std : : set < SourceInfo > source_match_set ;
2013-07-12 00:40:56 +08:00
2016-09-07 04:57:50 +08:00
bool displayed_something = false ;
for ( size_t i = 0 ; i < num_matches ; i + + ) {
SymbolContext sc ;
sc_list . GetContextAtIndex ( i , sc ) ;
SourceInfo source_info ( sc . GetFunctionName ( ) ,
sc . GetFunctionStartLineEntry ( ) ) ;
if ( source_info . IsValid ( ) ) {
if ( source_match_set . find ( source_info ) = = source_match_set . end ( ) ) {
source_match_set . insert ( source_info ) ;
if ( DisplayFunctionSource ( sc , source_info , result ) )
displayed_something = true ;
2013-07-12 00:40:56 +08:00
}
2016-09-07 04:57:50 +08:00
}
2013-07-12 00:40:56 +08:00
}
2016-09-07 04:57:50 +08:00
if ( displayed_something )
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
2013-07-12 00:40:56 +08:00
else
2016-09-07 04:57:50 +08:00
result . SetStatus ( eReturnStatusFailed ) ;
} else {
SymbolContext sc ;
sc_list . GetContextAtIndex ( 0 , sc ) ;
SourceInfo source_info ;
if ( DisplayFunctionSource ( sc , source_info , result ) ) {
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
} else {
result . SetStatus ( eReturnStatusFailed ) ;
}
}
return result . Succeeded ( ) ;
} else if ( m_options . address ! = LLDB_INVALID_ADDRESS ) {
Address so_addr ;
StreamString error_strm ;
SymbolContextList sc_list ;
if ( target - > GetSectionLoadList ( ) . IsEmpty ( ) ) {
2018-05-01 00:49:04 +08:00
// The target isn't loaded yet, we need to lookup the file address in
// all modules
2016-09-07 04:57:50 +08:00
const ModuleList & module_list = target - > GetImages ( ) ;
const size_t num_modules = module_list . GetSize ( ) ;
for ( size_t i = 0 ; i < num_modules ; + + i ) {
ModuleSP module_sp ( module_list . GetModuleAtIndex ( i ) ) ;
if ( module_sp & &
module_sp - > ResolveFileAddress ( m_options . address , so_addr ) ) {
SymbolContext sc ;
sc . Clear ( true ) ;
if ( module_sp - > ResolveSymbolContextForAddress (
so_addr , eSymbolContextEverything , sc ) &
eSymbolContextLineEntry )
sc_list . Append ( sc ) ;
}
2013-07-12 00:40:56 +08:00
}
2016-09-07 04:57:50 +08:00
if ( sc_list . GetSize ( ) = = 0 ) {
result . AppendErrorWithFormat (
" no modules have source information for file address 0x% " PRIx64
" . \n " ,
m_options . address ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
2013-07-12 00:40:56 +08:00
}
2016-09-07 04:57:50 +08:00
} else {
2018-05-01 00:49:04 +08:00
// The target has some things loaded, resolve this address to a compile
// unit + file + line and display
2016-09-07 04:57:50 +08:00
if ( target - > GetSectionLoadList ( ) . ResolveLoadAddress ( m_options . address ,
so_addr ) ) {
ModuleSP module_sp ( so_addr . GetModule ( ) ) ;
if ( module_sp ) {
SymbolContext sc ;
sc . Clear ( true ) ;
if ( module_sp - > ResolveSymbolContextForAddress (
so_addr , eSymbolContextEverything , sc ) &
eSymbolContextLineEntry ) {
sc_list . Append ( sc ) ;
} else {
so_addr . Dump ( & error_strm , nullptr ,
Address : : DumpStyleModuleWithFileAddress ) ;
result . AppendErrorWithFormat ( " address resolves to %s, but there "
" is no line table information "
" available for this address. \n " ,
error_strm . GetData ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
}
2013-07-12 00:40:56 +08:00
}
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
if ( sc_list . GetSize ( ) = = 0 ) {
result . AppendErrorWithFormat (
" no modules contain load address 0x% " PRIx64 " . \n " ,
m_options . address ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
2010-07-07 11:36:20 +08:00
}
2016-09-07 04:57:50 +08:00
}
uint32_t num_matches = sc_list . GetSize ( ) ;
for ( uint32_t i = 0 ; i < num_matches ; + + i ) {
SymbolContext sc ;
sc_list . GetContextAtIndex ( i , sc ) ;
if ( sc . comp_unit ) {
if ( m_options . show_bp_locs ) {
m_breakpoint_locations . Clear ( ) ;
const bool show_inlines = true ;
m_breakpoint_locations . Reset ( * sc . comp_unit , 0 , show_inlines ) ;
SearchFilterForUnconstrainedSearches target_search_filter (
target - > shared_from_this ( ) ) ;
target_search_filter . Search ( m_breakpoint_locations ) ;
}
bool show_fullpaths = true ;
bool show_module = true ;
bool show_inlined_frames = true ;
const bool show_function_arguments = true ;
const bool show_function_name = true ;
sc . DumpStopContext ( & result . GetOutputStream ( ) ,
m_exe_ctx . GetBestExecutionContextScope ( ) ,
sc . line_entry . range . GetBaseAddress ( ) ,
show_fullpaths , show_module , show_inlined_frames ,
show_function_arguments , show_function_name ) ;
result . GetOutputStream ( ) . EOL ( ) ;
if ( m_options . num_lines = = 0 )
m_options . num_lines = 10 ;
2010-07-07 11:36:20 +08:00
2016-09-07 04:57:50 +08:00
size_t lines_to_back_up =
m_options . num_lines > = 10 ? 5 : m_options . num_lines / 2 ;
2013-07-12 00:40:56 +08:00
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
const uint32_t column =
( m_interpreter . GetDebugger ( ) . GetStopShowColumn ( ) ! =
eStopShowColumnNone )
? sc . line_entry . column
: 0 ;
2016-09-07 04:57:50 +08:00
target - > GetSourceManager ( ) . DisplaySourceLinesWithLineNumbers (
2017-11-17 09:19:59 +08:00
sc . comp_unit , sc . line_entry . line , column , lines_to_back_up ,
2016-09-07 04:57:50 +08:00
m_options . num_lines - lines_to_back_up , " -> " ,
& result . GetOutputStream ( ) , GetBreakpointLocations ( ) ) ;
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
2010-08-20 09:17:07 +08:00
}
2016-09-07 04:57:50 +08:00
}
} else if ( m_options . file_name . empty ( ) ) {
2018-05-01 00:49:04 +08:00
// Last valid source manager context, or the current frame if no valid
// last context in source manager. One little trick here, if you type the
// exact same list command twice in a row, it is more likely because you
// typed it once, then typed it again
2016-09-07 04:57:50 +08:00
if ( m_options . start_line = = 0 ) {
if ( target - > GetSourceManager ( ) . DisplayMoreWithLineNumbers (
& result . GetOutputStream ( ) , m_options . num_lines ,
m_options . reverse , GetBreakpointLocations ( ) ) ) {
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
2012-12-07 08:19:47 +08:00
}
2016-09-07 04:57:50 +08:00
} else {
if ( m_options . num_lines = = 0 )
m_options . num_lines = 10 ;
if ( m_options . show_bp_locs ) {
SourceManager : : FileSP last_file_sp (
target - > GetSourceManager ( ) . GetLastFile ( ) ) ;
if ( last_file_sp ) {
const bool show_inlines = true ;
m_breakpoint_locations . Reset ( last_file_sp - > GetFileSpec ( ) , 0 ,
show_inlines ) ;
SearchFilterForUnconstrainedSearches target_search_filter (
target - > shared_from_this ( ) ) ;
target_search_filter . Search ( m_breakpoint_locations ) ;
}
} else
m_breakpoint_locations . Clear ( ) ;
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
const uint32_t column = 0 ;
2016-09-07 04:57:50 +08:00
if ( target - > GetSourceManager ( )
. DisplaySourceLinesWithLineNumbersUsingLastFile (
m_options . start_line , // Line to display
m_options . num_lines , // Lines after line to
UINT32_MAX , // Don't mark "line"
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
column ,
" " , // Don't mark "line"
2016-09-07 04:57:50 +08:00
& result . GetOutputStream ( ) , GetBreakpointLocations ( ) ) ) {
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
2010-06-09 00:52:24 +08:00
}
2016-09-07 04:57:50 +08:00
}
} else {
const char * filename = m_options . file_name . c_str ( ) ;
bool check_inlines = false ;
SymbolContextList sc_list ;
size_t num_matches = 0 ;
if ( ! m_options . modules . empty ( ) ) {
ModuleList matching_modules ;
for ( size_t i = 0 , e = m_options . modules . size ( ) ; i < e ; + + i ) {
2016-11-03 04:34:10 +08:00
FileSpec module_file_spec ( m_options . modules [ i ] , false ) ;
2016-09-07 04:57:50 +08:00
if ( module_file_spec ) {
ModuleSpec module_spec ( module_file_spec ) ;
matching_modules . Clear ( ) ;
target - > GetImages ( ) . FindModules ( module_spec , matching_modules ) ;
num_matches + = matching_modules . ResolveSymbolContextForFilePath (
filename , 0 , check_inlines ,
eSymbolContextModule | eSymbolContextCompUnit , sc_list ) ;
}
}
} else {
num_matches = target - > GetImages ( ) . ResolveSymbolContextForFilePath (
filename , 0 , check_inlines ,
eSymbolContextModule | eSymbolContextCompUnit , sc_list ) ;
}
if ( num_matches = = 0 ) {
result . AppendErrorWithFormat ( " Could not find source file \" %s \" . \n " ,
m_options . file_name . c_str ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
if ( num_matches > 1 ) {
bool got_multiple = false ;
FileSpec * test_cu_spec = nullptr ;
for ( unsigned i = 0 ; i < num_matches ; i + + ) {
SymbolContext sc ;
sc_list . GetContextAtIndex ( i , sc ) ;
if ( sc . comp_unit ) {
if ( test_cu_spec ) {
if ( test_cu_spec ! = static_cast < FileSpec * > ( sc . comp_unit ) )
got_multiple = true ;
break ;
} else
test_cu_spec = sc . comp_unit ;
}
}
if ( got_multiple ) {
result . AppendErrorWithFormat (
" Multiple source files found matching: \" %s. \" \n " ,
m_options . file_name . c_str ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
}
SymbolContext sc ;
if ( sc_list . GetContextAtIndex ( 0 , sc ) ) {
if ( sc . comp_unit ) {
if ( m_options . show_bp_locs ) {
const bool show_inlines = true ;
m_breakpoint_locations . Reset ( * sc . comp_unit , 0 , show_inlines ) ;
SearchFilterForUnconstrainedSearches target_search_filter (
target - > shared_from_this ( ) ) ;
target_search_filter . Search ( m_breakpoint_locations ) ;
} else
m_breakpoint_locations . Clear ( ) ;
2014-08-18 22:48:24 +08:00
2016-09-07 04:57:50 +08:00
if ( m_options . num_lines = = 0 )
m_options . num_lines = 10 ;
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
const uint32_t column = 0 ;
2016-09-07 04:57:50 +08:00
target - > GetSourceManager ( ) . DisplaySourceLinesWithLineNumbers (
2017-11-17 09:19:59 +08:00
sc . comp_unit , m_options . start_line , column ,
0 , m_options . num_lines ,
" " , & result . GetOutputStream ( ) , GetBreakpointLocations ( ) ) ;
2016-09-07 04:57:50 +08:00
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
} else {
result . AppendErrorWithFormat ( " No comp unit found for: \" %s. \" \n " ,
m_options . file_name . c_str ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
2010-06-09 00:52:24 +08:00
}
2016-09-07 04:57:50 +08:00
}
2010-06-09 00:52:24 +08:00
}
2016-09-07 04:57:50 +08:00
return result . Succeeded ( ) ;
}
const SymbolContextList * GetBreakpointLocations ( ) {
if ( m_breakpoint_locations . GetFileLineMatches ( ) . GetSize ( ) > 0 )
return & m_breakpoint_locations . GetFileLineMatches ( ) ;
return nullptr ;
}
CommandOptions m_options ;
FileLineResolver m_breakpoint_locations ;
std : : string m_reverse_name ;
2010-07-07 11:36:20 +08:00
} ;
# pragma mark CommandObjectMultiwordSource
//-------------------------------------------------------------------------
// CommandObjectMultiwordSource
//-------------------------------------------------------------------------
2016-09-07 04:57:50 +08:00
CommandObjectMultiwordSource : : CommandObjectMultiwordSource (
CommandInterpreter & interpreter )
: CommandObjectMultiword ( interpreter , " source " , " Commands for examining "
" source code described by "
" debug information for the "
" current target process. " ,
" source <subcommand> [<subcommand-options>] " ) {
LoadSubCommand ( " info " ,
CommandObjectSP ( new CommandObjectSourceInfo ( interpreter ) ) ) ;
LoadSubCommand ( " list " ,
CommandObjectSP ( new CommandObjectSourceList ( interpreter ) ) ) ;
2010-07-07 11:36:20 +08:00
}
2016-02-24 10:05:55 +08:00
CommandObjectMultiwordSource : : ~ CommandObjectMultiwordSource ( ) = default ;