new detailed descriptions for type summary add and type format add

some changes to the help system code for better display of long help text
-p and -r flags now also work for type format add

llvm-svn: 134574
This commit is contained in:
Enrico Granata 2011-07-07 00:38:40 +00:00
parent 2bbdc0bcda
commit 82a7d98342
6 changed files with 237 additions and 17 deletions

View File

@ -242,6 +242,18 @@ public:
const char *separator,
const char *help_text,
uint32_t max_word_len);
// this mimics OutputFormattedHelpText but it does perform a much simpler
// formatting, basically ensuring line alignment. This is only good if you have
// some complicated layout for your help text and want as little help as reasonable
// in properly displaying it. Most of the times, you simply want to type some text
// and have it printed in a reasonable way on screen. If so, use OutputFormattedHelpText
void
OutputHelpText (Stream &stream,
const char *command_word,
const char *separator,
const char *help_text,
uint32_t max_word_len);
Debugger &
GetDebugger ()

View File

@ -29,12 +29,36 @@ public:
typedef const char *(ArgumentHelpCallbackFunction) ();
struct ArgumentHelpCallback
{
ArgumentHelpCallbackFunction *help_callback;
bool self_formatting;
ArgumentHelpCallback(ArgumentHelpCallbackFunction *p,
bool f = false) :
help_callback(p),
self_formatting(f)
{
}
const char*
operator () () const
{
return (*help_callback)();
}
operator bool() const
{
return (help_callback != NULL);
}
};
struct ArgumentTableEntry // Entries in the main argument information table
{
lldb::CommandArgumentType arg_type;
const char *arg_name;
CommandCompletions::CommonCompletionTypes completion_type;
ArgumentHelpCallbackFunction *help_function;
ArgumentHelpCallback help_function;
const char *help_text;
};

View File

@ -351,7 +351,6 @@ namespace lldb {
eArgTypeExprFormat,
eArgTypeFilename,
eArgTypeFormat,
eArgTypeFormatString,
eArgTypeFrameIndex,
eArgTypeFullName,
eArgTypeFunctionName,
@ -387,6 +386,7 @@ namespace lldb {
eArgTypeSourceFile,
eArgTypeSortOrder,
eArgTypeStartAddress,
eArgTypeSummaryString,
eArgTypeSymbol,
eArgTypeThreadID,
eArgTypeThreadIndex,

View File

@ -63,6 +63,12 @@ private:
case 'f':
error = Args::StringToFormat(option_arg, m_format, NULL);
break;
case 'p':
m_skip_pointers = true;
break;
case 'r':
m_skip_references = true;
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
@ -76,6 +82,8 @@ private:
{
m_cascade = true;
m_format = eFormatInvalid;
m_skip_pointers = false;
m_skip_references = false;
}
const OptionDefinition*
@ -92,6 +100,8 @@ private:
bool m_cascade;
lldb::Format m_format;
bool m_skip_references;
bool m_skip_pointers;
};
CommandOptions m_options;
@ -118,6 +128,34 @@ public:
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
SetHelpLong(
"Some examples of using this command.\n"
"We use as reference the following snippet of code:\n"
"\n"
"typedef int Aint;\n"
"typedef float Afloat;\n"
"typedef Aint Bint;\n"
"typedef Afloat Bfloat;\n"
"\n"
"Aint ix = 5;\n"
"Bint iy = 5;\n"
"\n"
"Afloat fx = 3.14;\n"
"BFloat fy = 3.14;\n"
"\n"
"Typing:\n"
"type format add -f hex AInt\n"
"frame variable iy\n"
"will produce an hex display of iy, because no formatter is available for Bint and the one for Aint is used instead\n"
"To prevent this type\n"
"type format add -f hex -C no AInt\n"
"\n"
"A similar reasoning applies to\n"
"type format add -f hex -C no float -p\n"
"which now prints all floats and float&s as hexadecimal, but does not format float*s\n"
"and does not change the default display for Afloat and Bfloat objects.\n"
);
}
~CommandObjectTypeFormatAdd ()
@ -145,7 +183,10 @@ public:
ValueFormatSP entry;
entry.reset(new ValueFormat(m_options.m_format,m_options.m_cascade));
entry.reset(new ValueFormat(m_options.m_format,
m_options.m_cascade,
m_options.m_skip_pointers,
m_options.m_skip_references));
// now I have a valid format, let's add it to every type
@ -172,6 +213,8 @@ CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "format", 'f', required_argument, NULL, 0, eArgTypeFormat, "The format to use to display this type."},
{ LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for pointers-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
@ -343,9 +386,11 @@ private:
{
if (regex == NULL || regex->Execute(type))
{
result->GetOutputStream().Printf ("%s: %s%s\n", type,
result->GetOutputStream().Printf ("%s: %s%s%s%s\n", type,
FormatManager::GetFormatAsCString (entry->m_format),
entry->m_cascades ? "" : " (not cascading)");
entry->m_cascades ? "" : " (not cascading)",
entry->m_skip_pointers ? " (skip pointers)" : "",
entry->m_skip_references ? " (skip references)" : "");
}
return true;
}
@ -489,6 +534,62 @@ public:
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
SetHelpLong(
"Some examples of using this command.\n"
"We use as reference the following snippet of code:\n"
"struct JustADemo\n"
"{\n"
"int* ptr;\n"
"float value;\n"
"JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}\n"
"};\n"
"JustADemo object(42,3.14);\n"
"struct AnotherDemo : public JustADemo\n"
"{\n"
"uint8_t byte;\n"
"AnotherDemo(uint8_t b = 'E', int p = 1, float v = 0.1) : JustADemo(p,v), byte(b) {}\n"
"};\n"
"AnotherDemo *another_object = new AnotherDemo('E',42,3.14);\n"
"\n"
"type summary add -f \"the answer is ${*var.ptr}\" JustADemo\n"
"when typing frame variable object you will get \"the answer is 42\"\n"
"type summary add -f \"the answer is ${*var.ptr}, and the question is ${var.value}\" JustADemo\n"
"when typing frame variable object you will get \"the answer is 42 and the question is 3.14\"\n"
"\n"
"Alternatively, you could also say\n"
"type summary add -f \"${var%V} -> ${*var}\" \"int *\"\n"
"and replace the above summary string with\n"
"type summary add -f \"the answer is ${var.ptr}, and the question is ${var.value}\" JustADemo\n"
"to obtain a similar result\n"
"\n"
"To add a summary valid for both JustADemo and AnotherDemo you can use the scoping operator, as in:\n"
"type summary add -f \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes\n"
"\n"
"This will be used for both variables of type JustADemo and AnotherDemo. To prevent this, change the -C to read -C no\n"
"If you do not want pointers to be shown using that summary, you can use the -p option, as in:\n"
"type summary add -f \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes -p\n"
"A similar option -r exists for references.\n"
"\n"
"If you simply want a one-line summary of the content of your variable, without typing an explicit string to that effect\n"
"you can use the -c option, without giving any summary string:\n"
"type summary add -c JustADemo\n"
"frame variable object\n"
"the output being similar to (ptr=0xsomeaddress, value=3.14)\n"
"\n"
"If you want to display some summary text, but also expand the structure of your object, you can add the -e option, as in:\n"
"type summary add -e -f \"*ptr = ${*var.ptr}\" JustADemo\n"
"Here the value of the int* is displayed, followed by the standard LLDB sequence of children objects, one per line.\n"
"to get an output like:\n"
"\n"
"*ptr = 42 {\n"
" ptr = 0xsomeaddress\n"
" value = 3.14\n"
"}\n"
"\n"
"A command you may definitely want to try if you're doing C++ debugging is:\n"
"type summary add -f \"${var._M_dataplus._M_p}\" std::string\n"
);
}
~CommandObjectTypeSummaryAdd ()
@ -574,7 +675,7 @@ CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] =
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeBoolean, "Type names are actually regular expressions."},
{ LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeBoolean, "If true, inline all child values into summary string."},
{ LLDB_OPT_SET_2 , true, "format-string", 'f', required_argument, NULL, 0, eArgTypeFormatString, "Format string used to display text and object contents."},
{ LLDB_OPT_SET_2 , true, "format-string", 'f', required_argument, NULL, 0, eArgTypeSummaryString, "Format string used to display text and object contents."},
{ LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};

View File

@ -2013,6 +2013,49 @@ CommandInterpreter::OutputFormattedHelpText (Stream &strm,
strm.IndentLess(indent_size);
}
void
CommandInterpreter::OutputHelpText (Stream &strm,
const char *word_text,
const char *separator,
const char *help_text,
uint32_t max_word_len)
{
int indent_size = max_word_len + strlen (separator) + 2;
strm.IndentMore (indent_size);
StreamString text_strm;
text_strm.Printf ("%-*s %s %s", max_word_len, word_text, separator, help_text);
const uint32_t max_columns = m_debugger.GetTerminalWidth();
bool first_line = true;
size_t len = text_strm.GetSize();
const char *text = text_strm.GetData();
uint32_t chars_left = max_columns;
for (uint32_t i = 0; i < len; i++)
{
if ((text[i] == ' ' && ::strchr((text+i+1), ' ') && chars_left < ::strchr((text+i+1), ' ')-(text+i)) || text[i] == '\n')
{
first_line = false;
chars_left = max_columns - indent_size;
strm.EOL();
strm.Indent();
}
else
{
strm.PutChar(text[i]);
chars_left--;
}
}
strm.EOL();
strm.IndentLess(indent_size);
}
void
CommandInterpreter::AproposAllSubCommands (CommandObject *cmd_obj, const char *prefix, const char *search_word,
StringList &commands_found, StringList &commands_help)

View File

@ -469,9 +469,20 @@ CommandObject::GetArgumentHelp (Stream &str, CommandArgumentType arg_type, Comma
StreamString name_str;
name_str.Printf ("<%s>", entry->arg_name);
if (entry->help_function != NULL)
interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", (*(entry->help_function)) (),
name_str.GetSize());
if (entry->help_function)
{
const char* help_text = entry->help_function();
if (!entry->help_function.self_formatting)
{
interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", help_text,
name_str.GetSize());
}
else
{
interpreter.OutputHelpText(str, name_str.GetData(), "--", help_text,
name_str.GetSize());
}
}
else
interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", entry->help_text, name_str.GetSize());
}
@ -630,32 +641,61 @@ BreakpointIDRangeHelpTextCallback ()
static const char *
FormatHelpTextCallback ()
{
static char* help_text_ptr = NULL;
if (help_text_ptr)
return help_text_ptr;
StreamString sstr;
sstr << "One of the format names (or one-character names) that can be used to show a variable's value:\n";
for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1))
{
if (f != eFormatDefault)
sstr.PutChar('\n');
char format_char = FormatManager::GetFormatAsFormatChar(f);
if (format_char)
sstr.Printf("'%c' or ", format_char);
sstr.Printf ("\"%s\" ; ", FormatManager::GetFormatAsCString(f));
sstr.Printf ("\"%s\"", FormatManager::GetFormatAsCString(f));
}
sstr.Flush();
std::string data = sstr.GetString();
char* help = new char[data.length()+1];
help_text_ptr = new char[data.length()+1];
data.copy(help, data.length());
data.copy(help_text_ptr, data.length());
return help;
return help_text_ptr;
}
static const char *
FormatStringHelpTextCallback()
SummaryStringHelpTextCallback()
{
return "Ask me tomorrow";
return
"A summary string is a way to extract information from variables in order to present them using a summary.\n"
"Summary strings contain static text, variables, scopes and control sequences:\n"
" - Static text can be any sequence of non-special characters, i.e. anything but '{', '}', '$', or '\\'.\n"
" - Variables are sequences of characters beginning with ${, ending with } and that contain symbols in the format described below.\n"
" - Scopes are any sequence of text between { and }. Anything included in a scope will only appear in the output summary if there were no errors.\n"
" - Control sequences are the usual C/C++ '\\a', '\\n', ..., plus '\\$', '\\{' and '\\}'.\n"
"A summary string works by copying static text verbatim, turning control sequences into their character counterpart, expanding variables and trying to expand scopes.\n"
"A variable is expanded by giving it a value other than its textual representation, and the way this is done depends on what comes after the ${ marker.\n"
"The most common sequence if ${var followed by an expression path, which is the text one would type to access a member of an aggregate types, given a variable of that type"
" (e.g. if type T has a member named x, which has a member named y, and if t is of type T, the expression path would be .x.y and the way to fit that into a summary string would be"
" ${var.x.y}). In expression paths you can use either . or -> without any difference in meaning. You can also use ${*var followed by an expression path and in that case"
" the object referred by the path will be dereferenced before being displayed. If the object is not a pointer, doing so will cause an error.\n"
"By default, summary strings attempt to display the summary for any variable they reference, and if that fails the value. If neither can be shown, nothing is displayed."
"In a summary string, you can also use an array index [n], or a slice-like range [n-m]. This can have two different meanings depending on what kind of object the expression"
" path refers to:\n"
" - if it is a scalar type (any basic type like int, float, ...) the expression is a bitfield, i.e. the bits indicated by the indexing operator are extracted out of the number"
" and displayed as an individual variable\n"
" - if it is an array or pointer the array items indicated by the indexing operator are shown as the result of the variable. if the expression is an array, real array items are"
" printed; if it is a pointer, the pointer-as-array syntax is used to obtain the values (this means, the latter case can have no range checking)\n"
"If you are trying to display an array for which the size is known, you can also use [] instead of giving an exact range. This has the effect of showing items 0 thru size - 1.";
}
const char *
@ -693,8 +733,7 @@ CommandObject::g_arguments_data[] =
{ eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, NULL, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" },
{ eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, NULL, "The name of a file (can include path)." },
{ eArgTypeFormat, "format", CommandCompletions::eNoCompletion, FormatHelpTextCallback, NULL },
{ eArgTypeFormatString, "format-string", CommandCompletions::eNoCompletion, FormatStringHelpTextCallback, NULL },
{ eArgTypeFormat, "format", CommandCompletions::eNoCompletion, CommandObject::ArgumentHelpCallback(FormatHelpTextCallback, true), NULL },
{ eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, NULL, "Index into a thread's list of frames." },
{ eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, NULL, "The name of a function." },
@ -730,6 +769,7 @@ CommandObject::g_arguments_data[] =
{ eArgTypeSourceFile, "source-file", CommandCompletions::eSourceFileCompletion, NULL, "The name of a source file.." },
{ eArgTypeSortOrder, "sort-order", CommandCompletions::eNoCompletion, NULL, "Specify a sort order when dumping lists." },
{ eArgTypeStartAddress, "start-address", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeSummaryString, "summary-string", CommandCompletions::eNoCompletion, CommandObject::ArgumentHelpCallback(SummaryStringHelpTextCallback, true), NULL },
{ eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, NULL, "Any symbol name (function name, variable, argument, etc.)" },
{ eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, NULL, "Thread ID number." },
{ eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, NULL, "Index into the process' list of threads." },