2015-02-05 06:00:53 +08:00
//===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
# include "llvm/ADT/StringRef.h"
# include "lldb/Core/FormatEntity.h"
# include "lldb/Core/Address.h"
# include "lldb/Core/Debugger.h"
# include "lldb/Core/Module.h"
# include "lldb/Core/Stream.h"
# include "lldb/Core/StreamString.h"
# include "lldb/Core/ValueObject.h"
# include "lldb/Core/ValueObjectVariable.h"
# include "lldb/DataFormatters/DataVisualization.h"
# include "lldb/DataFormatters/FormatManager.h"
# include "lldb/Host/FileSpec.h"
# include "lldb/Interpreter/CommandInterpreter.h"
# include "lldb/Symbol/Block.h"
# include "lldb/Symbol/CompileUnit.h"
# include "lldb/Symbol/Function.h"
# include "lldb/Symbol/LineEntry.h"
# include "lldb/Symbol/Symbol.h"
# include "lldb/Symbol/VariableList.h"
# include "lldb/Target/ExecutionContext.h"
# include "lldb/Target/Process.h"
# include "lldb/Target/RegisterContext.h"
# include "lldb/Target/SectionLoadList.h"
# include "lldb/Target/StackFrame.h"
# include "lldb/Target/StopInfo.h"
# include "lldb/Target/Target.h"
# include "lldb/Target/Thread.h"
# include "lldb/Utility/AnsiTerminal.h"
using namespace lldb ;
using namespace lldb_private ;
enum FileKind
{
FileError = 0 ,
Basename ,
Dirname ,
Fullpath
} ;
# define ENTRY(n,t,f) { n, NULL, FormatEntity::Entry::Type::t, FormatEntity::Entry::FormatType::f, 0,0,NULL, false}
# define ENTRY_VALUE(n,t,f,v) { n, NULL, FormatEntity::Entry::Type::t, FormatEntity::Entry::FormatType::f, v,0,NULL, false}
# define ENTRY_CHILDREN(n,t,f,c) { n, NULL, FormatEntity::Entry::Type::t, FormatEntity::Entry::FormatType::f, 0,llvm::array_lengthof(c),c, false}
# define ENTRY_CHILDREN_KEEP_SEP(n,t,f,c) { n, NULL, FormatEntity::Entry::Type::t, FormatEntity::Entry::FormatType::f, 0,llvm::array_lengthof(c),c, true}
# define ENTRY_STRING(n,s) { n, s, FormatEntity::Entry::Type::InsertString, FormatEntity::Entry::FormatType::None, 0,0, NULL, false}
static FormatEntity : : Entry : : Definition g_string_entry [ ] =
{
ENTRY ( " * " , ParentString , None )
} ;
static FormatEntity : : Entry : : Definition g_addr_entries [ ] =
{
ENTRY ( " load " , AddressLoad , UInt64 ) ,
ENTRY ( " file " , AddressFile , UInt64 ) ,
ENTRY ( " load " , AddressLoadOrFile , UInt64 ) ,
} ;
static FormatEntity : : Entry : : Definition g_file_child_entries [ ] =
{
ENTRY_VALUE ( " basename " , ParentNumber , CString , FileKind : : Basename ) ,
ENTRY_VALUE ( " dirname " , ParentNumber , CString , FileKind : : Dirname ) ,
ENTRY_VALUE ( " fullpath " , ParentNumber , CString , FileKind : : Fullpath )
} ;
static FormatEntity : : Entry : : Definition g_frame_child_entries [ ] =
{
ENTRY ( " index " , FrameIndex , UInt32 ) ,
ENTRY ( " pc " , FrameRegisterPC , UInt64 ) ,
ENTRY ( " fp " , FrameRegisterFP , UInt64 ) ,
ENTRY ( " sp " , FrameRegisterSP , UInt64 ) ,
ENTRY ( " flags " , FrameRegisterFlags , UInt64 ) ,
ENTRY_CHILDREN ( " reg " , FrameRegisterByName , UInt64 , g_string_entry ) ,
} ;
static FormatEntity : : Entry : : Definition g_function_child_entries [ ] =
{
ENTRY ( " id " , FunctionID , UInt64 ) ,
ENTRY ( " name " , FunctionName , CString ) ,
ENTRY ( " name-without-args " , FunctionNameNoArgs , CString ) ,
ENTRY ( " name-with-args " , FunctionNameWithArgs , CString ) ,
ENTRY ( " addr-offset " , FunctionAddrOffset , UInt64 ) ,
ENTRY ( " concrete-only-addr-offset-no-padding " , FunctionAddrOffsetConcrete , UInt64 ) ,
ENTRY ( " line-offset " , FunctionLineOffset , UInt64 ) ,
ENTRY ( " pc-offset " , FunctionPCOffset , UInt64 )
} ;
static FormatEntity : : Entry : : Definition g_line_child_entries [ ] =
{
ENTRY_CHILDREN ( " file " , LineEntryFile , None , g_file_child_entries ) ,
ENTRY ( " number " , LineEntryLineNumber , UInt32 ) ,
ENTRY ( " start-addr " , LineEntryStartAddress , UInt64 ) ,
ENTRY ( " end-addr " , LineEntryEndAddress , UInt64 ) ,
} ;
static FormatEntity : : Entry : : Definition g_module_child_entries [ ] =
{
ENTRY_CHILDREN ( " file " , ModuleFile , None , g_file_child_entries ) ,
} ;
static FormatEntity : : Entry : : Definition g_process_child_entries [ ] =
{
ENTRY ( " id " , ProcessID , UInt64 ) ,
ENTRY_VALUE ( " name " , ProcessFile , CString , FileKind : : Basename ) ,
ENTRY_CHILDREN ( " file " , ProcessFile , None , g_file_child_entries ) ,
} ;
static FormatEntity : : Entry : : Definition g_svar_child_entries [ ] =
{
ENTRY ( " * " , ParentString , None )
} ;
static FormatEntity : : Entry : : Definition g_var_child_entries [ ] =
{
ENTRY ( " * " , ParentString , None )
} ;
static FormatEntity : : Entry : : Definition g_thread_child_entries [ ] =
{
ENTRY ( " id " , ThreadID , UInt64 ) ,
ENTRY ( " protocol_id " , ThreadProtocolID , UInt64 ) ,
ENTRY ( " index " , ThreadIndexID , UInt32 ) ,
ENTRY_CHILDREN ( " info " , ThreadInfo , None , g_string_entry ) ,
ENTRY ( " queue " , ThreadQueue , CString ) ,
ENTRY ( " name " , ThreadName , CString ) ,
ENTRY ( " stop-reason " , ThreadStopReason , CString ) ,
ENTRY ( " return-value " , ThreadReturnValue , CString ) ,
ENTRY ( " completed-expression " , ThreadCompletedExpression , CString ) ,
} ;
static FormatEntity : : Entry : : Definition g_target_child_entries [ ] =
{
ENTRY ( " arch " , TargetArch , CString ) ,
} ;
# define _TO_STR2(_val) #_val
# define _TO_STR(_val) _TO_STR2(_val)
static FormatEntity : : Entry : : Definition g_ansi_fg_entries [ ] =
{
ENTRY_STRING ( " black " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_BLACK ) ANSI_ESC_END ) ,
ENTRY_STRING ( " red " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_RED ) ANSI_ESC_END ) ,
ENTRY_STRING ( " green " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_GREEN ) ANSI_ESC_END ) ,
ENTRY_STRING ( " yellow " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_YELLOW ) ANSI_ESC_END ) ,
ENTRY_STRING ( " blue " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_BLUE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " purple " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_PURPLE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " cyan " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_CYAN ) ANSI_ESC_END ) ,
ENTRY_STRING ( " white " , ANSI_ESC_START _TO_STR ( ANSI_FG_COLOR_WHITE ) ANSI_ESC_END ) ,
} ;
static FormatEntity : : Entry : : Definition g_ansi_bg_entries [ ] =
{
ENTRY_STRING ( " black " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_BLACK ) ANSI_ESC_END ) ,
ENTRY_STRING ( " red " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_RED ) ANSI_ESC_END ) ,
ENTRY_STRING ( " green " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_GREEN ) ANSI_ESC_END ) ,
ENTRY_STRING ( " yellow " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_YELLOW ) ANSI_ESC_END ) ,
ENTRY_STRING ( " blue " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_BLUE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " purple " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_PURPLE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " cyan " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_CYAN ) ANSI_ESC_END ) ,
ENTRY_STRING ( " white " , ANSI_ESC_START _TO_STR ( ANSI_BG_COLOR_WHITE ) ANSI_ESC_END ) ,
} ;
static FormatEntity : : Entry : : Definition g_ansi_entries [ ] =
{
ENTRY_CHILDREN ( " fg " , Invalid , None , g_ansi_fg_entries ) ,
ENTRY_CHILDREN ( " bg " , Invalid , None , g_ansi_bg_entries ) ,
ENTRY_STRING ( " normal " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_NORMAL ) ANSI_ESC_END ) ,
ENTRY_STRING ( " bold " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_BOLD ) ANSI_ESC_END ) ,
ENTRY_STRING ( " faint " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_FAINT ) ANSI_ESC_END ) ,
ENTRY_STRING ( " italic " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_ITALIC ) ANSI_ESC_END ) ,
ENTRY_STRING ( " underline " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_UNDERLINE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " slow-blink " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_SLOW_BLINK ) ANSI_ESC_END ) ,
ENTRY_STRING ( " fast-blink " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_FAST_BLINK ) ANSI_ESC_END ) ,
ENTRY_STRING ( " negative " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_IMAGE_NEGATIVE ) ANSI_ESC_END ) ,
ENTRY_STRING ( " conceal " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_CONCEAL ) ANSI_ESC_END ) ,
ENTRY_STRING ( " crossed-out " , ANSI_ESC_START _TO_STR ( ANSI_CTRL_CROSSED_OUT ) ANSI_ESC_END ) ,
} ;
static FormatEntity : : Entry : : Definition g_script_child_entries [ ] =
{
ENTRY ( " frame " , ScriptFrame , None ) ,
ENTRY ( " process " , ScriptProcess , None ) ,
ENTRY ( " target " , ScriptTarget , None ) ,
ENTRY ( " thread " , ScriptThread , None ) ,
ENTRY ( " var " , ScriptVariable , None ) ,
ENTRY ( " svar " , ScriptVariableSynthetic , None ) ,
ENTRY ( " thread " , ScriptThread , None ) ,
} ;
static FormatEntity : : Entry : : Definition g_top_level_entries [ ] =
{
ENTRY_CHILDREN ( " addr " , AddressLoadOrFile , UInt64 , g_addr_entries ) ,
ENTRY ( " addr-file-or-load " , AddressLoadOrFile , UInt64 ) ,
ENTRY_CHILDREN ( " ansi " , Invalid , None , g_ansi_entries ) ,
ENTRY ( " current-pc-arrow " , CurrentPCArrow , CString ) ,
ENTRY_CHILDREN ( " file " , File , CString , g_file_child_entries ) ,
ENTRY_CHILDREN ( " frame " , Invalid , None , g_frame_child_entries ) ,
ENTRY_CHILDREN ( " function " , Invalid , None , g_function_child_entries ) ,
ENTRY_CHILDREN ( " line " , Invalid , None , g_line_child_entries ) ,
ENTRY_CHILDREN ( " module " , Invalid , None , g_module_child_entries ) ,
ENTRY_CHILDREN ( " process " , Invalid , None , g_process_child_entries ) ,
ENTRY_CHILDREN ( " script " , Invalid , None , g_script_child_entries ) ,
ENTRY_CHILDREN_KEEP_SEP ( " svar " , VariableSynthetic , None , g_svar_child_entries ) ,
ENTRY_CHILDREN ( " thread " , Invalid , None , g_thread_child_entries ) ,
ENTRY_CHILDREN ( " target " , Invalid , None , g_target_child_entries ) ,
ENTRY_CHILDREN_KEEP_SEP ( " var " , Variable , None , g_var_child_entries ) ,
} ;
static FormatEntity : : Entry : : Definition g_root = ENTRY_CHILDREN ( " <root> " , Root , None , g_top_level_entries ) ;
FormatEntity : : Entry : : Entry ( llvm : : StringRef s ) :
string ( s . data ( ) , s . size ( ) ) ,
printf_format ( ) ,
children ( ) ,
definition ( NULL ) ,
type ( Type : : String ) ,
fmt ( lldb : : eFormatDefault ) ,
number ( 0 ) ,
deref ( false )
{
}
FormatEntity : : Entry : : Entry ( char ch ) :
string ( 1 , ch ) ,
printf_format ( ) ,
children ( ) ,
definition ( NULL ) ,
type ( Type : : String ) ,
fmt ( lldb : : eFormatDefault ) ,
number ( 0 ) ,
deref ( false )
{
}
void
FormatEntity : : Entry : : AppendChar ( char ch )
{
if ( children . empty ( ) | | children . back ( ) . type ! = Entry : : Type : : String )
children . push_back ( Entry ( ch ) ) ;
else
children . back ( ) . string . append ( 1 , ch ) ;
}
void
FormatEntity : : Entry : : AppendText ( const llvm : : StringRef & s )
{
if ( children . empty ( ) | | children . back ( ) . type ! = Entry : : Type : : String )
children . push_back ( Entry ( s ) ) ;
else
children . back ( ) . string . append ( s . data ( ) , s . size ( ) ) ;
}
void
FormatEntity : : Entry : : AppendText ( const char * cstr )
{
return AppendText ( llvm : : StringRef ( cstr ) ) ;
}
Error
FormatEntity : : Parse ( const llvm : : StringRef & format_str , Entry & entry )
{
entry . Clear ( ) ;
entry . type = Entry : : Type : : Root ;
llvm : : StringRef modifiable_format ( format_str ) ;
return ParseInternal ( modifiable_format , entry , 0 ) ;
}
# define ENUM_TO_CSTR(eee) case FormatEntity::Entry::Type::eee: return #eee
const char *
FormatEntity : : Entry : : TypeToCString ( Type t )
{
switch ( t )
{
ENUM_TO_CSTR ( Invalid ) ;
ENUM_TO_CSTR ( ParentNumber ) ;
ENUM_TO_CSTR ( ParentString ) ;
ENUM_TO_CSTR ( InsertString ) ;
ENUM_TO_CSTR ( Root ) ;
ENUM_TO_CSTR ( String ) ;
ENUM_TO_CSTR ( Scope ) ;
ENUM_TO_CSTR ( Variable ) ;
ENUM_TO_CSTR ( VariableSynthetic ) ;
ENUM_TO_CSTR ( ScriptVariable ) ;
ENUM_TO_CSTR ( ScriptVariableSynthetic ) ;
ENUM_TO_CSTR ( AddressLoad ) ;
ENUM_TO_CSTR ( AddressFile ) ;
ENUM_TO_CSTR ( AddressLoadOrFile ) ;
ENUM_TO_CSTR ( ProcessID ) ;
ENUM_TO_CSTR ( ProcessFile ) ;
ENUM_TO_CSTR ( ScriptProcess ) ;
ENUM_TO_CSTR ( ThreadID ) ;
ENUM_TO_CSTR ( ThreadProtocolID ) ;
ENUM_TO_CSTR ( ThreadIndexID ) ;
ENUM_TO_CSTR ( ThreadName ) ;
ENUM_TO_CSTR ( ThreadQueue ) ;
ENUM_TO_CSTR ( ThreadStopReason ) ;
ENUM_TO_CSTR ( ThreadReturnValue ) ;
ENUM_TO_CSTR ( ThreadCompletedExpression ) ;
ENUM_TO_CSTR ( ScriptThread ) ;
ENUM_TO_CSTR ( ThreadInfo ) ;
ENUM_TO_CSTR ( TargetArch ) ;
ENUM_TO_CSTR ( ScriptTarget ) ;
ENUM_TO_CSTR ( ModuleFile ) ;
ENUM_TO_CSTR ( File ) ;
ENUM_TO_CSTR ( FrameIndex ) ;
ENUM_TO_CSTR ( FrameRegisterPC ) ;
ENUM_TO_CSTR ( FrameRegisterSP ) ;
ENUM_TO_CSTR ( FrameRegisterFP ) ;
ENUM_TO_CSTR ( FrameRegisterFlags ) ;
ENUM_TO_CSTR ( FrameRegisterByName ) ;
ENUM_TO_CSTR ( ScriptFrame ) ;
ENUM_TO_CSTR ( FunctionID ) ;
ENUM_TO_CSTR ( FunctionDidChange ) ;
ENUM_TO_CSTR ( FunctionInitialFunction ) ;
ENUM_TO_CSTR ( FunctionName ) ;
ENUM_TO_CSTR ( FunctionNameWithArgs ) ;
ENUM_TO_CSTR ( FunctionNameNoArgs ) ;
ENUM_TO_CSTR ( FunctionAddrOffset ) ;
ENUM_TO_CSTR ( FunctionAddrOffsetConcrete ) ;
ENUM_TO_CSTR ( FunctionLineOffset ) ;
ENUM_TO_CSTR ( FunctionPCOffset ) ;
ENUM_TO_CSTR ( LineEntryFile ) ;
ENUM_TO_CSTR ( LineEntryLineNumber ) ;
ENUM_TO_CSTR ( LineEntryStartAddress ) ;
ENUM_TO_CSTR ( LineEntryEndAddress ) ;
ENUM_TO_CSTR ( CurrentPCArrow ) ;
}
return " ??? " ;
}
# undef ENUM_TO_CSTR
void
FormatEntity : : Entry : : Dump ( Stream & s , int depth ) const
{
s . Printf ( " %*.*s%-20s: " , depth * 2 , depth * 2 , " " , TypeToCString ( type ) ) ;
if ( fmt ! = eFormatDefault )
s . Printf ( " lldb-format = %s, " , FormatManager : : GetFormatAsCString ( fmt ) ) ;
if ( ! string . empty ( ) )
s . Printf ( " string = \" %s \" " , string . c_str ( ) ) ;
if ( ! printf_format . empty ( ) )
s . Printf ( " printf_format = \" %s \" " , printf_format . c_str ( ) ) ;
if ( number ! = 0 )
s . Printf ( " number = % " PRIu64 " (0x% " PRIx64 " ), " , number , number ) ;
if ( deref )
s . Printf ( " deref = true, " ) ;
s . EOL ( ) ;
for ( const auto & child : children )
{
child . Dump ( s , depth + 1 ) ;
}
}
template < typename T >
static bool RunScriptFormatKeyword ( Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
T t ,
const char * script_function_name )
{
Target * target = Target : : GetTargetFromContexts ( exe_ctx , sc ) ;
if ( target )
{
ScriptInterpreter * script_interpreter = target - > GetDebugger ( ) . GetCommandInterpreter ( ) . GetScriptInterpreter ( ) ;
if ( script_interpreter )
{
Error error ;
std : : string script_output ;
if ( script_interpreter - > RunScriptFormatKeyword ( script_function_name , t , script_output , error ) & & error . Success ( ) )
{
s . Printf ( " %s " , script_output . c_str ( ) ) ;
return true ;
}
else
{
s . Printf ( " <error: %s> " , error . AsCString ( ) ) ;
}
}
}
return false ;
}
static bool
DumpAddress ( Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const Address & addr ,
bool print_file_addr_or_load_addr )
{
Target * target = Target : : GetTargetFromContexts ( exe_ctx , sc ) ;
addr_t vaddr = LLDB_INVALID_ADDRESS ;
if ( exe_ctx & & ! target - > GetSectionLoadList ( ) . IsEmpty ( ) )
vaddr = addr . GetLoadAddress ( target ) ;
if ( vaddr = = LLDB_INVALID_ADDRESS )
vaddr = addr . GetFileAddress ( ) ;
if ( vaddr ! = LLDB_INVALID_ADDRESS )
{
int addr_width = 0 ;
if ( exe_ctx & & target )
{
addr_width = target - > GetArchitecture ( ) . GetAddressByteSize ( ) * 2 ;
}
if ( addr_width = = 0 )
addr_width = 16 ;
if ( print_file_addr_or_load_addr )
{
ExecutionContextScope * exe_scope = NULL ;
if ( exe_ctx )
exe_scope = exe_ctx - > GetBestExecutionContextScope ( ) ;
addr . Dump ( & s , exe_scope , Address : : DumpStyleLoadAddress , Address : : DumpStyleModuleWithFileAddress , 0 ) ;
}
else
{
s . Printf ( " 0x%*.* " PRIx64 , addr_width , addr_width , vaddr ) ;
}
return true ;
}
return false ;
}
static bool
DumpAddressOffsetFromFunction ( Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const Address & format_addr ,
bool concrete_only ,
bool no_padding )
{
if ( format_addr . IsValid ( ) )
{
Address func_addr ;
if ( sc )
{
if ( sc - > function )
{
func_addr = sc - > function - > GetAddressRange ( ) . GetBaseAddress ( ) ;
if ( sc - > block & & ! concrete_only )
{
// Check to make sure we aren't in an inline
// function. If we are, use the inline block
// range that contains "format_addr" since
// blocks can be discontiguous.
Block * inline_block = sc - > block - > GetContainingInlinedBlock ( ) ;
AddressRange inline_range ;
if ( inline_block & & inline_block - > GetRangeContainingAddress ( format_addr , inline_range ) )
func_addr = inline_range . GetBaseAddress ( ) ;
}
}
else if ( sc - > symbol & & sc - > symbol - > ValueIsAddress ( ) )
func_addr = sc - > symbol - > GetAddress ( ) ;
}
if ( func_addr . IsValid ( ) )
{
const char * addr_offset_padding = no_padding ? " " : " " ;
if ( func_addr . GetSection ( ) = = format_addr . GetSection ( ) )
{
addr_t func_file_addr = func_addr . GetFileAddress ( ) ;
addr_t addr_file_addr = format_addr . GetFileAddress ( ) ;
if ( addr_file_addr > func_file_addr )
s . Printf ( " %s+%s% " PRIu64 , addr_offset_padding , addr_offset_padding , addr_file_addr - func_file_addr ) ;
else if ( addr_file_addr < func_file_addr )
s . Printf ( " %s-%s% " PRIu64 , addr_offset_padding , addr_offset_padding , func_file_addr - addr_file_addr ) ;
return true ;
}
else
{
Target * target = Target : : GetTargetFromContexts ( exe_ctx , sc ) ;
if ( target )
{
addr_t func_load_addr = func_addr . GetLoadAddress ( target ) ;
addr_t addr_load_addr = format_addr . GetLoadAddress ( target ) ;
if ( addr_load_addr > func_load_addr )
s . Printf ( " %s+%s% " PRIu64 , addr_offset_padding , addr_offset_padding , addr_load_addr - func_load_addr ) ;
else if ( addr_load_addr < func_load_addr )
s . Printf ( " %s-%s% " PRIu64 , addr_offset_padding , addr_offset_padding , func_load_addr - addr_load_addr ) ;
return true ;
}
}
}
}
return false ;
}
static bool
ScanBracketedRange ( llvm : : StringRef subpath ,
size_t & close_bracket_index ,
const char * & var_name_final_if_array_range ,
int64_t & index_lower ,
int64_t & index_higher )
{
Log * log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_TYPES ) ) ;
close_bracket_index = llvm : : StringRef : : npos ;
const size_t open_bracket_index = subpath . find ( ' [ ' ) ;
if ( open_bracket_index = = llvm : : StringRef : : npos )
{
if ( log )
log - > Printf ( " [ScanBracketedRange] no bracketed range, skipping entirely " ) ;
return false ;
}
close_bracket_index = subpath . find ( ' ] ' , open_bracket_index + 1 ) ;
if ( close_bracket_index = = llvm : : StringRef : : npos )
{
if ( log )
log - > Printf ( " [ScanBracketedRange] no bracketed range, skipping entirely " ) ;
return false ;
}
else
{
var_name_final_if_array_range = subpath . data ( ) + open_bracket_index ;
if ( close_bracket_index - open_bracket_index = = 1 )
{
if ( log )
log - > Printf ( " [ScanBracketedRange] '[]' detected.. going from 0 to end of data " ) ;
index_lower = 0 ;
}
else
{
const size_t separator_index = subpath . find ( ' - ' , open_bracket_index + 1 ) ;
if ( separator_index = = llvm : : StringRef : : npos )
{
const char * index_lower_cstr = subpath . data ( ) + open_bracket_index + 1 ;
index_lower = : : strtoul ( index_lower_cstr , NULL , 0 ) ;
index_higher = index_lower ;
if ( log )
log - > Printf ( " [ScanBracketedRange] [% " PRId64 " ] detected, high index is same " , index_lower ) ;
}
else
{
const char * index_lower_cstr = subpath . data ( ) + open_bracket_index + 1 ;
const char * index_higher_cstr = subpath . data ( ) + separator_index + 1 ;
index_lower = : : strtoul ( index_lower_cstr , NULL , 0 ) ;
index_higher = : : strtoul ( index_higher_cstr , NULL , 0 ) ;
if ( log )
log - > Printf ( " [ScanBracketedRange] [% " PRId64 " -% " PRId64 " ] detected " , index_lower , index_higher ) ;
}
if ( index_lower > index_higher & & index_higher > 0 )
{
if ( log )
log - > Printf ( " [ScanBracketedRange] swapping indices " ) ;
const int64_t temp = index_lower ;
index_lower = index_higher ;
index_higher = temp ;
}
}
}
return true ;
}
static bool
DumpFile ( Stream & s , const FileSpec & file , FileKind file_kind )
{
switch ( file_kind )
{
case FileKind : : FileError :
break ;
case FileKind : : Basename :
if ( file . GetFilename ( ) )
{
s < < file . GetFilename ( ) ;
return true ;
}
break ;
case FileKind : : Dirname :
if ( file . GetDirectory ( ) )
{
s < < file . GetDirectory ( ) ;
return true ;
}
break ;
case FileKind : : Fullpath :
if ( file )
{
s < < file ;
return true ;
}
break ;
}
return false ;
}
static bool
DumpRegister ( Stream & s ,
StackFrame * frame ,
RegisterKind reg_kind ,
uint32_t reg_num ,
Format format )
{
if ( frame )
{
RegisterContext * reg_ctx = frame - > GetRegisterContext ( ) . get ( ) ;
if ( reg_ctx )
{
const uint32_t lldb_reg_num = reg_ctx - > ConvertRegisterKindToRegisterNumber ( reg_kind , reg_num ) ;
if ( lldb_reg_num ! = LLDB_INVALID_REGNUM )
{
const RegisterInfo * reg_info = reg_ctx - > GetRegisterInfoAtIndex ( lldb_reg_num ) ;
if ( reg_info )
{
RegisterValue reg_value ;
if ( reg_ctx - > ReadRegister ( reg_info , reg_value ) )
{
reg_value . Dump ( & s , reg_info , false , false , format ) ;
return true ;
}
}
}
}
}
return false ;
}
static ValueObjectSP
ExpandIndexedExpression ( ValueObject * valobj ,
size_t index ,
StackFrame * frame ,
bool deref_pointer )
{
Log * log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_TYPES ) ) ;
const char * ptr_deref_format = " [%d] " ;
std : : string ptr_deref_buffer ( 10 , 0 ) ;
: : sprintf ( & ptr_deref_buffer [ 0 ] , ptr_deref_format , index ) ;
if ( log )
log - > Printf ( " [ExpandIndexedExpression] name to deref: %s " , ptr_deref_buffer . c_str ( ) ) ;
const char * first_unparsed ;
ValueObject : : GetValueForExpressionPathOptions options ;
ValueObject : : ExpressionPathEndResultType final_value_type ;
ValueObject : : ExpressionPathScanEndReason reason_to_stop ;
ValueObject : : ExpressionPathAftermath what_next = ( deref_pointer ? ValueObject : : eExpressionPathAftermathDereference : ValueObject : : eExpressionPathAftermathNothing ) ;
ValueObjectSP item = valobj - > GetValueForExpressionPath ( ptr_deref_buffer . c_str ( ) ,
& first_unparsed ,
& reason_to_stop ,
& final_value_type ,
options ,
& what_next ) ;
if ( ! item )
{
if ( log )
log - > Printf ( " [ExpandIndexedExpression] ERROR: unparsed portion = %s, why stopping = %d, "
" final_value_type %d " ,
first_unparsed , reason_to_stop , final_value_type ) ;
}
else
{
if ( log )
log - > Printf ( " [ExpandIndexedExpression] ALL RIGHT: unparsed portion = %s, why stopping = %d, "
" final_value_type %d " ,
first_unparsed , reason_to_stop , final_value_type ) ;
}
return item ;
}
static char
ConvertValueObjectStyleToChar ( ValueObject : : ValueObjectRepresentationStyle style )
{
switch ( style )
{
case ValueObject : : eValueObjectRepresentationStyleLanguageSpecific : return ' @ ' ;
case ValueObject : : eValueObjectRepresentationStyleValue : return ' V ' ;
case ValueObject : : eValueObjectRepresentationStyleLocation : return ' L ' ;
case ValueObject : : eValueObjectRepresentationStyleSummary : return ' S ' ;
case ValueObject : : eValueObjectRepresentationStyleChildrenCount : return ' # ' ;
case ValueObject : : eValueObjectRepresentationStyleType : return ' T ' ;
case ValueObject : : eValueObjectRepresentationStyleName : return ' N ' ;
case ValueObject : : eValueObjectRepresentationStyleExpressionPath : return ' > ' ;
}
return ' \0 ' ;
}
static bool
DumpValue ( Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const FormatEntity : : Entry & entry ,
ValueObject * valobj )
{
if ( valobj = = NULL )
return false ;
Log * log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_TYPES ) ) ;
Format custom_format = eFormatInvalid ;
ValueObject : : ValueObjectRepresentationStyle val_obj_display = entry . string . empty ( ) ? ValueObject : : eValueObjectRepresentationStyleValue : ValueObject : : eValueObjectRepresentationStyleSummary ;
bool do_deref_pointer = entry . deref ;
bool is_script = false ;
switch ( entry . type )
{
case FormatEntity : : Entry : : Type : : ScriptVariable :
is_script = true ;
break ;
case FormatEntity : : Entry : : Type : : Variable :
custom_format = entry . fmt ;
val_obj_display = ( ValueObject : : ValueObjectRepresentationStyle ) entry . number ;
break ;
case FormatEntity : : Entry : : Type : : ScriptVariableSynthetic :
is_script = true ;
// Fall through
case FormatEntity : : Entry : : Type : : VariableSynthetic :
custom_format = entry . fmt ;
val_obj_display = ( ValueObject : : ValueObjectRepresentationStyle ) entry . number ;
if ( ! valobj - > IsSynthetic ( ) )
{
valobj = valobj - > GetSyntheticValue ( ) . get ( ) ;
if ( valobj = = nullptr )
return false ;
}
break ;
default :
return false ;
}
if ( valobj = = NULL )
return false ;
ValueObject : : ExpressionPathAftermath what_next = ( do_deref_pointer ?
ValueObject : : eExpressionPathAftermathDereference : ValueObject : : eExpressionPathAftermathNothing ) ;
ValueObject : : GetValueForExpressionPathOptions options ;
options . DontCheckDotVsArrowSyntax ( ) . DoAllowBitfieldSyntax ( ) . DoAllowFragileIVar ( ) . DoAllowSyntheticChildren ( ) ;
ValueObject * target = NULL ;
const char * var_name_final_if_array_range = NULL ;
size_t close_bracket_index = llvm : : StringRef : : npos ;
int64_t index_lower = - 1 ;
int64_t index_higher = - 1 ;
bool is_array_range = false ;
const char * first_unparsed ;
bool was_plain_var = false ;
bool was_var_format = false ;
bool was_var_indexed = false ;
ValueObject : : ExpressionPathScanEndReason reason_to_stop = ValueObject : : eExpressionPathScanEndReasonEndOfString ;
ValueObject : : ExpressionPathEndResultType final_value_type = ValueObject : : eExpressionPathEndResultTypePlain ;
if ( is_script )
{
return RunScriptFormatKeyword ( s , sc , exe_ctx , valobj , entry . string . c_str ( ) ) ;
}
llvm : : StringRef subpath ( entry . string ) ;
// simplest case ${var}, just print valobj's value
if ( entry . string . empty ( ) )
{
if ( entry . printf_format . empty ( ) & & entry . fmt = = eFormatDefault & & entry . number = = ValueObject : : eValueObjectRepresentationStyleValue )
was_plain_var = true ;
else
was_var_format = true ;
target = valobj ;
}
else // this is ${var.something} or multiple .something nested
{
if ( entry . string [ 0 ] = = ' [ ' )
was_var_indexed = true ;
ScanBracketedRange ( subpath ,
close_bracket_index ,
var_name_final_if_array_range ,
index_lower ,
index_higher ) ;
Error error ;
const std : : string & expr_path = entry . string ;
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] symbol to expand: %s " , expr_path . c_str ( ) ) ;
target = valobj - > GetValueForExpressionPath ( expr_path . c_str ( ) ,
& first_unparsed ,
& reason_to_stop ,
& final_value_type ,
options ,
& what_next ) . get ( ) ;
if ( ! target )
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] ERROR: unparsed portion = %s, why stopping = %d, "
" final_value_type %d " ,
first_unparsed , reason_to_stop , final_value_type ) ;
return false ;
}
else
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d, "
" final_value_type %d " ,
first_unparsed , reason_to_stop , final_value_type ) ;
target = target - > GetQualifiedRepresentationIfAvailable ( target - > GetDynamicValueType ( ) , true ) . get ( ) ;
}
}
is_array_range = ( final_value_type = = ValueObject : : eExpressionPathEndResultTypeBoundedRange | |
final_value_type = = ValueObject : : eExpressionPathEndResultTypeUnboundedRange ) ;
do_deref_pointer = ( what_next = = ValueObject : : eExpressionPathAftermathDereference ) ;
if ( do_deref_pointer & & ! is_array_range )
{
// I have not deref-ed yet, let's do it
// this happens when we are not going through GetValueForVariableExpressionPath
// to get to the target ValueObject
Error error ;
target = target - > Dereference ( error ) . get ( ) ;
if ( error . Fail ( ) )
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] ERROR: %s \n " , error . AsCString ( " unknown " ) ) ; \
return false ;
}
do_deref_pointer = false ;
}
if ( ! target )
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] could not calculate target for prompt expression " ) ;
return false ;
}
// we do not want to use the summary for a bitfield of type T:n
// if we were originally dealing with just a T - that would get
// us into an endless recursion
if ( target - > IsBitfield ( ) & & was_var_indexed )
{
// TODO: check for a (T:n)-specific summary - we should still obey that
StreamString bitfield_name ;
bitfield_name . Printf ( " %s:%d " , target - > GetTypeName ( ) . AsCString ( ) , target - > GetBitfieldBitSize ( ) ) ;
lldb : : TypeNameSpecifierImplSP type_sp ( new TypeNameSpecifierImpl ( bitfield_name . GetData ( ) , false ) ) ;
if ( val_obj_display = = ValueObject : : eValueObjectRepresentationStyleSummary & & ! DataVisualization : : GetSummaryForType ( type_sp ) )
val_obj_display = ValueObject : : eValueObjectRepresentationStyleValue ;
}
// TODO use flags for these
const uint32_t type_info_flags = target - > GetClangType ( ) . GetTypeInfo ( NULL ) ;
bool is_array = ( type_info_flags & eTypeIsArray ) ! = 0 ;
bool is_pointer = ( type_info_flags & eTypeIsPointer ) ! = 0 ;
bool is_aggregate = target - > GetClangType ( ) . IsAggregateType ( ) ;
if ( ( is_array | | is_pointer ) & & ( ! is_array_range ) & & val_obj_display = = ValueObject : : eValueObjectRepresentationStyleValue ) // this should be wrong, but there are some exceptions
{
StreamString str_temp ;
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] I am into array || pointer && !range " ) ;
if ( target - > HasSpecialPrintableRepresentation ( val_obj_display , custom_format ) )
{
// try to use the special cases
bool success = target - > DumpPrintableRepresentation ( str_temp ,
val_obj_display ,
custom_format ) ;
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] special cases did%s match " , success ? " " : " n't " ) ;
// should not happen
if ( success )
s < < str_temp . GetData ( ) ;
return true ;
}
else
{
if ( was_plain_var ) // if ${var}
{
s < < target - > GetTypeName ( ) < < " @ " < < target - > GetLocationAsCString ( ) ;
}
else if ( is_pointer ) // if pointer, value is the address stored
{
target - > DumpPrintableRepresentation ( s ,
val_obj_display ,
custom_format ,
ValueObject : : ePrintableRepresentationSpecialCasesDisable ) ;
}
return true ;
}
}
// if directly trying to print ${var}, and this is an aggregate, display a nice
// type @ location message
if ( is_aggregate & & was_plain_var )
{
s < < target - > GetTypeName ( ) < < " @ " < < target - > GetLocationAsCString ( ) ;
return true ;
}
// if directly trying to print ${var%V}, and this is an aggregate, do not let the user do it
if ( is_aggregate & & ( ( was_var_format & & val_obj_display = = ValueObject : : eValueObjectRepresentationStyleValue ) ) )
{
s < < " <invalid use of aggregate type> " ;
return true ;
}
if ( ! is_array_range )
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] dumping ordinary printable output " ) ;
return target - > DumpPrintableRepresentation ( s , val_obj_display , custom_format ) ;
}
else
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] checking if I can handle as array " ) ;
if ( ! is_array & & ! is_pointer )
return false ;
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] handle as array " ) ;
llvm : : StringRef special_directions ;
if ( close_bracket_index ! = llvm : : StringRef : : npos & & subpath . size ( ) > close_bracket_index )
{
ConstString additional_data ( subpath . drop_front ( close_bracket_index + 1 ) ) ;
StreamString special_directions_stream ;
special_directions_stream . Printf ( " ${%svar%s " ,
do_deref_pointer ? " * " : " " ,
additional_data . GetCString ( ) ) ;
if ( entry . fmt ! = eFormatDefault )
{
const char format_char = FormatManager : : GetFormatAsFormatChar ( entry . fmt ) ;
if ( format_char ! = ' \0 ' )
special_directions_stream . Printf ( " %%%c " , format_char ) ;
else
{
const char * format_cstr = FormatManager : : GetFormatAsCString ( entry . fmt ) ;
special_directions_stream . Printf ( " %%%s " , format_cstr ) ;
}
}
else if ( entry . number ! = 0 )
{
const char style_char = ConvertValueObjectStyleToChar ( ( ValueObject : : ValueObjectRepresentationStyle ) entry . number ) ;
if ( style_char )
special_directions_stream . Printf ( " %%%c " , style_char ) ;
}
special_directions_stream . PutChar ( ' } ' ) ;
special_directions = llvm : : StringRef ( special_directions_stream . GetString ( ) ) ;
}
// let us display items index_lower thru index_higher of this array
s . PutChar ( ' [ ' ) ;
if ( index_higher < 0 )
index_higher = valobj - > GetNumChildren ( ) - 1 ;
uint32_t max_num_children = target - > GetTargetSP ( ) - > GetMaximumNumberOfChildrenToDisplay ( ) ;
bool success = true ;
for ( int64_t index = index_lower ; index < = index_higher ; + + index )
{
ValueObject * item = ExpandIndexedExpression ( target ,
index ,
exe_ctx - > GetFramePtr ( ) ,
false ) . get ( ) ;
if ( ! item )
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] ERROR in getting child item at index % " PRId64 , index ) ;
}
else
{
if ( log )
log - > Printf ( " [Debugger::FormatPrompt] special_directions for child item: %s " , special_directions . data ( ) ? special_directions . data ( ) : " " ) ;
}
if ( special_directions . empty ( ) )
{
success & = item - > DumpPrintableRepresentation ( s , val_obj_display , custom_format ) ;
}
else
{
success & = FormatEntity : : FormatStringRef ( special_directions , s , sc , exe_ctx , NULL , item , false , false ) ;
}
if ( - - max_num_children = = 0 )
{
s . PutCString ( " , ... " ) ;
break ;
}
if ( index < index_higher )
s . PutChar ( ' , ' ) ;
}
s . PutChar ( ' ] ' ) ;
return success ;
}
}
static bool
DumpRegister ( Stream & s ,
StackFrame * frame ,
const char * reg_name ,
Format format )
{
if ( frame )
{
RegisterContext * reg_ctx = frame - > GetRegisterContext ( ) . get ( ) ;
if ( reg_ctx )
{
const RegisterInfo * reg_info = reg_ctx - > GetRegisterInfoByName ( reg_name ) ;
if ( reg_info )
{
RegisterValue reg_value ;
if ( reg_ctx - > ReadRegister ( reg_info , reg_value ) )
{
reg_value . Dump ( & s , reg_info , false , false , format ) ;
return true ;
}
}
}
}
return false ;
}
static bool
FormatThreadExtendedInfoRecurse ( const FormatEntity : : Entry & entry ,
const StructuredData : : ObjectSP & thread_info_dictionary ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
Stream & s )
{
llvm : : StringRef path ( entry . string ) ;
StructuredData : : ObjectSP value = thread_info_dictionary - > GetObjectForDotSeparatedPath ( path ) ;
if ( value . get ( ) )
{
if ( value - > GetType ( ) = = StructuredData : : Type : : eTypeInteger )
{
const char * token_format = " 0x%4.4 " PRIx64 ;
if ( ! entry . printf_format . empty ( ) )
token_format = entry . printf_format . c_str ( ) ;
s . Printf ( token_format , value - > GetAsInteger ( ) - > GetValue ( ) ) ;
return true ;
}
else if ( value - > GetType ( ) = = StructuredData : : Type : : eTypeFloat )
{
s . Printf ( " %f " , value - > GetAsFloat ( ) - > GetValue ( ) ) ;
return true ;
}
else if ( value - > GetType ( ) = = StructuredData : : Type : : eTypeString )
{
s . Printf ( " %s " , value - > GetAsString ( ) - > GetValue ( ) . c_str ( ) ) ;
return true ;
}
else if ( value - > GetType ( ) = = StructuredData : : Type : : eTypeArray )
{
if ( value - > GetAsArray ( ) - > GetSize ( ) > 0 )
{
s . Printf ( " %zu " , value - > GetAsArray ( ) - > GetSize ( ) ) ;
return true ;
}
}
else if ( value - > GetType ( ) = = StructuredData : : Type : : eTypeDictionary )
{
s . Printf ( " %zu " , value - > GetAsDictionary ( ) - > GetKeys ( ) - > GetAsArray ( ) - > GetSize ( ) ) ;
return true ;
}
}
return false ;
}
static inline bool
IsToken ( const char * var_name_begin , const char * var )
{
return ( : : strncmp ( var_name_begin , var , strlen ( var ) ) = = 0 ) ;
}
bool
FormatEntity : : FormatStringRef ( const llvm : : StringRef & format_str ,
Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const Address * addr ,
ValueObject * valobj ,
bool function_changed ,
bool initial_function )
{
if ( ! format_str . empty ( ) )
{
FormatEntity : : Entry root ;
Error error = FormatEntity : : Parse ( format_str , root ) ;
if ( error . Success ( ) )
{
return FormatEntity : : Format ( root ,
s ,
sc ,
exe_ctx ,
addr ,
valobj ,
function_changed ,
initial_function ) ;
}
}
return false ;
}
bool
FormatEntity : : FormatCString ( const char * format ,
Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const Address * addr ,
ValueObject * valobj ,
bool function_changed ,
bool initial_function )
{
if ( format & & format [ 0 ] )
{
FormatEntity : : Entry root ;
llvm : : StringRef format_str ( format ) ;
Error error = FormatEntity : : Parse ( format_str , root ) ;
if ( error . Success ( ) )
{
return FormatEntity : : Format ( root ,
s ,
sc ,
exe_ctx ,
addr ,
valobj ,
function_changed ,
initial_function ) ;
}
}
return false ;
}
bool
FormatEntity : : Format ( const Entry & entry ,
Stream & s ,
const SymbolContext * sc ,
const ExecutionContext * exe_ctx ,
const Address * addr ,
ValueObject * valobj ,
bool function_changed ,
bool initial_function )
{
switch ( entry . type )
{
case Entry : : Type : : Invalid :
case Entry : : Type : : ParentNumber : // Only used for FormatEntity::Entry::Definition encoding
case Entry : : Type : : ParentString : // Only used for FormatEntity::Entry::Definition encoding
case Entry : : Type : : InsertString : // Only used for FormatEntity::Entry::Definition encoding
return false ;
case Entry : : Type : : Root :
for ( const auto & child : entry . children )
{
if ( Format ( child ,
s ,
sc ,
exe_ctx ,
addr ,
valobj ,
function_changed ,
initial_function ) = = false )
{
return false ; // If any item of root fails, then the formatting fails
}
}
return true ; // Only return true if all items succeeded
case Entry : : Type : : String :
s . PutCString ( entry . string . c_str ( ) ) ;
return true ;
case Entry : : Type : : Scope :
{
StreamString scope_stream ;
bool success = false ;
for ( const auto & child : entry . children )
{
success = Format ( child , scope_stream , sc , exe_ctx , addr , valobj , function_changed , initial_function ) ;
if ( ! success )
break ;
}
// Only if all items in a scope succeed, then do we
// print the output into the main stream
if ( success )
s . Write ( scope_stream . GetString ( ) . data ( ) , scope_stream . GetString ( ) . size ( ) ) ;
}
return true ; // Scopes always successfully print themselves
case Entry : : Type : : Variable :
case Entry : : Type : : VariableSynthetic :
case Entry : : Type : : ScriptVariable :
case Entry : : Type : : ScriptVariableSynthetic :
if ( DumpValue ( s , sc , exe_ctx , entry , valobj ) )
return true ;
return false ;
case Entry : : Type : : AddressFile :
case Entry : : Type : : AddressLoad :
case Entry : : Type : : AddressLoadOrFile :
if ( addr & & addr - > IsValid ( ) & & DumpAddress ( s , sc , exe_ctx , * addr , entry . type = = Entry : : Type : : AddressLoadOrFile ) )
return true ;
return false ;
case Entry : : Type : : ProcessID :
if ( exe_ctx )
{
Process * process = exe_ctx - > GetProcessPtr ( ) ;
if ( process )
{
const char * format = " % " PRIu64 ;
if ( ! entry . printf_format . empty ( ) )
format = entry . printf_format . c_str ( ) ;
s . Printf ( format , process - > GetID ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : ProcessFile :
if ( exe_ctx )
{
Process * process = exe_ctx - > GetProcessPtr ( ) ;
if ( process )
{
Module * exe_module = process - > GetTarget ( ) . GetExecutableModulePointer ( ) ;
if ( exe_module )
{
if ( DumpFile ( s , exe_module - > GetFileSpec ( ) , ( FileKind ) entry . number ) )
return true ;
}
}
}
return false ;
case Entry : : Type : : ScriptProcess :
if ( exe_ctx )
{
Process * process = exe_ctx - > GetProcessPtr ( ) ;
if ( process )
return RunScriptFormatKeyword ( s , sc , exe_ctx , process , entry . string . c_str ( ) ) ;
}
return false ;
case Entry : : Type : : ThreadID :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
const char * format = " 0x%4.4 " PRIx64 ;
if ( ! entry . printf_format . empty ( ) )
{
// Watch for the special "tid" format...
if ( entry . printf_format = = " tid " )
{
bool handled = false ;
Target & target = thread - > GetProcess ( ) - > GetTarget ( ) ;
ArchSpec arch ( target . GetArchitecture ( ) ) ;
llvm : : Triple : : OSType ostype = arch . IsValid ( ) ? arch . GetTriple ( ) . getOS ( ) : llvm : : Triple : : UnknownOS ;
if ( ( ostype = = llvm : : Triple : : FreeBSD ) | | ( ostype = = llvm : : Triple : : Linux ) )
{
handled = true ;
format = " % " PRIu64 ;
}
}
else
{
format = entry . printf_format . c_str ( ) ;
}
}
s . Printf ( format , thread - > GetID ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : ThreadProtocolID :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
const char * format = " 0x%4.4 " PRIx64 ;
if ( ! entry . printf_format . empty ( ) )
format = entry . printf_format . c_str ( ) ;
s . Printf ( format , thread - > GetProtocolID ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : ThreadIndexID :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
const char * format = " % " PRIu32 ;
if ( ! entry . printf_format . empty ( ) )
format = entry . printf_format . c_str ( ) ;
s . Printf ( format , thread - > GetIndexID ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : ThreadName :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
const char * cstr = thread - > GetName ( ) ;
if ( cstr & & cstr [ 0 ] )
{
s . PutCString ( cstr ) ;
return true ;
}
}
}
return false ;
case Entry : : Type : : ThreadQueue :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
const char * cstr = thread - > GetQueueName ( ) ;
if ( cstr & & cstr [ 0 ] )
{
s . PutCString ( cstr ) ;
return true ;
}
}
}
return false ;
case Entry : : Type : : ThreadStopReason :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
StopInfoSP stop_info_sp = thread - > GetStopInfo ( ) ;
if ( stop_info_sp & & stop_info_sp - > IsValid ( ) )
{
const char * cstr = stop_info_sp - > GetDescription ( ) ;
if ( cstr & & cstr [ 0 ] )
{
s . PutCString ( cstr ) ;
return true ;
}
}
}
}
return false ;
case Entry : : Type : : ThreadReturnValue :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
StopInfoSP stop_info_sp = thread - > GetStopInfo ( ) ;
if ( stop_info_sp & & stop_info_sp - > IsValid ( ) )
{
ValueObjectSP return_valobj_sp = StopInfo : : GetReturnValueObject ( stop_info_sp ) ;
if ( return_valobj_sp )
{
return_valobj_sp - > Dump ( s ) ;
return true ;
}
}
}
}
return false ;
case Entry : : Type : : ThreadCompletedExpression :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
StopInfoSP stop_info_sp = thread - > GetStopInfo ( ) ;
if ( stop_info_sp & & stop_info_sp - > IsValid ( ) )
{
ClangExpressionVariableSP expression_var_sp = StopInfo : : GetExpressionVariable ( stop_info_sp ) ;
if ( expression_var_sp & & expression_var_sp - > GetValueObject ( ) )
{
expression_var_sp - > GetValueObject ( ) - > Dump ( s ) ;
return true ;
}
}
}
}
return false ;
case Entry : : Type : : ScriptThread :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
return RunScriptFormatKeyword ( s , sc , exe_ctx , thread , entry . string . c_str ( ) ) ;
}
return false ;
case Entry : : Type : : ThreadInfo :
if ( exe_ctx )
{
Thread * thread = exe_ctx - > GetThreadPtr ( ) ;
if ( thread )
{
StructuredData : : ObjectSP object_sp = thread - > GetExtendedInfo ( ) ;
if ( object_sp & & object_sp - > GetType ( ) = = StructuredData : : Type : : eTypeDictionary )
{
if ( FormatThreadExtendedInfoRecurse ( entry , object_sp , sc , exe_ctx , s ) )
return true ;
}
}
}
return false ;
case Entry : : Type : : TargetArch :
if ( exe_ctx )
{
Target * target = exe_ctx - > GetTargetPtr ( ) ;
if ( target )
{
const ArchSpec & arch = target - > GetArchitecture ( ) ;
if ( arch . IsValid ( ) )
{
s . PutCString ( arch . GetArchitectureName ( ) ) ;
return true ;
}
}
}
return false ;
case Entry : : Type : : ScriptTarget :
if ( exe_ctx )
{
Target * target = exe_ctx - > GetTargetPtr ( ) ;
if ( target )
return RunScriptFormatKeyword ( s , sc , exe_ctx , target , entry . string . c_str ( ) ) ;
}
return false ;
case Entry : : Type : : ModuleFile :
if ( sc )
{
Module * module = sc - > module_sp . get ( ) ;
if ( module )
{
if ( DumpFile ( s , module - > GetFileSpec ( ) , ( FileKind ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : File :
if ( sc )
{
CompileUnit * cu = sc - > comp_unit ;
if ( cu )
{
// CompileUnit is a FileSpec
if ( DumpFile ( s , * cu , ( FileKind ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : FrameIndex :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
const char * format = " % " PRIu32 ;
if ( ! entry . printf_format . empty ( ) )
format = entry . printf_format . c_str ( ) ;
s . Printf ( format , frame - > GetFrameIndex ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : FrameRegisterPC :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
const Address & pc_addr = frame - > GetFrameCodeAddress ( ) ;
if ( pc_addr . IsValid ( ) )
{
if ( DumpAddress ( s , sc , exe_ctx , pc_addr , false ) )
return true ;
}
}
}
return false ;
case Entry : : Type : : FrameRegisterSP :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
if ( DumpRegister ( s , frame , eRegisterKindGeneric , LLDB_REGNUM_GENERIC_SP , ( lldb : : Format ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : FrameRegisterFP :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
if ( DumpRegister ( s , frame , eRegisterKindGeneric , LLDB_REGNUM_GENERIC_FP , ( lldb : : Format ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : FrameRegisterFlags :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
if ( DumpRegister ( s , frame , eRegisterKindGeneric , LLDB_REGNUM_GENERIC_FLAGS , ( lldb : : Format ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : FrameRegisterByName :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
if ( DumpRegister ( s , frame , entry . string . c_str ( ) , ( lldb : : Format ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : ScriptFrame :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
return RunScriptFormatKeyword ( s , sc , exe_ctx , frame , entry . string . c_str ( ) ) ;
}
return false ;
case Entry : : Type : : FunctionID :
if ( sc )
{
if ( sc - > function )
{
s . Printf ( " function{0x%8.8 " PRIx64 " } " , sc - > function - > GetID ( ) ) ;
return true ;
}
else if ( sc - > symbol )
{
s . Printf ( " symbol[%u] " , sc - > symbol - > GetID ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : FunctionDidChange :
return function_changed ;
case Entry : : Type : : FunctionInitialFunction :
return initial_function ;
case Entry : : Type : : FunctionName :
{
const char * name = NULL ;
if ( sc - > function )
name = sc - > function - > GetName ( ) . AsCString ( NULL ) ;
else if ( sc - > symbol )
name = sc - > symbol - > GetName ( ) . AsCString ( NULL ) ;
if ( name )
{
s . PutCString ( name ) ;
if ( sc - > block )
{
Block * inline_block = sc - > block - > GetContainingInlinedBlock ( ) ;
if ( inline_block )
{
const InlineFunctionInfo * inline_info = sc - > block - > GetInlinedFunctionInfo ( ) ;
if ( inline_info )
{
s . PutCString ( " [inlined] " ) ;
inline_info - > GetName ( ) . Dump ( & s ) ;
}
}
}
return true ;
}
}
return false ;
case Entry : : Type : : FunctionNameNoArgs :
{
ConstString name ;
if ( sc - > function )
name = sc - > function - > GetMangled ( ) . GetName ( Mangled : : ePreferDemangledWithoutArguments ) ;
else if ( sc - > symbol )
name = sc - > symbol - > GetMangled ( ) . GetName ( Mangled : : ePreferDemangledWithoutArguments ) ;
if ( name )
{
s . PutCString ( name . GetCString ( ) ) ;
return true ;
}
}
return false ;
case Entry : : Type : : FunctionNameWithArgs :
{
// Print the function name with arguments in it
if ( sc - > function )
{
ExecutionContextScope * exe_scope = exe_ctx ? exe_ctx - > GetBestExecutionContextScope ( ) : NULL ;
const char * cstr = sc - > function - > GetName ( ) . AsCString ( NULL ) ;
if ( cstr )
{
const InlineFunctionInfo * inline_info = NULL ;
VariableListSP variable_list_sp ;
bool get_function_vars = true ;
if ( sc - > block )
{
Block * inline_block = sc - > block - > GetContainingInlinedBlock ( ) ;
if ( inline_block )
{
get_function_vars = false ;
inline_info = sc - > block - > GetInlinedFunctionInfo ( ) ;
if ( inline_info )
variable_list_sp = inline_block - > GetBlockVariableList ( true ) ;
}
}
if ( get_function_vars )
{
variable_list_sp = sc - > function - > GetBlock ( true ) . GetBlockVariableList ( true ) ;
}
if ( inline_info )
{
s . PutCString ( cstr ) ;
s . PutCString ( " [inlined] " ) ;
cstr = inline_info - > GetName ( ) . GetCString ( ) ;
}
VariableList args ;
if ( variable_list_sp )
variable_list_sp - > AppendVariablesWithScope ( eValueTypeVariableArgument , args ) ;
if ( args . GetSize ( ) > 0 )
{
const char * open_paren = strchr ( cstr , ' ( ' ) ;
const char * close_paren = nullptr ;
const char * generic = strchr ( cstr , ' < ' ) ;
// if before the arguments list begins there is a template sign
// then scan to the end of the generic args before you try to find
// the arguments list
if ( generic & & open_paren & & generic < open_paren )
{
int generic_depth = 1 ;
+ + generic ;
for ( ;
* generic & & generic_depth > 0 ;
generic + + )
{
if ( * generic = = ' < ' )
generic_depth + + ;
if ( * generic = = ' > ' )
generic_depth - - ;
}
if ( * generic )
open_paren = strchr ( generic , ' ( ' ) ;
else
open_paren = nullptr ;
}
if ( open_paren )
{
if ( IsToken ( open_paren , " (anonymous namespace) " ) )
{
open_paren = strchr ( open_paren + strlen ( " (anonymous namespace) " ) , ' ( ' ) ;
if ( open_paren )
close_paren = strchr ( open_paren , ' ) ' ) ;
}
else
close_paren = strchr ( open_paren , ' ) ' ) ;
}
if ( open_paren )
s . Write ( cstr , open_paren - cstr + 1 ) ;
else
{
s . PutCString ( cstr ) ;
s . PutChar ( ' ( ' ) ;
}
const size_t num_args = args . GetSize ( ) ;
for ( size_t arg_idx = 0 ; arg_idx < num_args ; + + arg_idx )
{
std : : string buffer ;
VariableSP var_sp ( args . GetVariableAtIndex ( arg_idx ) ) ;
ValueObjectSP var_value_sp ( ValueObjectVariable : : Create ( exe_scope , var_sp ) ) ;
const char * var_representation = nullptr ;
const char * var_name = var_value_sp - > GetName ( ) . GetCString ( ) ;
if ( var_value_sp - > GetClangType ( ) . IsAggregateType ( ) & &
DataVisualization : : ShouldPrintAsOneLiner ( * var_value_sp . get ( ) ) )
{
static StringSummaryFormat format ( TypeSummaryImpl : : Flags ( )
. SetHideItemNames ( false )
. SetShowMembersOneLiner ( true ) ,
" " ) ;
format . FormatObject ( var_value_sp . get ( ) , buffer , TypeSummaryOptions ( ) ) ;
var_representation = buffer . c_str ( ) ;
}
else
var_representation = var_value_sp - > GetValueAsCString ( ) ;
if ( arg_idx > 0 )
s . PutCString ( " , " ) ;
if ( var_value_sp - > GetError ( ) . Success ( ) )
{
if ( var_representation )
s . Printf ( " %s=%s " , var_name , var_representation ) ;
else
s . Printf ( " %s=%s at %s " , var_name , var_value_sp - > GetTypeName ( ) . GetCString ( ) , var_value_sp - > GetLocationAsCString ( ) ) ;
}
else
s . Printf ( " %s=<unavailable> " , var_name ) ;
}
if ( close_paren )
s . PutCString ( close_paren ) ;
else
s . PutChar ( ' ) ' ) ;
}
else
{
s . PutCString ( cstr ) ;
}
return true ;
}
}
else if ( sc - > symbol )
{
const char * cstr = sc - > symbol - > GetName ( ) . AsCString ( NULL ) ;
if ( cstr )
{
s . PutCString ( cstr ) ;
return true ;
}
}
}
return false ;
case Entry : : Type : : FunctionAddrOffset :
if ( addr )
{
if ( DumpAddressOffsetFromFunction ( s , sc , exe_ctx , * addr , false , false ) )
return true ;
}
return false ;
case Entry : : Type : : FunctionAddrOffsetConcrete :
if ( addr )
{
if ( DumpAddressOffsetFromFunction ( s , sc , exe_ctx , * addr , true , true ) )
return true ;
}
return false ;
case Entry : : Type : : FunctionLineOffset :
if ( DumpAddressOffsetFromFunction ( s , sc , exe_ctx , sc - > line_entry . range . GetBaseAddress ( ) , false , false ) )
return true ;
return false ;
case Entry : : Type : : FunctionPCOffset :
if ( exe_ctx )
{
StackFrame * frame = exe_ctx - > GetFramePtr ( ) ;
if ( frame )
{
if ( DumpAddressOffsetFromFunction ( s , sc , exe_ctx , frame - > GetFrameCodeAddress ( ) , false , false ) )
return true ;
}
}
return false ;
case Entry : : Type : : LineEntryFile :
if ( sc & & sc - > line_entry . IsValid ( ) )
{
Module * module = sc - > module_sp . get ( ) ;
if ( module )
{
if ( DumpFile ( s , sc - > line_entry . file , ( FileKind ) entry . number ) )
return true ;
}
}
return false ;
case Entry : : Type : : LineEntryLineNumber :
if ( sc & & sc - > line_entry . IsValid ( ) )
{
const char * format = " % " PRIu32 ;
if ( ! entry . printf_format . empty ( ) )
format = entry . printf_format . c_str ( ) ;
s . Printf ( format , sc - > line_entry . line ) ;
return true ;
}
return false ;
case Entry : : Type : : LineEntryStartAddress :
case Entry : : Type : : LineEntryEndAddress :
if ( sc & & sc - > line_entry . range . GetBaseAddress ( ) . IsValid ( ) )
{
Address addr = sc - > line_entry . range . GetBaseAddress ( ) ;
if ( entry . type = = Entry : : Type : : LineEntryEndAddress )
addr . Slide ( sc - > line_entry . range . GetByteSize ( ) ) ;
if ( DumpAddress ( s , sc , exe_ctx , addr , false ) )
return true ;
}
return false ;
case Entry : : Type : : CurrentPCArrow :
if ( addr & & exe_ctx & & exe_ctx - > GetFramePtr ( ) )
{
RegisterContextSP reg_ctx = exe_ctx - > GetFramePtr ( ) - > GetRegisterContextSP ( ) ;
if ( reg_ctx . get ( ) )
{
addr_t pc_loadaddr = reg_ctx - > GetPC ( ) ;
if ( pc_loadaddr ! = LLDB_INVALID_ADDRESS )
{
Address pc ;
pc . SetLoadAddress ( pc_loadaddr , exe_ctx - > GetTargetPtr ( ) ) ;
if ( pc = = * addr )
{
s . Printf ( " -> " ) ;
return true ;
}
}
}
s . Printf ( " " ) ;
return true ;
}
return false ;
}
return false ;
}
static bool
DumpCommaSeparatedChildEntryNames ( Stream & s , const FormatEntity : : Entry : : Definition * parent )
{
if ( parent - > children )
{
const size_t n = parent - > num_children ;
for ( size_t i = 0 ; i < n ; + + i )
{
if ( i > 0 )
s . PutCString ( " , " ) ;
s . Printf ( " \" %s \" " , parent - > children [ i ] . name ) ;
}
return true ;
}
return false ;
}
static Error
ParseEntry ( const llvm : : StringRef & format_str ,
const FormatEntity : : Entry : : Definition * parent ,
FormatEntity : : Entry & entry )
{
Error error ;
const size_t sep_pos = format_str . find_first_of ( " .[: " ) ;
const char sep_char = ( sep_pos = = llvm : : StringRef : : npos ) ? ' \0 ' : format_str [ sep_pos ] ;
llvm : : StringRef key = format_str . substr ( 0 , sep_pos ) ;
const size_t n = parent - > num_children ;
for ( size_t i = 0 ; i < n ; + + i )
{
const FormatEntity : : Entry : : Definition * entry_def = parent - > children + i ;
if ( key . equals ( entry_def - > name ) | | entry_def - > name [ 0 ] = = ' * ' )
{
llvm : : StringRef value ;
if ( sep_char )
value = format_str . substr ( sep_pos + ( entry_def - > keep_separator ? 0 : 1 ) ) ;
switch ( entry_def - > type )
{
case FormatEntity : : Entry : : Type : : ParentString :
entry . string = std : : move ( format_str . str ( ) ) ;
return error ; // Success
case FormatEntity : : Entry : : Type : : ParentNumber :
entry . number = entry_def - > data ;
return error ; // Success
case FormatEntity : : Entry : : Type : : InsertString :
entry . type = entry_def - > type ;
entry . string = entry_def - > string ;
return error ; // Success
default :
entry . type = entry_def - > type ;
break ;
}
if ( value . empty ( ) )
{
if ( entry_def - > type = = FormatEntity : : Entry : : Type : : Invalid )
{
if ( entry_def - > children )
{
StreamString error_strm ;
error_strm . Printf ( " '%s' can't be specified on its own, you must access one of its children: " , entry_def - > name ) ;
DumpCommaSeparatedChildEntryNames ( error_strm , entry_def ) ;
error . SetErrorStringWithFormat ( " %s " , error_strm . GetString ( ) . c_str ( ) ) ;
}
else if ( sep_char = = ' : ' )
{
// Any value whose separator is a with a ':' means this value has a string argument
// that needs to be stored in the entry (like "${script.var:}").
// In this case the string value is the empty string which is ok.
}
else
{
error . SetErrorStringWithFormat ( " %s " , " invalid entry definitions " ) ;
}
}
}
else
{
if ( entry_def - > children )
{
error = ParseEntry ( value , entry_def , entry ) ;
}
else if ( sep_char = = ' : ' )
{
// Any value whose separator is a with a ':' means this value has a string argument
// that needs to be stored in the entry (like "${script.var:modulename.function}")
entry . string = std : : move ( value . str ( ) ) ;
}
else
{
error . SetErrorStringWithFormat ( " '%s' followed by '%s' but it has no children " ,
key . str ( ) . c_str ( ) ,
value . str ( ) . c_str ( ) ) ;
}
}
return error ;
}
}
StreamString error_strm ;
if ( parent - > type = = FormatEntity : : Entry : : Type : : Root )
error_strm . Printf ( " invalid top level item '%s'. Valid top level items are: " , key . str ( ) . c_str ( ) ) ;
else
error_strm . Printf ( " invalid member '%s' in '%s'. Valid members are: " , key . str ( ) . c_str ( ) , parent - > name ) ;
DumpCommaSeparatedChildEntryNames ( error_strm , parent ) ;
error . SetErrorStringWithFormat ( " %s " , error_strm . GetString ( ) . c_str ( ) ) ;
return error ;
}
static const FormatEntity : : Entry : : Definition *
FindEntry ( const llvm : : StringRef & format_str , const FormatEntity : : Entry : : Definition * parent , llvm : : StringRef & remainder )
{
Error error ;
std : : pair < llvm : : StringRef , llvm : : StringRef > p = format_str . split ( ' . ' ) ;
const size_t n = parent - > num_children ;
for ( size_t i = 0 ; i < n ; + + i )
{
const FormatEntity : : Entry : : Definition * entry_def = parent - > children + i ;
if ( p . first . equals ( entry_def - > name ) | | entry_def - > name [ 0 ] = = ' * ' )
{
if ( p . second . empty ( ) )
{
if ( format_str . back ( ) = = ' . ' )
remainder = format_str . drop_front ( format_str . size ( ) - 1 ) ;
else
remainder = llvm : : StringRef ( ) ; // Exact match
return entry_def ;
}
else
{
if ( entry_def - > children )
{
return FindEntry ( p . second , entry_def , remainder ) ;
}
else
{
remainder = p . second ;
return entry_def ;
}
}
}
}
remainder = format_str ;
return parent ;
}
Error
FormatEntity : : ParseInternal ( llvm : : StringRef & format , Entry & parent_entry , uint32_t depth )
{
Error error ;
while ( ! format . empty ( ) & & error . Success ( ) )
{
const size_t non_special_chars = format . find_first_of ( " ${} \\ " ) ;
if ( non_special_chars = = llvm : : StringRef : : npos )
{
// No special characters, just string bytes so add them and we are done
parent_entry . AppendText ( format ) ;
return error ;
}
if ( non_special_chars > 0 )
{
// We have a special character, so add all characters before these as a plain string
parent_entry . AppendText ( format . substr ( 0 , non_special_chars ) ) ;
format = format . drop_front ( non_special_chars ) ;
}
switch ( format [ 0 ] )
{
case ' \0 ' :
return error ;
case ' { ' :
{
format = format . drop_front ( ) ; // Skip the '{'
Entry scope_entry ( Entry : : Type : : Scope ) ;
error = FormatEntity : : ParseInternal ( format , scope_entry , depth + 1 ) ;
if ( error . Fail ( ) )
return error ;
parent_entry . AppendEntry ( std : : move ( scope_entry ) ) ;
}
break ;
case ' } ' :
if ( depth = = 0 )
error . SetErrorString ( " unmatched '}' character " ) ;
else
format = format . drop_front ( ) ; // Skip the '}' as we are at the end of the scope
return error ;
case ' \\ ' :
{
format = format . drop_front ( ) ; // Skip the '\' character
if ( format . empty ( ) )
{
error . SetErrorString ( " ' \\ ' character was not followed by another character " ) ;
return error ;
}
const char desens_char = format [ 0 ] ;
format = format . drop_front ( ) ; // Skip the desensitized char character
switch ( desens_char )
{
case ' a ' : parent_entry . AppendChar ( ' \a ' ) ; break ;
case ' b ' : parent_entry . AppendChar ( ' \b ' ) ; break ;
case ' f ' : parent_entry . AppendChar ( ' \f ' ) ; break ;
case ' n ' : parent_entry . AppendChar ( ' \n ' ) ; break ;
case ' r ' : parent_entry . AppendChar ( ' \r ' ) ; break ;
case ' t ' : parent_entry . AppendChar ( ' \t ' ) ; break ;
case ' v ' : parent_entry . AppendChar ( ' \v ' ) ; break ;
case ' \' ' : parent_entry . AppendChar ( ' \' ' ) ; break ;
case ' \\ ' : parent_entry . AppendChar ( ' \\ ' ) ; break ;
case ' 0 ' :
// 1 to 3 octal chars
{
// Make a string that can hold onto the initial zero char,
// up to 3 octal digits, and a terminating NULL.
char oct_str [ 5 ] = { 0 , 0 , 0 , 0 , 0 } ;
int i ;
for ( i = 0 ; ( format [ i ] > = ' 0 ' & & format [ i ] < = ' 7 ' ) & & i < 4 ; + + i )
oct_str [ i ] = format [ i ] ;
// We don't want to consume the last octal character since
// the main for loop will do this for us, so we advance p by
// one less than i (even if i is zero)
format = format . drop_front ( i ) ;
unsigned long octal_value = : : strtoul ( oct_str , NULL , 8 ) ;
if ( octal_value < = UINT8_MAX )
{
parent_entry . AppendChar ( ( char ) octal_value ) ;
}
else
{
error . SetErrorString ( " octal number is larger than a single byte " ) ;
return error ;
}
}
break ;
case ' x ' :
// hex number in the format
if ( isxdigit ( format [ 0 ] ) )
{
// Make a string that can hold onto two hex chars plus a
// NULL terminator
char hex_str [ 3 ] = { 0 , 0 , 0 } ;
hex_str [ 0 ] = format [ 0 ] ;
format = format . drop_front ( ) ;
if ( isxdigit ( format [ 0 ] ) )
{
hex_str [ 1 ] = format [ 0 ] ;
format = format . drop_front ( ) ;
}
unsigned long hex_value = strtoul ( hex_str , NULL , 16 ) ;
if ( hex_value < = UINT8_MAX )
{
parent_entry . AppendChar ( ( char ) hex_value ) ;
}
else
{
error . SetErrorString ( " hex number is larger than a single byte " ) ;
return error ;
}
}
else
{
parent_entry . AppendChar ( desens_char ) ;
}
break ;
default :
// Just desensitize any other character by just printing what
// came after the '\'
parent_entry . AppendChar ( desens_char ) ;
break ;
}
}
break ;
case ' $ ' :
if ( format . size ( ) = = 1 )
{
// '$' at the end of a format string, just print the '$'
parent_entry . AppendText ( " $ " ) ;
}
else
{
format = format . drop_front ( ) ; // Skip the '$'
if ( format [ 0 ] = = ' { ' )
{
format = format . drop_front ( ) ; // Skip the '{'
llvm : : StringRef variable , variable_format ;
error = FormatEntity : : ExtractVariableInfo ( format , variable , variable_format ) ;
if ( error . Fail ( ) )
return error ;
bool verify_is_thread_id = false ;
Entry entry ;
if ( ! variable_format . empty ( ) )
{
entry . printf_format = std : : move ( variable_format . str ( ) ) ;
// If the format contains a '%' we are going to assume this is
// a printf style format. So if you want to format your thread ID
// using "0x%llx" you can use:
// ${thread.id%0x%llx}
//
// If there is no '%' in the format, then it is assumed to be a
// LLDB format name, or one of the extended formats specified in
// the switch statement below.
if ( entry . printf_format . find ( ' % ' ) = = std : : string : : npos )
{
bool clear_printf = false ;
if ( FormatManager : : GetFormatFromCString ( entry . printf_format . c_str ( ) ,
false ,
entry . fmt ) )
{
// We have an LLDB format, so clear the printf format
clear_printf = true ;
}
else if ( entry . printf_format . size ( ) = = 1 )
{
switch ( entry . printf_format [ 0 ] )
{
case ' @ ' : // if this is an @ sign, print ObjC description
entry . number = ValueObject : : eValueObjectRepresentationStyleLanguageSpecific ;
clear_printf = true ;
break ;
case ' V ' : // if this is a V, print the value using the default format
entry . number = ValueObject : : eValueObjectRepresentationStyleValue ;
clear_printf = true ;
break ;
case ' L ' : // if this is an L, print the location of the value
entry . number = ValueObject : : eValueObjectRepresentationStyleLocation ;
clear_printf = true ;
break ;
case ' S ' : // if this is an S, print the summary after all
entry . number = ValueObject : : eValueObjectRepresentationStyleSummary ;
clear_printf = true ;
break ;
case ' # ' : // if this is a '#', print the number of children
entry . number = ValueObject : : eValueObjectRepresentationStyleChildrenCount ;
clear_printf = true ;
break ;
case ' T ' : // if this is a 'T', print the type
entry . number = ValueObject : : eValueObjectRepresentationStyleType ;
clear_printf = true ;
break ;
case ' N ' : // if this is a 'N', print the name
entry . number = ValueObject : : eValueObjectRepresentationStyleName ;
clear_printf = true ;
break ;
2015-02-05 06:24:47 +08:00
case ' > ' : // if this is a '>', print the expression path
2015-02-05 06:00:53 +08:00
entry . number = ValueObject : : eValueObjectRepresentationStyleExpressionPath ;
clear_printf = true ;
break ;
default :
error . SetErrorStringWithFormat ( " invalid format: '%s' " , entry . printf_format . c_str ( ) ) ;
return error ;
}
}
else if ( FormatManager : : GetFormatFromCString ( entry . printf_format . c_str ( ) ,
true ,
entry . fmt ) )
{
clear_printf = true ;
}
else if ( entry . printf_format = = " tid " )
{
verify_is_thread_id = true ;
}
else
{
error . SetErrorStringWithFormat ( " invalid format: '%s' " , entry . printf_format . c_str ( ) ) ;
return error ;
}
// Our format string turned out to not be a printf style format
// so lets clear the string
if ( clear_printf )
entry . printf_format . clear ( ) ;
}
}
// Check for dereferences
if ( variable [ 0 ] = = ' * ' )
{
entry . deref = true ;
variable = variable . drop_front ( ) ;
}
error = ParseEntry ( variable , & g_root , entry ) ;
if ( error . Fail ( ) )
return error ;
if ( verify_is_thread_id )
{
if ( entry . type ! = Entry : : Type : : ThreadID & &
entry . type ! = Entry : : Type : : ThreadProtocolID )
{
error . SetErrorString ( " the 'tid' format can only be used on ${thread.id} and ${thread.protocol_id} " ) ;
}
}
switch ( entry . type )
{
case Entry : : Type : : Variable :
case Entry : : Type : : VariableSynthetic :
if ( entry . number = = 0 )
{
if ( entry . string . empty ( ) )
entry . number = ValueObject : : eValueObjectRepresentationStyleValue ;
else
entry . number = ValueObject : : eValueObjectRepresentationStyleSummary ;
}
break ;
default :
// Make sure someone didn't try to dereference anything but ${var} or ${svar}
if ( entry . deref )
{
error . SetErrorStringWithFormat ( " ${%s} can't be dereferenced, only ${var} and ${svar} can. " , variable . str ( ) . c_str ( ) ) ;
return error ;
}
}
// Check if this entry just wants to insert a constant string
// value into the parent_entry, if so, insert the string with
// AppendText, else append the entry to the parent_entry.
if ( entry . type = = Entry : : Type : : InsertString )
parent_entry . AppendText ( entry . string . c_str ( ) ) ;
else
parent_entry . AppendEntry ( std : : move ( entry ) ) ;
}
}
break ;
}
}
return error ;
}
Error
FormatEntity : : ExtractVariableInfo ( llvm : : StringRef & format_str , llvm : : StringRef & variable_name , llvm : : StringRef & variable_format )
{
Error error ;
variable_name = llvm : : StringRef ( ) ;
variable_format = llvm : : StringRef ( ) ;
const size_t paren_pos = format_str . find_first_of ( ' } ' ) ;
if ( paren_pos ! = llvm : : StringRef : : npos )
{
const size_t percent_pos = format_str . find_first_of ( ' % ' ) ;
if ( percent_pos < paren_pos )
{
if ( percent_pos > 0 )
{
if ( percent_pos > 1 )
variable_name = format_str . substr ( 0 , percent_pos ) ;
variable_format = format_str . substr ( percent_pos + 1 , paren_pos - ( percent_pos + 1 ) ) ;
}
}
else
{
variable_name = format_str . substr ( 0 , paren_pos ) ;
}
// Strip off elements and the formatting and the trailing '}'
format_str = format_str . substr ( paren_pos + 1 ) ;
}
else
{
error . SetErrorStringWithFormat ( " missing terminating '}' character for '${%s' " , format_str . str ( ) . c_str ( ) ) ;
}
return error ;
}
bool
FormatEntity : : FormatFileSpec ( const FileSpec & file_spec , Stream & s , llvm : : StringRef variable_name , llvm : : StringRef variable_format )
{
if ( variable_name . empty ( ) | | variable_name . equals ( " .fullpath " ) )
{
file_spec . Dump ( & s ) ;
return true ;
}
else if ( variable_name . equals ( " .basename " ) )
{
s . PutCString ( file_spec . GetFilename ( ) . AsCString ( " " ) ) ;
return true ;
}
else if ( variable_name . equals ( " .dirname " ) )
{
s . PutCString ( file_spec . GetFilename ( ) . AsCString ( " " ) ) ;
return true ;
}
return false ;
}
static std : : string
MakeMatch ( const llvm : : StringRef & prefix , const char * suffix )
{
std : : string match ( prefix . str ( ) ) ;
match . append ( suffix ) ;
return std : : move ( match ) ;
}
static void
AddMatches ( const FormatEntity : : Entry : : Definition * def ,
const llvm : : StringRef & prefix ,
const llvm : : StringRef & match_prefix ,
StringList & matches )
{
const size_t n = def - > num_children ;
if ( n > 0 )
{
for ( size_t i = 0 ; i < n ; + + i )
{
std : : string match = std : : move ( prefix . str ( ) ) ;
if ( match_prefix . empty ( ) )
matches . AppendString ( MakeMatch ( prefix , def - > children [ i ] . name ) ) ;
else if ( strncmp ( def - > children [ i ] . name , match_prefix . data ( ) , match_prefix . size ( ) ) = = 0 )
matches . AppendString ( MakeMatch ( prefix , def - > children [ i ] . name + match_prefix . size ( ) ) ) ;
}
}
}
size_t
FormatEntity : : AutoComplete ( const char * s ,
int match_start_point ,
int max_return_elements ,
bool & word_complete ,
StringList & matches )
{
word_complete = false ;
llvm : : StringRef str ( s + match_start_point ) ;
matches . Clear ( ) ;
const size_t dollar_pos = str . rfind ( ' $ ' ) ;
if ( dollar_pos ! = llvm : : StringRef : : npos )
{
// Hitting TAB after $ at the end of the string add a "{"
if ( dollar_pos = = str . size ( ) - 1 )
{
std : : string match = std : : move ( str . str ( ) ) ;
match . append ( " { " ) ;
matches . AppendString ( std : : move ( match ) ) ;
}
else if ( str [ dollar_pos + 1 ] = = ' { ' )
{
const size_t close_pos = str . find ( ' } ' , dollar_pos + 2 ) ;
if ( close_pos = = llvm : : StringRef : : npos )
{
const size_t format_pos = str . find ( ' % ' , dollar_pos + 2 ) ;
if ( format_pos = = llvm : : StringRef : : npos )
{
llvm : : StringRef partial_variable ( str . substr ( dollar_pos + 2 ) ) ;
if ( partial_variable . empty ( ) )
{
// Suggest all top level entites as we are just past "${"
AddMatches ( & g_root , str , llvm : : StringRef ( ) , matches ) ;
}
else
{
// We have a partially specified variable, find it
llvm : : StringRef remainder ;
const FormatEntity : : Entry : : Definition * entry_def = FindEntry ( partial_variable , & g_root , remainder ) ;
if ( entry_def )
{
const size_t n = entry_def - > num_children ;
if ( remainder . empty ( ) )
{
// Exact match
if ( n > 0 )
{
// "${thread.info" <TAB>
matches . AppendString ( std : : move ( MakeMatch ( str , " . " ) ) ) ;
}
else
{
// "${thread.id" <TAB>
matches . AppendString ( std : : move ( MakeMatch ( str , " } " ) ) ) ;
word_complete = true ;
}
}
else if ( remainder . equals ( " . " ) )
{
// "${thread." <TAB>
AddMatches ( entry_def , str , llvm : : StringRef ( ) , matches ) ;
}
else
{
// We have a partial match
// "${thre" <TAB>
AddMatches ( entry_def , str , remainder , matches ) ;
}
}
}
}
}
}
}
return matches . GetSize ( ) ;
}