diff --git a/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h b/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h --- a/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h +++ b/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h @@ -66,7 +66,7 @@ DumpValueObjectOptions & SetMaximumPointerDepth(PointerDepth depth = {PointerDepth::Mode::Never, 0}); - DumpValueObjectOptions &SetMaximumDepth(uint32_t depth = 0); + DumpValueObjectOptions &SetMaximumDepth(uint32_t depth, bool is_default); DumpValueObjectOptions &SetDeclPrintingHelper(DeclPrintingHelper helper); @@ -125,6 +125,7 @@ SetPointerAsArray(const PointerAsArraySettings &ptr_array); uint32_t m_max_depth = UINT32_MAX; + bool m_max_depth_is_default = true; lldb::DynamicValueType m_use_dynamic = lldb::eNoDynamicValues; uint32_t m_omit_summary_depth = 0; lldb::Format m_format = lldb::eFormatDefault; diff --git a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h --- a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h +++ b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h @@ -117,6 +117,8 @@ bool PrintChildrenOneLiner(bool hide_names); + bool HasReachedMaximumDepth(); + private: ValueObject *m_orig_valobj; ValueObject *m_valobj; diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -223,11 +223,11 @@ eBroadcastBitAsynchronousErrorData = (1 << 4) }; - enum ChildrenTruncatedWarningStatus // tristate boolean to manage children - // truncation warning - { eNoTruncation = 0, // never truncated - eUnwarnedTruncation = 1, // truncated but did not notify - eWarnedTruncation = 2 // truncated and notified + /// Tristate boolean to manage children omission warnings. + enum ChildrenOmissionWarningStatus { + eNoOmission = 0, ///< No children were omitted. + eUnwarnedOmission = 1, ///< Children omitted, and not yet notified. + eWarnedOmission = 2 ///< Children omitted and notified. }; enum CommandTypes { @@ -496,21 +496,33 @@ } void ChildrenTruncated() { - if (m_truncation_warning == eNoTruncation) - m_truncation_warning = eUnwarnedTruncation; + if (m_truncation_warning == eNoOmission) + m_truncation_warning = eUnwarnedOmission; } - bool TruncationWarningNecessary() { - return (m_truncation_warning == eUnwarnedTruncation); + void SetReachedMaximumDepth() { + if (m_max_depth_warning == eNoOmission) + m_max_depth_warning = eUnwarnedOmission; } - void TruncationWarningGiven() { m_truncation_warning = eWarnedTruncation; } + void PrintWarningsIfNecessary(Stream &s, const std::string &cmd_name) { + if (m_truncation_warning == eUnwarnedOmission) { + s.Printf("*** Some of the displayed variables have more members than the " + "debugger will show by default. To show all of them, you can " + "either use the --show-all-children option to %s or raise the " + "limit by changing the target.max-children-count setting.\n", + cmd_name.c_str()); + m_truncation_warning = eWarnedOmission; + } - const char *TruncationWarningText() { - return "*** Some of your variables have more members than the debugger " - "will show by default. To show all of them, you can either use the " - "--show-all-children option to %s or raise the limit by changing " - "the target.max-children-count setting.\n"; + if (m_max_depth_warning == eUnwarnedOmission) { + s.Printf("*** Some of the displayed variables have a greater depth of " + "members than the debugger will show by default. To increase " + "the limit, use the --depth option to %s, or raise the limit by " + "changing the target.max-children-depth setting.\n", + cmd_name.c_str()); + m_max_depth_warning = eWarnedOmission; + } } CommandHistory &GetCommandHistory() { return m_command_history; } @@ -701,9 +713,12 @@ lldb::IOHandlerSP m_command_io_handler_sp; char m_comment_char; bool m_batch_command_mode; - ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated - // children and whether - // the user has been told + /// Whether we truncated a value's list of children and whether the user has + /// been told. + ChildrenOmissionWarningStatus m_truncation_warning; + /// Whether we reached the maximum child nesting depth and whether the user + /// has been told. + ChildrenOmissionWarningStatus m_max_depth_warning; // FIXME: Stop using this to control adding to the history and then replace // this with m_command_source_dirs.size(). diff --git a/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h --- a/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h +++ b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h @@ -47,6 +47,7 @@ uint32_t no_summary_depth; uint32_t max_depth; + bool max_depth_is_default; uint32_t ptr_depth; uint32_t elem_count; lldb::DynamicValueType use_dynamic; diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h b/lldb/include/lldb/Interpreter/OptionValueProperties.h --- a/lldb/include/lldb/Interpreter/OptionValueProperties.h +++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h @@ -152,6 +152,10 @@ GetPropertyAtIndexAsOptionValueSInt64(const ExecutionContext *exe_ctx, uint32_t idx) const; + OptionValueUInt64 * + GetPropertyAtIndexAsOptionValueUInt64(const ExecutionContext *exe_ctx, + uint32_t idx) const; + int64_t GetPropertyAtIndexAsSInt64(const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -166,6 +166,8 @@ uint32_t GetMaximumNumberOfChildrenToDisplay() const; + std::pair GetMaximumDepthOfChildrenToDisplay() const; + uint32_t GetMaximumSizeOfStringSummary() const; uint32_t GetMaximumMemReadSize() const; diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -695,11 +695,8 @@ } } - if (m_interpreter.TruncationWarningNecessary()) { - result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), - m_cmd_name.c_str()); - m_interpreter.TruncationWarningGiven(); - } + m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), + m_cmd_name); // Increment statistics. bool res = result.Succeeded(); diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -972,11 +972,8 @@ } } - if (m_interpreter.TruncationWarningNecessary()) { - result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), - m_cmd_name.c_str()); - m_interpreter.TruncationWarningGiven(); - } + m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), + m_cmd_name); return result.Succeeded(); } diff --git a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp --- a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp +++ b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp @@ -38,8 +38,9 @@ } DumpValueObjectOptions & -DumpValueObjectOptions::SetMaximumDepth(uint32_t depth) { +DumpValueObjectOptions::SetMaximumDepth(uint32_t depth, bool is_default) { m_max_depth = depth; + m_max_depth_is_default = is_default; return *this; } diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -500,7 +500,7 @@ if (m_options.m_use_objc) return false; - if (is_failed_description || m_curr_depth < m_options.m_max_depth) { + if (is_failed_description || !HasReachedMaximumDepth()) { // We will show children for all concrete types. We won't show pointer // contents unless a pointer depth has been specified. We won't reference // contents unless the reference is the root object (depth of zero). @@ -786,9 +786,22 @@ m_stream->EOL(); } else PrintChildren(value_printed, summary_printed, curr_ptr_depth); - } else if (m_curr_depth >= m_options.m_max_depth && IsAggregate() && + } else if (HasReachedMaximumDepth() && IsAggregate() && ShouldPrintValueObject()) { m_stream->PutCString("{...}\n"); + // The maximum child depth has been reached. If `m_max_depth` is the default + // (i.e. the user has _not_ customized it), then lldb presents a warning to + // the user. The warning tells the user that the limit has been reached, but + // more importantly tells them how to expand the limit if desired. + if (m_options.m_max_depth_is_default) + m_valobj->GetTargetSP() + ->GetDebugger() + .GetCommandInterpreter() + .SetReachedMaximumDepth(); } else m_stream->EOL(); } + +bool ValueObjectPrinter::HasReachedMaximumDepth() { + return m_curr_depth >= m_options.m_max_depth; +} diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -128,7 +128,8 @@ m_debugger(debugger), m_synchronous_execution(true), m_skip_lldbinit_files(false), m_skip_app_init_files(false), m_comment_char('#'), m_batch_command_mode(false), - m_truncation_warning(eNoTruncation), m_command_source_depth(0) { + m_truncation_warning(eNoOmission), m_max_depth_warning(eNoOmission), + m_command_source_depth(0) { SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit"); SetEventName(eBroadcastBitResetPrompt, "reset-prompt"); SetEventName(eBroadcastBitQuitCommandReceived, "quit"); diff --git a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp --- a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp +++ b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -104,6 +104,8 @@ max_depth = UINT32_MAX; error.SetErrorStringWithFormat("invalid max depth '%s'", option_arg.str().c_str()); + } else { + max_depth_is_default = false; } break; @@ -163,6 +165,7 @@ flat_output = false; use_objc = false; max_depth = UINT32_MAX; + max_depth_is_default = true; ptr_depth = 0; elem_count = 0; use_synth = true; @@ -172,9 +175,11 @@ TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); - if (target_sp) + if (target_sp) { use_dynamic = target_sp->GetPreferDynamicValue(); - else { + std::tie(max_depth, max_depth_is_default) = + target_sp->GetMaximumDepthOfChildrenToDisplay(); + } else { // If we don't have any targets, then dynamic values won't do us much good. use_dynamic = lldb::eNoDynamicValues; } @@ -190,7 +195,7 @@ options.SetShowSummary(false); else options.SetOmitSummaryDepth(no_summary_depth); - options.SetMaximumDepth(max_depth) + options.SetMaximumDepth(max_depth, max_depth_is_default) .SetShowTypes(show_types) .SetShowLocation(show_location) .SetUseObjectiveC(use_objc) diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp b/lldb/source/Interpreter/OptionValueProperties.cpp --- a/lldb/source/Interpreter/OptionValueProperties.cpp +++ b/lldb/source/Interpreter/OptionValueProperties.cpp @@ -412,6 +412,17 @@ return nullptr; } +OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64( + const ExecutionContext *exe_ctx, uint32_t idx) const { + const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); + if (property) { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsUInt64(); + } + return nullptr; +} + int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64( const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4236,6 +4236,15 @@ nullptr, idx, g_target_properties[idx].default_uint_value); } +std::pair +TargetProperties::GetMaximumDepthOfChildrenToDisplay() const { + const uint32_t idx = ePropertyMaxChildrenDepth; + auto *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueUInt64(nullptr, idx); + bool is_default = !option_value->OptionWasSet(); + return {option_value->GetCurrentValue(), is_default}; +} + uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { const uint32_t idx = ePropertyMaxSummaryLength; return m_collection_sp->GetPropertyAtIndexAsSInt64( diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -72,6 +72,9 @@ def MaxChildrenCount: Property<"max-children-count", "SInt64">, DefaultUnsignedValue<256>, Desc<"Maximum number of children to expand in any level of depth.">; + def MaxChildrenDepth: Property<"max-children-depth", "UInt64">, + DefaultUnsignedValue<0xFFFFFFFF>, + Desc<"Maximum depth to expand children.">; def MaxSummaryLength: Property<"max-string-summary-length", "SInt64">, DefaultUnsignedValue<1024>, Desc<"Maximum number of characters to show when using %s in summary strings.">;