diff options
author | emaste <emaste@FreeBSD.org> | 2015-02-09 01:44:09 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2015-02-09 01:44:09 +0000 |
commit | d61b076ede88b56f3372a55e7d1eac6a9d717120 (patch) | |
tree | a8f4b3abea3e6937e60728991c736e6e3d322fc1 /source/Core | |
parent | 0c2019f4ca6b2dc6d710f6bb16a0e3ed10271531 (diff) | |
download | FreeBSD-src-d61b076ede88b56f3372a55e7d1eac6a9d717120.zip FreeBSD-src-d61b076ede88b56f3372a55e7d1eac6a9d717120.tar.gz |
Import LLDB as of upstream SVN 228549 (git 39760838)
Diffstat (limited to 'source/Core')
25 files changed, 2804 insertions, 1815 deletions
diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index a79becb..d449d0b 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -654,7 +654,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (module_sp) { SymbolContext sc; - module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything | eSymbolContextVariable, sc); if (sc.function || sc.symbol) { bool show_stop_context = true; @@ -712,7 +712,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (module_sp) { SymbolContext sc; - module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextEverything | eSymbolContextVariable, sc); if (sc.symbol) { // If we have just a symbol make sure it is in the same section diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index e7a5e48..015f76b 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -765,6 +765,19 @@ ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) return IsValid(); } +void +ArchSpec::MergeFrom(const ArchSpec &other) +{ + if (GetTriple().getVendor() == llvm::Triple::UnknownVendor && !TripleVendorWasSpecified()) + GetTriple().setVendor(other.GetTriple().getVendor()); + if (GetTriple().getOS() == llvm::Triple::UnknownOS && !TripleOSWasSpecified()) + GetTriple().setOS(other.GetTriple().getOS()); + if (GetTriple().getArch() == llvm::Triple::UnknownArch) + GetTriple().setArch(other.GetTriple().getArch()); + if (GetTriple().getEnvironment() == llvm::Triple::UnknownEnvironment) + GetTriple().setEnvironment(other.GetTriple().getEnvironment()); +} + bool ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) { diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp index dc37516..1cbbde2 100644 --- a/source/Core/Broadcaster.cpp +++ b/source/Core/Broadcaster.cpp @@ -303,6 +303,16 @@ Broadcaster::HijackBroadcaster (Listener *listener, uint32_t event_mask) return true; } +bool +Broadcaster::IsHijackedForEvent (uint32_t event_mask) +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + if (!m_hijacking_listeners.empty()) + return (event_mask & m_hijacking_masks.back()) != 0; + return false; +} + void Broadcaster::RestoreBroadcaster () { diff --git a/source/Core/ConnectionMachPort.cpp b/source/Core/ConnectionMachPort.cpp index 05ada98..fe29814 100644 --- a/source/Core/ConnectionMachPort.cpp +++ b/source/Core/ConnectionMachPort.cpp @@ -107,6 +107,7 @@ ConnectionMachPort::Connect (const char *s, Error *error_ptr) { if (error_ptr) error_ptr->Clear(); + m_uri.assign(s); } else { @@ -209,6 +210,7 @@ ConnectionMachPort::Disconnect (Error *error_ptr) error_ptr->SetError (kret, eErrorTypeMachKernel); m_port = MACH_PORT_TYPE_NONE; } + m_uri.clear(); return eConnectionStatusSuccess; } @@ -256,6 +258,12 @@ ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &st return 0; } +std::string +ConnectionMachPort::GetURI() +{ + return m_uri; +} + ConnectionStatus ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) { diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp index 1cbee20c..d3dfa3c 100644 --- a/source/Core/ConnectionSharedMemory.cpp +++ b/source/Core/ConnectionSharedMemory.cpp @@ -107,6 +107,13 @@ ConnectionSharedMemory::Write (const void *src, size_t src_len, ConnectionStatus return 0; } +std::string +ConnectionSharedMemory::GetURI() +{ + // TODO: fix when Connect is fixed? + return ""; +} + ConnectionStatus ConnectionSharedMemory::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) { diff --git a/source/Core/ConstString.cpp b/source/Core/ConstString.cpp index 37d24e0..85f8d3c 100644 --- a/source/Core/ConstString.cpp +++ b/source/Core/ConstString.cpp @@ -11,7 +11,7 @@ #include "lldb/Host/Mutex.h" #include "llvm/ADT/StringMap.h" -#include <mutex> +#include <mutex> // std::once using namespace lldb_private; diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index c7342ad..f25a3f4 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/StringRef.h" #include "lldb/lldb-private.h" +#include "lldb/Core/FormatEntity.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" @@ -130,24 +131,23 @@ g_language_enumerators[] = static PropertyDefinition g_properties[] = { -{ "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, -{ "disassembly-format", OptionValue::eTypeString , true, 0 , DEFAULT_DISASSEMBLY_FORMAT, NULL, "The default disassembly format string to use when disassembling instruction sequences." }, -{ "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, -{ "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, -{ "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, -{ "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, -{ "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL, NULL, "The number of disassembly lines to show when displaying a stopped context." }, -{ "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, -{ "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come after the current source line when displaying a stopped context." }, -{ "stop-line-count-before", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, -{ "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, -{ "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, -{ "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, -{ "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, -{ "auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, -{ "escape-non-printables", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." }, - - { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +{ "auto-confirm", OptionValue::eTypeBoolean , true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "disassembly-format", OptionValue::eTypeFormatEntity, true, 0 , DEFAULT_DISASSEMBLY_FORMAT, NULL, "The default disassembly format string to use when disassembling instruction sequences." }, +{ "frame-format", OptionValue::eTypeFormatEntity, true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, +{ "notify-void", OptionValue::eTypeBoolean , true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, +{ "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, +{ "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, +{ "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL, NULL, "The number of disassembly lines to show when displaying a stopped context." }, +{ "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, +{ "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come after the current source line when displaying a stopped context." }, +{ "stop-line-count-before", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, +{ "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, +{ "thread-format", OptionValue::eTypeFormatEntity, true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, +{ "use-external-editor", OptionValue::eTypeBoolean , true, false, NULL, NULL, "Whether to use an external editor or not." }, +{ "use-color", OptionValue::eTypeBoolean , true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, +{ "auto-one-line-summaries", OptionValue::eTypeBoolean , true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, +{ "escape-non-printables", OptionValue::eTypeBoolean , true, true, NULL, NULL, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." }, +{ NULL, OptionValue::eTypeInvalid , true, 0 , NULL, NULL, NULL } }; enum @@ -242,18 +242,18 @@ Debugger::GetAutoConfirm () const return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } -const char * +const FormatEntity::Entry * Debugger::GetDisassemblyFormat() const { const uint32_t idx = ePropertyDisassemblyFormat; - return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); + return m_collection_sp->GetPropertyAtIndexAsFormatEntity(NULL, idx); } -const char * +const FormatEntity::Entry * Debugger::GetFrameFormat() const { const uint32_t idx = ePropertyFrameFormat; - return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); + return m_collection_sp->GetPropertyAtIndexAsFormatEntity(NULL, idx); } bool @@ -282,11 +282,11 @@ Debugger::SetPrompt(const char *p) GetCommandInterpreter().UpdatePrompt(new_prompt); } -const char * +const FormatEntity::Entry * Debugger::GetThreadFormat() const { const uint32_t idx = ePropertyThreadFormat; - return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); + return m_collection_sp->GetPropertyAtIndexAsFormatEntity(NULL, idx); } lldb::ScriptLanguage @@ -927,6 +927,18 @@ Debugger::GetTopIOHandlerControlSequence(char ch) return m_input_reader_stack.GetTopIOHandlerControlSequence (ch); } +const char * +Debugger::GetIOHandlerCommandPrefix() +{ + return m_input_reader_stack.GetTopIOHandlerCommandPrefix(); +} + +const char * +Debugger::GetIOHandlerHelpPrologue() +{ + return m_input_reader_stack.GetTopIOHandlerHelpPrologue(); +} + void Debugger::RunIOHandler (const IOHandlerSP& reader_sp) { @@ -1214,1691 +1226,25 @@ TestPromptFormats (StackFrame *frame) } #endif -static bool -ScanFormatDescriptor (const char* var_name_begin, - const char* var_name_end, - const char** var_name_final, - const char** percent_position, - Format* custom_format, - ValueObject::ValueObjectRepresentationStyle* val_obj_display) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); - *percent_position = ::strchr(var_name_begin,'%'); - if (!*percent_position || *percent_position > var_name_end) - { - if (log) - log->Printf("[ScanFormatDescriptor] no format descriptor in string, skipping"); - *var_name_final = var_name_end; - } - else - { - *var_name_final = *percent_position; - std::string format_name(*var_name_final+1, var_name_end-*var_name_final-1); - if (log) - log->Printf("[ScanFormatDescriptor] parsing %s as a format descriptor", format_name.c_str()); - if ( !FormatManager::GetFormatFromCString(format_name.c_str(), - true, - *custom_format) ) - { - if (log) - log->Printf("[ScanFormatDescriptor] %s is an unknown format", format_name.c_str()); - - switch (format_name.front()) - { - case '@': // if this is an @ sign, print ObjC description - *val_obj_display = ValueObject::eValueObjectRepresentationStyleLanguageSpecific; - break; - case 'V': // if this is a V, print the value using the default format - *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; - break; - case 'L': // if this is an L, print the location of the value - *val_obj_display = ValueObject::eValueObjectRepresentationStyleLocation; - break; - case 'S': // if this is an S, print the summary after all - *val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; - break; - case '#': // if this is a '#', print the number of children - *val_obj_display = ValueObject::eValueObjectRepresentationStyleChildrenCount; - break; - case 'T': // if this is a 'T', print the type - *val_obj_display = ValueObject::eValueObjectRepresentationStyleType; - break; - case 'N': // if this is a 'N', print the name - *val_obj_display = ValueObject::eValueObjectRepresentationStyleName; - break; - case '>': // if this is a '>', print the name - *val_obj_display = ValueObject::eValueObjectRepresentationStyleExpressionPath; - break; - default: - if (log) - log->Printf("ScanFormatDescriptor] %s is an error, leaving the previous value alone", format_name.c_str()); - break; - } - } - // a good custom format tells us to print the value using it - else - { - if (log) - log->Printf("[ScanFormatDescriptor] will display value for this VO"); - *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; - } - } - if (log) - log->Printf("[ScanFormatDescriptor] final format description outcome: custom_format = %d, val_obj_display = %d", - *custom_format, - *val_obj_display); - return true; -} - -static bool -ScanBracketedRange (const char* var_name_begin, - const char* var_name_end, - const char* var_name_final, - const char** open_bracket_position, - const char** separator_position, - const char** close_bracket_position, - const char** var_name_final_if_array_range, - int64_t* index_lower, - int64_t* index_higher) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); - *open_bracket_position = ::strchr(var_name_begin,'['); - if (*open_bracket_position && *open_bracket_position < var_name_final) - { - *separator_position = ::strchr(*open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield - *close_bracket_position = ::strchr(*open_bracket_position,']'); - // as usual, we assume that [] will come before % - //printf("trying to expand a []\n"); - *var_name_final_if_array_range = *open_bracket_position; - if (*close_bracket_position - *open_bracket_position == 1) - { - if (log) - log->Printf("[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); - *index_lower = 0; - } - else if (*separator_position == NULL || *separator_position > var_name_end) - { - char *end = NULL; - *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); - *index_higher = *index_lower; - if (log) - log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", *index_lower); - } - else if (*close_bracket_position && *close_bracket_position < var_name_end) - { - char *end = NULL; - *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); - *index_higher = ::strtoul (*separator_position+1, &end, 0); - if (log) - log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", *index_lower, *index_higher); - } - else - { - if (log) - log->Printf("[ScanBracketedRange] expression is erroneous, cannot extract indices out of it"); - return false; - } - if (*index_lower > *index_higher && *index_higher > 0) - { - if (log) - log->Printf("[ScanBracketedRange] swapping indices"); - int64_t temp = *index_lower; - *index_lower = *index_higher; - *index_higher = temp; - } - } - else if (log) - log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); - return true; -} - -template <typename T> -static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name) -{ - if (script_interpreter) - { - Error script_error; - std::string script_output; - - if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success()) - { - s.Printf("%s", script_output.c_str()); - return true; - } - else - { - s.Printf("<error: %s>",script_error.AsCString()); - } - } - 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 inline bool -IsToken(const char *var_name_begin, const char *var) -{ - return (::strncmp (var_name_begin, var, strlen(var)) == 0); -} - -static bool -IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format, - const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) -{ - int var_len = strlen(var); - if (::strncmp (var_name_begin, var, var_len) == 0) - { - var_name_begin += var_len; - if (*var_name_begin == '}') - { - format = default_format; - return true; - } - else if (*var_name_begin == '%') - { - // Allow format specifiers: x|X|u with optional width specifiers. - // ${thread.id%x} ; hex - // ${thread.id%X} ; uppercase hex - // ${thread.id%u} ; unsigned decimal - // ${thread.id%8.8X} ; width.precision + specifier - // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id) - int dot_count = 0; - const char *specifier = NULL; - int width_precision_length = 0; - const char *width_precision = ++var_name_begin; - while (isdigit(*var_name_begin) || *var_name_begin == '.') - { - dot_count += (*var_name_begin == '.'); - if (dot_count > 1) - break; - var_name_begin++; - width_precision_length++; - } - - if (IsToken (var_name_begin, "tid}")) - { - Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr); - if (target) - { - 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)) - specifier = PRIu64; - } - if (!specifier) - { - format = default_format; - return true; - } - } - else if (IsToken (var_name_begin, "x}")) - specifier = PRIx64; - else if (IsToken (var_name_begin, "X}")) - specifier = PRIX64; - else if (IsToken (var_name_begin, "u}")) - specifier = PRIu64; - - if (specifier) - { - format = "%"; - if (width_precision_length) - format += std::string(width_precision, width_precision_length); - format += specifier; - return true; - } - } - } - return false; -} - -// Find information for the "thread.info.*" specifiers in a format string -static bool -FormatThreadExtendedInfoRecurse -( - const char *var_name_begin, - StructuredData::ObjectSP thread_info_dictionary, - const SymbolContext *sc, - const ExecutionContext *exe_ctx, - Stream &s -) -{ - bool var_success = false; - std::string token_format; - - llvm::StringRef var_name(var_name_begin); - size_t percent_idx = var_name.find('%'); - size_t close_curly_idx = var_name.find('}'); - llvm::StringRef path = var_name; - llvm::StringRef formatter = var_name; - - // 'path' will be the dot separated list of objects to transverse up until we hit - // a close curly brace, a percent sign, or an end of string. - if (percent_idx != llvm::StringRef::npos || close_curly_idx != llvm::StringRef::npos) - { - if (percent_idx != llvm::StringRef::npos && close_curly_idx != llvm::StringRef::npos) - { - if (percent_idx < close_curly_idx) - { - path = var_name.slice(0, percent_idx); - formatter = var_name.substr (percent_idx); - } - else - { - path = var_name.slice(0, close_curly_idx); - formatter = var_name.substr (close_curly_idx); - } - } - else if (percent_idx != llvm::StringRef::npos) - { - path = var_name.slice(0, percent_idx); - formatter = var_name.substr (percent_idx); - } - else if (close_curly_idx != llvm::StringRef::npos) - { - path = var_name.slice(0, close_curly_idx); - formatter = var_name.substr (close_curly_idx); - } - } - - StructuredData::ObjectSP value = thread_info_dictionary->GetObjectForDotSeparatedPath (path); - - if (value.get()) - { - if (value->GetType() == StructuredData::Type::eTypeInteger) - { - if (IsTokenWithFormat (formatter.str().c_str(), "", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) - { - s.Printf(token_format.c_str(), value->GetAsInteger()->GetValue()); - var_success = true; - } - } - else if (value->GetType() == StructuredData::Type::eTypeFloat) - { - s.Printf ("%f", value->GetAsFloat()->GetValue()); - var_success = true; - } - else if (value->GetType() == StructuredData::Type::eTypeString) - { - s.Printf("%s", value->GetAsString()->GetValue().c_str()); - var_success = true; - } - else if (value->GetType() == StructuredData::Type::eTypeArray) - { - if (value->GetAsArray()->GetSize() > 0) - { - s.Printf ("%zu", value->GetAsArray()->GetSize()); - var_success = true; - } - } - else if (value->GetType() == StructuredData::Type::eTypeDictionary) - { - s.Printf ("%zu", value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); - var_success = true; - } - } - - return var_success; -} - - -static bool -FormatPromptRecurse -( - const char *format, - const SymbolContext *sc, - const ExecutionContext *exe_ctx, - const Address *addr, - Stream &s, - const char **end, - ValueObject* valobj, - bool function_changed, - bool initial_function -) -{ - ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers - bool success = true; - const char *p; - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); - - for (p = format; *p != '\0'; ++p) - { - if (realvalobj) - { - valobj = realvalobj; - realvalobj = NULL; - } - size_t non_special_chars = ::strcspn (p, "${}\\"); - if (non_special_chars > 0) - { - if (success) - s.Write (p, non_special_chars); - p += non_special_chars; - } - - if (*p == '\0') - { - break; - } - else if (*p == '{') - { - // Start a new scope that must have everything it needs if it is to - // to make it into the final output stream "s". If you want to make - // a format that only prints out the function or symbol name if there - // is one in the symbol context you can use: - // "{function =${function.name}}" - // The first '{' starts a new scope that end with the matching '}' at - // the end of the string. The contents "function =${function.name}" - // will then be evaluated and only be output if there is a function - // or symbol with a valid name. - StreamString sub_strm; - - ++p; // Skip the '{' - - if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj, function_changed, initial_function)) - { - // The stream had all it needed - s.Write(sub_strm.GetData(), sub_strm.GetSize()); - } - if (*p != '}') - { - success = false; - break; - } - } - else if (*p == '}') - { - // End of a enclosing scope - break; - } - else if (*p == '$') - { - // We have a prompt variable to print - ++p; - if (*p == '{') - { - ++p; - const char *var_name_begin = p; - const char *var_name_end = ::strchr (p, '}'); - - if (var_name_end && var_name_begin < var_name_end) - { - // if we have already failed to parse, skip this variable - if (success) - { - const char *cstr = NULL; - std::string token_format; - Address format_addr; - - // normally "addr" means print a raw address but - // "file-addr-or-load-addr" means print a module + file addr if there's no load addr - bool print_file_addr_or_load_addr = false; - bool addr_offset_concrete_func_only = false; - bool addr_offset_print_with_no_padding = false; - bool calculate_format_addr_function_offset = false; - // Set reg_kind and reg_num to invalid values - RegisterKind reg_kind = kNumRegisterKinds; - uint32_t reg_num = LLDB_INVALID_REGNUM; - FileSpec format_file_spec; - const RegisterInfo *reg_info = NULL; - RegisterContext *reg_ctx = NULL; - bool do_deref_pointer = false; - ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; - ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; - - // Each variable must set success to true below... - bool var_success = false; - switch (var_name_begin[0]) - { - case '*': - case 'v': - case 's': - { - if (!valobj) - break; - - if (log) - log->Printf("[Debugger::FormatPrompt] initial string: %s",var_name_begin); - - // check for *var and *svar - if (*var_name_begin == '*') - { - do_deref_pointer = true; - var_name_begin++; - if (log) - log->Printf("[Debugger::FormatPrompt] found a deref, new string is: %s",var_name_begin); - } - - if (*var_name_begin == 's') - { - if (!valobj->IsSynthetic()) - valobj = valobj->GetSyntheticValue().get(); - if (!valobj) - break; - var_name_begin++; - if (log) - log->Printf("[Debugger::FormatPrompt] found a synthetic, new string is: %s",var_name_begin); - } - - // should be a 'v' by now - if (*var_name_begin != 'v') - break; - - if (log) - log->Printf("[Debugger::FormatPrompt] string I am working with: %s",var_name_begin); - - ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? - ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); - ValueObject::GetValueForExpressionPathOptions options; - options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); - ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; - ValueObject* target = NULL; - Format custom_format = eFormatInvalid; - const char* var_name_final = NULL; - const char* var_name_final_if_array_range = NULL; - const char* close_bracket_position = NULL; - 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; - - if (!valobj) break; - // simplest case ${var}, just print valobj's value - if (IsToken (var_name_begin, "var}")) - { - was_plain_var = true; - target = valobj; - val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; - } - else if (IsToken (var_name_begin, "var.script:")) - { - var_name_begin += ::strlen("var.script:"); - std::string script_name(var_name_begin,var_name_end); - ScriptInterpreter* script_interpreter = valobj->GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (RunScriptFormatKeyword (s, script_interpreter, valobj, script_name)) - var_success = true; - break; - } - else if (IsToken (var_name_begin,"var%")) - { - was_var_format = true; - // this is a variable with some custom format applied to it - const char* percent_position; - target = valobj; - val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; - ScanFormatDescriptor (var_name_begin, - var_name_end, - &var_name_final, - &percent_position, - &custom_format, - &val_obj_display); - } - // this is ${var.something} or multiple .something nested - else if (IsToken (var_name_begin, "var")) - { - if (IsToken (var_name_begin, "var[")) - was_var_indexed = true; - const char* percent_position; - ScanFormatDescriptor (var_name_begin, - var_name_end, - &var_name_final, - &percent_position, - &custom_format, - &val_obj_display); - - const char* open_bracket_position; - const char* separator_position; - ScanBracketedRange (var_name_begin, - var_name_end, - var_name_final, - &open_bracket_position, - &separator_position, - &close_bracket_position, - &var_name_final_if_array_range, - &index_lower, - &index_higher); - - Error error; - - std::string expr_path(var_name_final-var_name_begin-1,0); - memcpy(&expr_path[0], var_name_begin+3,var_name_final-var_name_begin-3); - - 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); - break; - } - 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(); - } - } - else - break; - - 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")); \ - break; - } - do_deref_pointer = false; - } - - if (!target) - { - if (log) - log->Printf("[Debugger::FormatPrompt] could not calculate target for prompt expression"); - break; - } - - // 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 (!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 - var_success = target->DumpPrintableRepresentation(str_temp, - val_obj_display, - custom_format); - if (log) - log->Printf("[Debugger::FormatPrompt] special cases did%s match", var_success ? "" : "n't"); - - // should not happen - if (var_success) - s << str_temp.GetData(); - var_success = true; - break; - } - 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); - } - var_success = true; - break; - } - } - - // 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(); - var_success = true; - break; - } - - // 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>"; - var_success = true; - break; - } - - if (!is_array_range) - { - if (log) - log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); - var_success = 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) - break; - if (log) - log->Printf("[Debugger::FormatPrompt] handle as array"); - const char* special_directions = NULL; - StreamString special_directions_writer; - if (close_bracket_position && (var_name_end-close_bracket_position > 1)) - { - ConstString additional_data; - additional_data.SetCStringWithLength(close_bracket_position+1, var_name_end-close_bracket_position-1); - special_directions_writer.Printf("${%svar%s}", - do_deref_pointer ? "*" : "", - additional_data.GetCString()); - special_directions = special_directions_writer.GetData(); - } - - // let us display items index_lower thru index_higher of this array - s.PutChar('['); - var_success = true; - - if (index_higher < 0) - index_higher = valobj->GetNumChildren() - 1; - - uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); - - for (;index_lower<=index_higher;index_lower++) - { - ValueObject* item = ExpandIndexedExpression (target, - index_lower, - exe_ctx->GetFramePtr(), - false).get(); - - if (!item) - { - if (log) - log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at index %" PRId64, index_lower); - } - else - { - if (log) - log->Printf("[Debugger::FormatPrompt] special_directions for child item: %s",special_directions); - } - - if (!special_directions) - var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); - else - var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item, function_changed, initial_function); - - if (--max_num_children == 0) - { - s.PutCString(", ..."); - break; - } - - if (index_lower < index_higher) - s.PutChar(','); - } - s.PutChar(']'); - } - } - break; - case 'a': - if (IsToken (var_name_begin, "addr-file-or-load}")) - { - print_file_addr_or_load_addr = true; - } - if (IsToken (var_name_begin, "addr}") - || IsToken (var_name_begin, "addr-file-or-load}")) - { - if (addr && addr->IsValid()) - { - var_success = true; - format_addr = *addr; - } - } - break; - - case 'p': - if (IsToken (var_name_begin, "process.")) - { - if (exe_ctx) - { - Process *process = exe_ctx->GetProcessPtr(); - if (process) - { - var_name_begin += ::strlen ("process."); - if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64, exe_ctx, sc)) - { - s.Printf(token_format.c_str(), process->GetID()); - var_success = true; - } - else if ((IsToken (var_name_begin, "name}")) || - (IsToken (var_name_begin, "file.basename}")) || - (IsToken (var_name_begin, "file.fullpath}"))) - { - Module *exe_module = process->GetTarget().GetExecutableModulePointer(); - if (exe_module) - { - if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') - { - format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); - var_success = (bool)format_file_spec; - } - else - { - format_file_spec = exe_module->GetFileSpec(); - var_success = (bool)format_file_spec; - } - } - } - else if (IsToken (var_name_begin, "script:")) - { - var_name_begin += ::strlen("script:"); - std::string script_name(var_name_begin,var_name_end); - ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (RunScriptFormatKeyword (s, script_interpreter, process, script_name)) - var_success = true; - } - } - } - } - break; - - case 't': - if (IsToken (var_name_begin, "thread.")) - { - if (exe_ctx) - { - Thread *thread = exe_ctx->GetThreadPtr(); - if (thread) - { - var_name_begin += ::strlen ("thread."); - if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) - { - s.Printf(token_format.c_str(), thread->GetID()); - var_success = true; - } - else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) - { - s.Printf(token_format.c_str(), thread->GetProtocolID()); - var_success = true; - } - else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64, exe_ctx, sc)) - { - s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID()); - var_success = true; - } - else if (IsToken (var_name_begin, "name}")) - { - cstr = thread->GetName(); - var_success = cstr && cstr[0]; - if (var_success) - s.PutCString(cstr); - } - else if (IsToken (var_name_begin, "queue}")) - { - cstr = thread->GetQueueName(); - var_success = cstr && cstr[0]; - if (var_success) - s.PutCString(cstr); - } - else if (IsToken (var_name_begin, "stop-reason}")) - { - StopInfoSP stop_info_sp = thread->GetStopInfo (); - if (stop_info_sp && stop_info_sp->IsValid()) - { - cstr = stop_info_sp->GetDescription(); - if (cstr && cstr[0]) - { - s.PutCString(cstr); - var_success = true; - } - } - } - else if (IsToken (var_name_begin, "return-value}")) - { - 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); - var_success = true; - } - } - } - else if (IsToken (var_name_begin, "completed-expression}")) - { - 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); - var_success = true; - } - } - } - else if (IsToken (var_name_begin, "script:")) - { - var_name_begin += ::strlen("script:"); - std::string script_name(var_name_begin,var_name_end); - ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) - var_success = true; - } - else if (IsToken (var_name_begin, "info.")) - { - var_name_begin += ::strlen("info."); - StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); - if (object_sp && object_sp->GetType() == StructuredData::Type::eTypeDictionary) - { - var_success = FormatThreadExtendedInfoRecurse (var_name_begin, object_sp, sc, exe_ctx, s); - } - } - } - } - } - else if (IsToken (var_name_begin, "target.")) - { - // TODO: hookup properties -// if (!target_properties_sp) -// { -// Target *target = Target::GetTargetFromContexts (exe_ctx, sc); -// if (target) -// target_properties_sp = target->GetProperties(); -// } -// -// if (target_properties_sp) -// { -// var_name_begin += ::strlen ("target."); -// const char *end_property = strchr(var_name_begin, '}'); -// if (end_property) -// { -// ConstString property_name(var_name_begin, end_property - var_name_begin); -// std::string property_value (target_properties_sp->GetPropertyValue(property_name)); -// if (!property_value.empty()) -// { -// s.PutCString (property_value.c_str()); -// var_success = true; -// } -// } -// } - Target *target = Target::GetTargetFromContexts (exe_ctx, sc); - if (target) - { - var_name_begin += ::strlen ("target."); - if (IsToken (var_name_begin, "arch}")) - { - ArchSpec arch (target->GetArchitecture ()); - if (arch.IsValid()) - { - s.PutCString (arch.GetArchitectureName()); - var_success = true; - } - } - else if (IsToken (var_name_begin, "script:")) - { - var_name_begin += ::strlen("script:"); - std::string script_name(var_name_begin,var_name_end); - ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (RunScriptFormatKeyword (s, script_interpreter, target, script_name)) - var_success = true; - } - } - } - break; - - case 'm': - if (IsToken (var_name_begin, "module.")) - { - if (sc && sc->module_sp.get()) - { - Module *module = sc->module_sp.get(); - var_name_begin += ::strlen ("module."); - - if (IsToken (var_name_begin, "file.")) - { - if (module->GetFileSpec()) - { - var_name_begin += ::strlen ("file."); - - if (IsToken (var_name_begin, "basename}")) - { - format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); - var_success = (bool)format_file_spec; - } - else if (IsToken (var_name_begin, "fullpath}")) - { - format_file_spec = module->GetFileSpec(); - var_success = (bool)format_file_spec; - } - } - } - } - } - break; - - - case 'f': - if (IsToken (var_name_begin, "file.")) - { - if (sc && sc->comp_unit != NULL) - { - var_name_begin += ::strlen ("file."); - - if (IsToken (var_name_begin, "basename}")) - { - format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); - var_success = (bool)format_file_spec; - } - else if (IsToken (var_name_begin, "fullpath}")) - { - format_file_spec = *sc->comp_unit; - var_success = (bool)format_file_spec; - } - } - } - else if (IsToken (var_name_begin, "frame.")) - { - if (exe_ctx) - { - StackFrame *frame = exe_ctx->GetFramePtr(); - if (frame) - { - var_name_begin += ::strlen ("frame."); - if (IsToken (var_name_begin, "index}")) - { - s.Printf("%u", frame->GetFrameIndex()); - var_success = true; - } - else if (IsToken (var_name_begin, "pc}")) - { - reg_kind = eRegisterKindGeneric; - reg_num = LLDB_REGNUM_GENERIC_PC; - var_success = true; - } - else if (IsToken (var_name_begin, "sp}")) - { - reg_kind = eRegisterKindGeneric; - reg_num = LLDB_REGNUM_GENERIC_SP; - var_success = true; - } - else if (IsToken (var_name_begin, "fp}")) - { - reg_kind = eRegisterKindGeneric; - reg_num = LLDB_REGNUM_GENERIC_FP; - var_success = true; - } - else if (IsToken (var_name_begin, "flags}")) - { - reg_kind = eRegisterKindGeneric; - reg_num = LLDB_REGNUM_GENERIC_FLAGS; - var_success = true; - } - else if (IsToken (var_name_begin, "reg.")) - { - reg_ctx = frame->GetRegisterContext().get(); - if (reg_ctx) - { - var_name_begin += ::strlen ("reg."); - if (var_name_begin < var_name_end) - { - std::string reg_name (var_name_begin, var_name_end); - reg_info = reg_ctx->GetRegisterInfoByName (reg_name.c_str()); - if (reg_info) - var_success = true; - } - } - } - else if (IsToken (var_name_begin, "script:")) - { - var_name_begin += ::strlen("script:"); - std::string script_name(var_name_begin,var_name_end); - ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name)) - var_success = true; - } - } - } - } - else if (IsToken (var_name_begin, "function.")) - { - if (sc && (sc->function != NULL || sc->symbol != NULL)) - { - var_name_begin += ::strlen ("function."); - if (IsToken (var_name_begin, "id}")) - { - if (sc->function) - s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); - else - s.Printf("symbol[%u]", sc->symbol->GetID()); - - var_success = true; - } - if (IsToken (var_name_begin, "changed}") && function_changed) - { - var_success = true; - } - if (IsToken (var_name_begin, "initial-function}") && initial_function) - { - var_success = true; - } - else if (IsToken (var_name_begin, "name}")) - { - if (sc->function) - cstr = sc->function->GetName().AsCString (NULL); - else if (sc->symbol) - cstr = sc->symbol->GetName().AsCString (NULL); - if (cstr) - { - s.PutCString(cstr); - - 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); - } - } - } - var_success = true; - } - } - else if (IsToken (var_name_begin, "name-without-args}")) - { - 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()); - var_success = true; - } - } - else if (IsToken (var_name_begin, "name-with-args}")) - { - // Print the function name with arguments in it - - if (sc->function) - { - var_success = true; - ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL; - 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); - } - } - } - else if (sc->symbol) - { - cstr = sc->symbol->GetName().AsCString (NULL); - if (cstr) - { - s.PutCString(cstr); - var_success = true; - } - } - } - else if (IsToken (var_name_begin, "addr-offset}") - || IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) - { - if (IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) - { - addr_offset_print_with_no_padding = true; - addr_offset_concrete_func_only = true; - } - var_success = addr != NULL; - if (var_success) - { - format_addr = *addr; - calculate_format_addr_function_offset = true; - } - } - else if (IsToken (var_name_begin, "line-offset}")) - { - var_success = sc->line_entry.range.GetBaseAddress().IsValid(); - if (var_success) - { - format_addr = sc->line_entry.range.GetBaseAddress(); - calculate_format_addr_function_offset = true; - } - } - else if (IsToken (var_name_begin, "pc-offset}")) - { - StackFrame *frame = exe_ctx->GetFramePtr(); - var_success = frame != NULL; - if (var_success) - { - format_addr = frame->GetFrameCodeAddress(); - calculate_format_addr_function_offset = true; - } - } - } - } - break; - - case 'l': - if (IsToken (var_name_begin, "line.")) - { - if (sc && sc->line_entry.IsValid()) - { - var_name_begin += ::strlen ("line."); - if (IsToken (var_name_begin, "file.")) - { - var_name_begin += ::strlen ("file."); - - if (IsToken (var_name_begin, "basename}")) - { - format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); - var_success = (bool)format_file_spec; - } - else if (IsToken (var_name_begin, "fullpath}")) - { - format_file_spec = sc->line_entry.file; - var_success = (bool)format_file_spec; - } - } - else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc)) - { - var_success = true; - s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line); - } - else if ((IsToken (var_name_begin, "start-addr}")) || - (IsToken (var_name_begin, "end-addr}"))) - { - var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid(); - if (var_success) - { - format_addr = sc->line_entry.range.GetBaseAddress(); - if (var_name_begin[0] == 'e') - format_addr.Slide (sc->line_entry.range.GetByteSize()); - } - } - } - } - break; - case 'c': - if (IsToken (var_name_begin, "current-pc-arrow")) - { - 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 ("-> "); - var_success = true; - } - } - } - if (var_success == false) - { - s.Printf(" "); - var_success = true; - } - } - var_success = true; - } - break; - } - - if (var_success) - { - // If format addr is valid, then we need to print an address - if (reg_num != LLDB_INVALID_REGNUM) - { - StackFrame *frame = exe_ctx->GetFramePtr(); - // We have a register value to display... - if (reg_num == LLDB_REGNUM_GENERIC_PC && reg_kind == eRegisterKindGeneric) - { - format_addr = frame->GetFrameCodeAddress(); - } - else - { - if (reg_ctx == NULL) - reg_ctx = frame->GetRegisterContext().get(); - - if (reg_ctx) - { - if (reg_kind != kNumRegisterKinds) - reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); - reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); - var_success = reg_info != NULL; - } - } - } - - if (reg_info != NULL) - { - RegisterValue reg_value; - var_success = reg_ctx->ReadRegister (reg_info, reg_value); - if (var_success) - { - reg_value.Dump(&s, reg_info, false, false, eFormatDefault); - } - } - - if (format_file_spec) - { - s << format_file_spec; - } - - // If format addr is valid, then we need to print an address - if (format_addr.IsValid()) - { - var_success = false; - - if (calculate_format_addr_function_offset) - { - Address func_addr; - - if (sc) - { - if (sc->function) - { - func_addr = sc->function->GetAddressRange().GetBaseAddress(); - if (sc->block && addr_offset_concrete_func_only == false) - { - // 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 = " "; - if (addr_offset_print_with_no_padding) - { - addr_offset_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); - var_success = 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); - var_success = true; - } - } - } - } - else - { - Target *target = Target::GetTargetFromContexts (exe_ctx, sc); - addr_t vaddr = LLDB_INVALID_ADDRESS; - if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) - vaddr = format_addr.GetLoadAddress (target); - if (vaddr == LLDB_INVALID_ADDRESS) - vaddr = format_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) - { - format_addr.Dump (&s, exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); - } - else - { - s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); - } - var_success = true; - } - } - } - } - - if (var_success == false) - success = false; - } - p = var_name_end; - } - else - break; - } - else - { - // We got a dollar sign with no '{' after it, it must just be a dollar sign - s.PutChar(*p); - } - } - else if (*p == '\\') - { - ++p; // skip the slash - switch (*p) - { - case 'a': s.PutChar ('\a'); break; - case 'b': s.PutChar ('\b'); break; - case 'f': s.PutChar ('\f'); break; - case 'n': s.PutChar ('\n'); break; - case 'r': s.PutChar ('\r'); break; - case 't': s.PutChar ('\t'); break; - case 'v': s.PutChar ('\v'); break; - case '\'': s.PutChar ('\''); break; - case '\\': s.PutChar ('\\'); 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; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) - oct_str[i] = p[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) - p += i - 1; - unsigned long octal_value = ::strtoul (oct_str, NULL, 8); - if (octal_value <= UINT8_MAX) - { - s.PutChar((char)octal_value); - } - } - break; - - case 'x': - // hex number in the format - if (isxdigit(p[1])) - { - ++p; // Skip the 'x' - - // Make a string that can hold onto two hex chars plus a - // NULL terminator - char hex_str[3] = { 0,0,0 }; - hex_str[0] = *p; - if (isxdigit(p[1])) - { - ++p; // Skip the first of the two hex chars - hex_str[1] = *p; - } - - unsigned long hex_value = strtoul (hex_str, NULL, 16); - if (hex_value <= UINT8_MAX) - s.PutChar ((char)hex_value); - } - else - { - s.PutChar('x'); - } - break; - - default: - // Just desensitize any other character by just printing what - // came after the '\' - s << *p; - break; - - } - - } - } - if (end) - *end = p; - return success; -} - bool -Debugger::FormatPrompt -( - const char *format, - const SymbolContext *sc, - const ExecutionContext *exe_ctx, - const Address *addr, - Stream &s, - ValueObject* valobj -) -{ - bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; - std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); - if (format_str.length()) - format = format_str.c_str(); - return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj, false, false); -} - -bool -Debugger::FormatDisassemblerAddress (const char *format, +Debugger::FormatDisassemblerAddress (const FormatEntity::Entry *format, const SymbolContext *sc, const SymbolContext *prev_sc, const ExecutionContext *exe_ctx, const Address *addr, Stream &s) { - if (format == NULL && exe_ctx != NULL && exe_ctx->HasTargetScope()) + FormatEntity::Entry format_entry; + + if (format == NULL) { - format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); + if (exe_ctx != NULL && exe_ctx->HasTargetScope()) + format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); + if (format == NULL) + { + FormatEntity::Parse("${addr}: ", format_entry); + format = &format_entry; + } } bool function_changed = false; bool initial_function = false; @@ -2930,7 +1276,7 @@ Debugger::FormatDisassemblerAddress (const char *format, { initial_function = true; } - return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, NULL, function_changed, initial_function); + return FormatEntity::Format(*format, s, sc, exe_ctx, addr, NULL, function_changed, initial_function); } diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index 649f0c5..14fbee1 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -471,12 +471,7 @@ Disassembler::PrintInstructions } const bool show_bytes = (options & eOptionShowBytes) != 0; - const char *disassembly_format = "${addr-file-or-load}: "; - if (exe_ctx.HasTargetScope()) - { - disassembly_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat (); - } - inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, disassembly_format); + inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, NULL); strm.EOL(); } else @@ -566,7 +561,7 @@ Instruction::Dump (lldb_private::Stream *s, const ExecutionContext* exe_ctx, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, - const char *disassembly_addr_format_spec) + const FormatEntity::Entry *disassembly_addr_format) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; @@ -577,7 +572,7 @@ Instruction::Dump (lldb_private::Stream *s, if (show_address) { - Debugger::FormatDisassemblerAddress (disassembly_addr_format_spec, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); + Debugger::FormatDisassemblerAddress (disassembly_addr_format, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); } if (show_bytes) @@ -985,18 +980,26 @@ InstructionList::Dump (Stream *s, { const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); collection::const_iterator pos, begin, end; - const char *disassemble_format = "${addr-file-or-load}: "; - if (exe_ctx) + + const FormatEntity::Entry *disassembly_format = NULL; + FormatEntity::Entry format; + if (exe_ctx && exe_ctx->HasTargetScope()) + { + disassembly_format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat (); + } + else { - disassemble_format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat (); + FormatEntity::Parse("${addr}: ", format); + disassembly_format = &format; } + for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; pos != end; ++pos) { if (pos != begin) s->EOL(); - (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassemble_format); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassembly_format); } } @@ -1169,6 +1172,24 @@ Disassembler::Disassembler(const ArchSpec& arch, const char *flavor) : m_flavor.assign("default"); else m_flavor.assign(flavor); + + // If this is an arm variant that can only include thumb (T16, T32) + // instructions, force the arch triple to be "thumbv.." instead of + // "armv..." + if (arch.GetTriple().getArch() == llvm::Triple::arm + && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m + || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em + || arch.GetCore() == ArchSpec::Core::eCore_arm_armv6m)) + { + std::string thumb_arch_name (arch.GetTriple().getArchName().str()); + // Replace "arm" with "thumb" so we get all thumb variants correct + if (thumb_arch_name.size() > 3) + { + thumb_arch_name.erase(0, 3); + thumb_arch_name.insert(0, "thumb"); + } + m_arch.SetTriple (thumb_arch_name.c_str()); + } } //---------------------------------------------------------------------- diff --git a/source/Core/Error.cpp b/source/Core/Error.cpp index 03cfd41..5b0bbe2 100644 --- a/source/Core/Error.cpp +++ b/source/Core/Error.cpp @@ -146,7 +146,7 @@ void Error::Clear () { m_code = 0; - m_type = eErrorTypeGeneric; + m_type = eErrorTypeInvalid; m_string.clear(); } diff --git a/source/Core/FormatEntity.cpp b/source/Core/FormatEntity.cpp new file mode 100644 index 0000000..48b2c2d --- /dev/null +++ b/source/Core/FormatEntity.cpp @@ -0,0 +1,2511 @@ +//===-- 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; + case '>': // if this is a '>', print the expression path + 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(); +} diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp index 21ba965..747fd44 100644 --- a/source/Core/IOHandler.cpp +++ b/source/Core/IOHandler.cpp @@ -3217,6 +3217,8 @@ public: FrameTreeDelegate () : TreeDelegate() { + FormatEntity::Parse ("frame #${frame.index}: {${function.name}${function.pc-offset}}}", + m_format); } virtual ~FrameTreeDelegate() @@ -3236,9 +3238,7 @@ public: StreamString strm; const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything); ExecutionContext exe_ctx (frame_sp); - //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}"; - const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}"; - if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm)) + if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, NULL, NULL, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().c_str(), right_pad); @@ -3265,6 +3265,8 @@ public: } return false; } +protected: + FormatEntity::Entry m_format; }; class ThreadTreeDelegate : public TreeDelegate @@ -3276,6 +3278,8 @@ public: m_tid (LLDB_INVALID_THREAD_ID), m_stop_id (UINT32_MAX) { + FormatEntity::Parse ("thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}", + m_format); } virtual @@ -3306,8 +3310,7 @@ public: { StreamString strm; ExecutionContext exe_ctx (thread_sp); - const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}"; - if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm)) + if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().c_str(), right_pad); @@ -3383,6 +3386,8 @@ protected: std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; lldb::user_id_t m_tid; uint32_t m_stop_id; + FormatEntity::Entry m_format; + }; class ThreadsTreeDelegate : public TreeDelegate @@ -3394,6 +3399,8 @@ public: m_debugger (debugger), m_stop_id (UINT32_MAX) { + FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", + m_format); } virtual @@ -3415,8 +3422,7 @@ public: { StreamString strm; ExecutionContext exe_ctx (process_sp); - const char *format = "process ${process.id}{, name = ${process.name}}"; - if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm)) + if (FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().c_str(), right_pad); @@ -3472,6 +3478,8 @@ protected: std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp; Debugger &m_debugger; uint32_t m_stop_id; + FormatEntity::Entry m_format; + }; class ValueObjectListDelegate : public WindowDelegate @@ -4635,6 +4643,8 @@ public: StatusBarWindowDelegate (Debugger &debugger) : m_debugger (debugger) { + FormatEntity::Parse("Thread: ${thread.id%tid}", + m_format); } virtual @@ -4659,8 +4669,7 @@ public: if (StateIsStoppedState(state, true)) { StreamString strm; - const char *format = "Thread: ${thread.id%tid}"; - if (thread && Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm)) + if (thread && FormatEntity::Format (m_format, strm, NULL, &exe_ctx, NULL, NULL, false, false)) { window.MoveCursor (40, 0); window.PutCStringTruncated(strm.GetString().c_str(), 1); @@ -4686,6 +4695,7 @@ public: protected: Debugger &m_debugger; + FormatEntity::Entry m_format; }; class SourceFileWindowDelegate : public WindowDelegate diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp index d205d36..fe4cfb3 100644 --- a/source/Core/Log.cpp +++ b/source/Core/Log.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - // C Includes #include <stdio.h> #include <stdarg.h> @@ -20,7 +18,6 @@ // Other libraries and framework includes // Project includes -#include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" @@ -219,7 +216,6 @@ Log::LogIf (uint32_t bits, const char *format, ...) } } - //---------------------------------------------------------------------- // Printing of errors that are not fatal. //---------------------------------------------------------------------- diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index c0ab66c..e1598d3 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -10,8 +10,9 @@ // FreeBSD9-STABLE requires this to know about size_t in cxxabi.h #include <cstddef> -#if defined(_MSC_VER) -// Cannot enable the builtin demangler on msvc as it does not support the cpp11 within the implementation. +#if defined(_MSC_VER) +#include "lldb/Host/windows/windows.h" +#include <Dbghelp.h> #elif defined (__FreeBSD__) #define LLDB_USE_BUILTIN_DEMANGLER #else @@ -4998,7 +4999,11 @@ static inline bool cstring_is_mangled (const char *s) { if (s) - return s[0] == '_' && s[1] == 'Z'; +#if defined(_MSC_VER) + return (s[0] == '?'); +#else + return (s[0] == '_' && s[1] == 'Z'); +#endif return false; } @@ -5226,15 +5231,27 @@ Mangled::GetDemangledName () const if (!demangled_name) demangled_name = __cxa_demangle (mangled_cstr, NULL, NULL, NULL); #elif defined(_MSC_VER) - // Cannot demangle on msvc. - char *demangled_name = nullptr; + char *demangled_name = (char *)::malloc(1024); + ::ZeroMemory(demangled_name, 1024); + DWORD result = ::UnDecorateSymbolName(mangled_cstr, demangled_name, 1023, + UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected keywords + UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc keywords + UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications + UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers + UNDNAME_NO_MS_KEYWORDS // Strip all MS extension keywords + ); + if (result == 0) + { + free (demangled_name); + demangled_name = nullptr; + } #else char *demangled_name = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); #endif if (demangled_name) { - m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); + m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); free (demangled_name); } } @@ -5335,6 +5352,21 @@ Mangled::MemorySize () const return m_mangled.MemorySize() + m_demangled.MemorySize(); } +lldb::LanguageType +Mangled::GetLanguage () +{ + ConstString mangled = GetMangledName(); + if (mangled) + { + if (GetDemangledName()) + { + if (cstring_is_mangled(mangled.GetCString())) + return lldb::eLanguageTypeC_plus_plus; + } + } + return lldb::eLanguageTypeUnknown; +} + //---------------------------------------------------------------------- // Dump OBJ to the supplied stream S. //---------------------------------------------------------------------- diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp index 900eea2..891bd87 100644 --- a/source/Core/Module.cpp +++ b/source/Core/Module.cpp @@ -152,7 +152,6 @@ Module::Module (const ModuleSpec &module_spec) : m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_did_init_ast (false), - m_is_dynamic_loader_module (false), m_file_has_changed (false), m_first_file_changed_log (false) { @@ -257,7 +256,6 @@ Module::Module(const FileSpec& file_spec, m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_did_init_ast (false), - m_is_dynamic_loader_module (false), m_file_has_changed (false), m_first_file_changed_log (false) { @@ -304,7 +302,6 @@ Module::Module () : m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_did_init_ast (false), - m_is_dynamic_loader_module (false), m_file_has_changed (false), m_first_file_changed_log (false) { @@ -1304,10 +1301,14 @@ Module::GetObjectFile() data_offset); if (m_objfile_sp) { - // Once we get the object file, update our module with the object file's + // Once we get the object file, update our module with the object file's // architecture since it might differ in vendor/os if some parts were - // unknown. - m_objfile_sp->GetArchitecture (m_arch); + // unknown. But since the matching arch might already be more specific + // than the generic COFF architecture, only merge in those values that + // overwrite unspecified unknown values. + ArchSpec new_arch; + m_objfile_sp->GetArchitecture(new_arch); + m_arch.MergeFrom(new_arch); } else { @@ -1732,7 +1733,7 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, if (CPPLanguageRuntime::ExtractContextAndIdentifier (name_cstr, context, basename)) lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); else - lookup_name_type_mask = eFunctionNameTypeFull; + lookup_name_type_mask |= eFunctionNameTypeFull; } else { @@ -1821,3 +1822,13 @@ Module::CreateJITModule (const lldb::ObjectFileJITDelegateSP &delegate_sp) return ModuleSP(); } +bool +Module::GetIsDynamicLinkEditor() +{ + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file) + return obj_file->GetIsDynamicLinkEditor(); + + return false; +} diff --git a/source/Core/RegisterValue.cpp b/source/Core/RegisterValue.cpp index 91f5bea..272c1ee 100644 --- a/source/Core/RegisterValue.cpp +++ b/source/Core/RegisterValue.cpp @@ -19,6 +19,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" #include "lldb/Interpreter/Args.h" +#include "lldb/Host/StringConvert.h" using namespace lldb; using namespace lldb_private; @@ -467,7 +468,7 @@ RegisterValue::SetValueFromCString (const RegisterInfo *reg_info, const char *va case eEncodingUint: if (byte_size <= sizeof (uint64_t)) { - uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + uint64_t uval64 = StringConvert::ToUInt64(value_str, UINT64_MAX, 0, &success); if (!success) error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); else if (!Args::UInt64ValueIsValidForByteSize (uval64, byte_size)) @@ -488,7 +489,7 @@ RegisterValue::SetValueFromCString (const RegisterInfo *reg_info, const char *va case eEncodingSint: if (byte_size <= sizeof (long long)) { - uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + uint64_t sval64 = StringConvert::ToSInt64(value_str, INT64_MAX, 0, &success); if (!success) error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); else if (!Args::SInt64ValueIsValidForByteSize (sval64, byte_size)) diff --git a/source/Core/RegularExpression.cpp b/source/Core/RegularExpression.cpp index 54924d0..3f712e1 100644 --- a/source/Core/RegularExpression.cpp +++ b/source/Core/RegularExpression.cpp @@ -7,36 +7,34 @@ // //===----------------------------------------------------------------------===// +#include <string.h> #include "lldb/Core/RegularExpression.h" #include "llvm/ADT/StringRef.h" -#include <string.h> +#include "lldb/Core/Error.h" -using namespace lldb_private; //---------------------------------------------------------------------- -// Default constructor +// Enable enhanced mode if it is available. This allows for things like +// \d for digit, \s for space, and many more, but it isn't available +// everywhere. //---------------------------------------------------------------------- -RegularExpression::RegularExpression() : - m_re(), - m_comp_err (1), - m_preg(), - m_compile_flags(REG_EXTENDED) -{ - memset(&m_preg,0,sizeof(m_preg)); -} +#if defined(REG_ENHANCED) +#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED|REG_EXTENDED) +#else +#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED) +#endif + +using namespace lldb_private; //---------------------------------------------------------------------- -// Constructor that compiles "re" using "flags" and stores the -// resulting compiled regular expression into this object. +// Default constructor //---------------------------------------------------------------------- -RegularExpression::RegularExpression(const char* re, int flags) : +RegularExpression::RegularExpression() : m_re(), m_comp_err (1), - m_preg(), - m_compile_flags(flags) + m_preg() { memset(&m_preg,0,sizeof(m_preg)); - Compile(re); } //---------------------------------------------------------------------- @@ -46,8 +44,7 @@ RegularExpression::RegularExpression(const char* re, int flags) : RegularExpression::RegularExpression(const char* re) : m_re(), m_comp_err (1), - m_preg(), - m_compile_flags(REG_EXTENDED) + m_preg() { memset(&m_preg,0,sizeof(m_preg)); Compile(re); @@ -56,16 +53,14 @@ RegularExpression::RegularExpression(const char* re) : RegularExpression::RegularExpression(const RegularExpression &rhs) { memset(&m_preg,0,sizeof(m_preg)); - Compile(rhs.GetText(), rhs.GetCompileFlags()); + Compile(rhs.GetText()); } const RegularExpression & RegularExpression::operator= (const RegularExpression &rhs) { if (&rhs != this) - { - Compile (rhs.GetText(), rhs.GetCompileFlags()); - } + Compile (rhs.GetText()); return *this; } //---------------------------------------------------------------------- @@ -94,19 +89,12 @@ RegularExpression::~RegularExpression() bool RegularExpression::Compile(const char* re) { - return Compile (re, m_compile_flags); -} - -bool -RegularExpression::Compile(const char* re, int flags) -{ Free(); - m_compile_flags = flags; if (re && re[0]) { m_re = re; - m_comp_err = ::regcomp (&m_preg, re, flags); + m_comp_err = ::regcomp (&m_preg, re, DEFAULT_COMPILE_FLAGS); } else { @@ -126,7 +114,7 @@ RegularExpression::Compile(const char* re, int flags) // will be executed using the "execute_flags". //--------------------------------------------------------------------- bool -RegularExpression::Execute(const char* s, Match *match, int execute_flags) const +RegularExpression::Execute (const char* s, Match *match) const { int err = 1; if (s != NULL && m_comp_err == 0) @@ -137,7 +125,7 @@ RegularExpression::Execute(const char* s, Match *match, int execute_flags) const s, match->GetSize(), match->GetData(), - execute_flags); + 0); } else { @@ -145,7 +133,7 @@ RegularExpression::Execute(const char* s, Match *match, int execute_flags) const s, 0, NULL, - execute_flags); + 0); } } diff --git a/source/Core/Scalar.cpp b/source/Core/Scalar.cpp index 1bfe6f2..0e9b98d 100644 --- a/source/Core/Scalar.cpp +++ b/source/Core/Scalar.cpp @@ -17,6 +17,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Host/Endian.h" +#include "lldb/Host/StringConvert.h" #include "Plugins/Process/Utility/InstructionUtils.h" @@ -1790,7 +1791,7 @@ Scalar::SetValueFromCString (const char *value_str, Encoding encoding, size_t by case eEncodingUint: if (byte_size <= sizeof (unsigned long long)) { - uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + uint64_t uval64 = StringConvert::ToUInt64(value_str, UINT64_MAX, 0, &success); if (!success) error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); else if (!UIntValueIsValidForSize (uval64, byte_size)) @@ -1819,7 +1820,7 @@ Scalar::SetValueFromCString (const char *value_str, Encoding encoding, size_t by case eEncodingSint: if (byte_size <= sizeof (long long)) { - uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + uint64_t sval64 = StringConvert::ToSInt64(value_str, INT64_MAX, 0, &success); if (!success) error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); else if (!SIntValueIsValidForSize (sval64, byte_size)) diff --git a/source/Core/StreamFile.cpp b/source/Core/StreamFile.cpp index 2285ca9..9f8dd62 100644 --- a/source/Core/StreamFile.cpp +++ b/source/Core/StreamFile.cpp @@ -49,7 +49,8 @@ StreamFile::StreamFile (FILE *fh, bool transfer_ownership) : StreamFile::StreamFile (const char *path) : Stream (), - m_file (path, File::eOpenOptionWrite | File::eOpenOptionCanCreate, lldb::eFilePermissionsFileDefault) + m_file (path, File::eOpenOptionWrite | File::eOpenOptionCanCreate | File::eOpenOptionCloseOnExec, + lldb::eFilePermissionsFileDefault) { } diff --git a/source/Core/Value.cpp b/source/Core/Value.cpp index db33fce..a416d07 100644 --- a/source/Core/Value.cpp +++ b/source/Core/Value.cpp @@ -277,7 +277,7 @@ Value::GetValueByteSize (Error *error_ptr) { const ClangASTType &ast_type = GetClangType(); if (ast_type.IsValid()) - byte_size = ast_type.GetByteSize(); + byte_size = ast_type.GetByteSize(nullptr); } break; } @@ -434,7 +434,7 @@ Value::GetValueAsData (ExecutionContext *exe_ctx, lldb::Encoding type_encoding = ast_type.GetEncoding(type_encoding_count); if (type_encoding == eEncodingUint || type_encoding == eEncodingSint) - limit_byte_size = ast_type.GetByteSize(); + limit_byte_size = ast_type.GetByteSize(nullptr); } if (m_value.GetData (data, limit_byte_size)) diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp index fa5fb14..b72e5c3 100644 --- a/source/Core/ValueObject.cpp +++ b/source/Core/ValueObject.cpp @@ -99,6 +99,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), m_value_checksum(), + m_preferred_display_language(lldb::eLanguageTypeUnknown), m_value_is_valid (false), m_value_did_change (false), m_children_count_valid (false), @@ -149,6 +150,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope, m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), m_value_checksum(), + m_preferred_display_language(lldb::eLanguageTypeUnknown), m_value_is_valid (false), m_value_did_change (false), m_children_count_valid (false), @@ -969,7 +971,9 @@ ValueObject::GetPointeeData (DataExtractor& data, if (item_count == 0) return 0; - const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(); + ExecutionContext exe_ctx (GetExecutionContextRef()); + + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(&exe_ctx); const uint64_t bytes = item_count * item_type_size; const uint64_t offset = item_idx * item_type_size; @@ -1045,7 +1049,7 @@ ValueObject::GetPointeeData (DataExtractor& data, break; case eAddressTypeHost: { - const uint64_t max_bytes = GetClangType().GetByteSize(); + const uint64_t max_bytes = GetClangType().GetByteSize(&exe_ctx); if (max_bytes > offset) { size_t bytes_read = std::min<uint64_t>(max_bytes - offset, bytes); @@ -1505,14 +1509,14 @@ ValueObject::GetValueAsSigned (int64_t fail_value, bool *success) { if (success) *success = true; - return scalar.SLongLong(fail_value); + return scalar.SLongLong(fail_value); } // fallthrough, otherwise... } if (success) *success = false; - return fail_value; + return fail_value; } // if any more "special cases" are added to ValueObject::DumpPrintableRepresentation() please keep @@ -2220,10 +2224,12 @@ ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type if (!can_create) return ValueObjectSP(); + ExecutionContext exe_ctx (GetExecutionContextRef()); + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, type, name_const_str, - type.GetByteSize(), + type.GetByteSize(&exe_ctx), offset, 0, 0, @@ -2261,10 +2267,12 @@ ValueObject::GetSyntheticBase (uint32_t offset, const ClangASTType& type, bool c const bool is_base_class = true; + ExecutionContext exe_ctx (GetExecutionContextRef()); + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, type, name_const_str, - type.GetByteSize(), + type.GetByteSize(&exe_ctx), offset, 0, 0, @@ -4128,16 +4136,22 @@ ValueObject::GetRoot () { if (m_root) return m_root; - ValueObject* parent = m_parent; - if (!parent) - return (m_root = this); - while (parent->m_parent) + return (m_root = FollowParentChain( [] (ValueObject* vo) -> bool { + return (vo->m_parent != nullptr); + })); +} + +ValueObject* +ValueObject::FollowParentChain (std::function<bool(ValueObject*)> f) +{ + ValueObject* vo = this; + while (vo) { - if (parent->m_root) - return (m_root = parent->m_root); - parent = parent->m_parent; + if (f(vo) == false) + break; + vo = vo->m_parent; } - return (m_root = parent); + return vo; } AddressType @@ -4181,24 +4195,33 @@ ValueObject::GetFormat () const lldb::LanguageType ValueObject::GetPreferredDisplayLanguage () { - lldb::LanguageType type = lldb::eLanguageTypeUnknown; - if (GetRoot()) + lldb::LanguageType type = m_preferred_display_language; + if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { - if (GetRoot() == this) + if (GetRoot()) { - if (StackFrameSP frame_sp = GetFrameSP()) + if (GetRoot() == this) { - const SymbolContext& sc(frame_sp->GetSymbolContext(eSymbolContextCompUnit)); - if (CompileUnit* cu = sc.comp_unit) - type = cu->GetLanguage(); + if (StackFrameSP frame_sp = GetFrameSP()) + { + const SymbolContext& sc(frame_sp->GetSymbolContext(eSymbolContextCompUnit)); + if (CompileUnit* cu = sc.comp_unit) + type = cu->GetLanguage(); + } + } + else + { + type = GetRoot()->GetPreferredDisplayLanguage(); } - } - else - { - type = GetRoot()->GetPreferredDisplayLanguage(); } } - return type; + return (m_preferred_display_language = type); // only compute it once +} + +void +ValueObject::SetPreferredDisplayLanguage (lldb::LanguageType lt) +{ + m_preferred_display_language = lt; } bool diff --git a/source/Core/ValueObjectConstResult.cpp b/source/Core/ValueObjectConstResult.cpp index fc870d7..e8b477a 100644 --- a/source/Core/ValueObjectConstResult.cpp +++ b/source/Core/ValueObjectConstResult.cpp @@ -256,8 +256,10 @@ ValueObjectConstResult::GetValueType() const uint64_t ValueObjectConstResult::GetByteSize() { + ExecutionContext exe_ctx(GetExecutionContextRef()); + if (m_byte_size == 0) - m_byte_size = GetClangType().GetByteSize(); + SetByteSize(GetClangType().GetByteSize(&exe_ctx)); return m_byte_size; } diff --git a/source/Core/ValueObjectDynamicValue.cpp b/source/Core/ValueObjectDynamicValue.cpp index 1b8ec80..89b98a1 100644 --- a/source/Core/ValueObjectDynamicValue.cpp +++ b/source/Core/ValueObjectDynamicValue.cpp @@ -127,7 +127,7 @@ ValueObjectDynamicValue::GetByteSize() { const bool success = UpdateValueIfNeeded(false); if (success && m_dynamic_type_info.HasType()) - return m_value.GetValueByteSize(NULL); + return m_value.GetValueByteSize(nullptr); else return m_parent->GetByteSize(); } diff --git a/source/Core/ValueObjectMemory.cpp b/source/Core/ValueObjectMemory.cpp index 5fbe87b..9f19531 100644 --- a/source/Core/ValueObjectMemory.cpp +++ b/source/Core/ValueObjectMemory.cpp @@ -169,7 +169,7 @@ ValueObjectMemory::GetByteSize() { if (m_type_sp) return m_type_sp->GetByteSize(); - return m_clang_type.GetByteSize (); + return m_clang_type.GetByteSize (nullptr); } lldb::ValueType diff --git a/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp index dafe73a..e266267 100644 --- a/source/Core/ValueObjectSyntheticFilter.cpp +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -298,3 +298,9 @@ ValueObjectSynthetic::CanProvideValue () return true; return m_parent->CanProvideValue(); } + +bool +ValueObjectSynthetic::SetValueFromCString (const char *value_str, Error& error) +{ + return m_parent->SetValueFromCString(value_str, error); +} diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp index ab74a50..ed2aeb3 100644 --- a/source/Core/ValueObjectVariable.cpp +++ b/source/Core/ValueObjectVariable.cpp @@ -105,12 +105,14 @@ ValueObjectVariable::CalculateNumChildren() uint64_t ValueObjectVariable::GetByteSize() { + ExecutionContext exe_ctx(GetExecutionContextRef()); + ClangASTType type(GetClangType()); if (!type.IsValid()) return 0; - return type.GetByteSize(); + return type.GetByteSize(&exe_ctx); } lldb::ValueType |