From 88da35f88121fbc3402ffd698f04b2d1923b7fea Mon Sep 17 00:00:00 2001
From: Enrico Granata
Date: Tue, 23 Aug 2011 21:26:09 +0000
Subject: [PATCH] Improved the user-friendliness of errors shown by the summary
feature in certain areas Renamed format "signed decimal" to be "decimal".
"unsigned decimal" remains unchanged: - the name "signed decimal" was
interfering with symbol %S (use summary) in summary strings. because of
the way summary strings are implemented, this did not really lead to a bug,
but simply to performing more steps than necessary to display a summary.
this is fixed. Documentation improvements (more on synthetic children, some
information on filters). This is still a WIP.
llvm-svn: 138384
---
lldb/source/Core/Debugger.cpp | 9 +-
lldb/source/Core/FormatManager.cpp | 2 +-
lldb/source/Core/ValueObject.cpp | 13 +-
.../data-formatter/rdar-9974002/Makefile | 5 +
.../rdar-9974002/Test-rdar-9974002.py | 146 ++++++++++++++++++
.../data-formatter/rdar-9974002/main.cpp | 30 ++++
lldb/www/varformats.html | 142 +++++++++++++----
7 files changed, 311 insertions(+), 36 deletions(-)
create mode 100644 lldb/test/functionalities/data-formatter/rdar-9974002/Makefile
create mode 100644 lldb/test/functionalities/data-formatter/rdar-9974002/Test-rdar-9974002.py
create mode 100644 lldb/test/functionalities/data-formatter/rdar-9974002/main.cpp
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index ecca589cab67..875466c3e0ce 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1186,11 +1186,16 @@ Debugger::FormatPrompt
}
else
{
- // if ${var}
- if (was_plain_var)
+ if (was_plain_var) // if ${var}
{
s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
}
+ else if (is_pointer) // if pointer, value is the address stored
+ {
+ var_success = target->GetPrintableRepresentation(s,
+ val_obj_display,
+ custom_format);
+ }
else
{
s << "";
diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp
index 89f921664759..40d3a81da396 100644
--- a/lldb/source/Core/FormatManager.cpp
+++ b/lldb/source/Core/FormatManager.cpp
@@ -39,7 +39,7 @@ g_format_infos[] =
{ eFormatCharPrintable , 'C' , "printable character" },
{ eFormatComplexFloat , 'F' , "complex float" },
{ eFormatCString , 's' , "c-string" },
- { eFormatDecimal , 'i' , "signed decimal" },
+ { eFormatDecimal , 'i' , "decimal" },
{ eFormatEnum , 'E' , "enumeration" },
{ eFormatHex , 'x' , "hex" },
{ eFormatFloat , 'f' , "float" },
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 948109d5415c..8b8d452a77cf 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -1021,7 +1021,18 @@ ValueObject::GetPrintableRepresentation(Stream& s,
if (return_value)
s.PutCString(return_value);
else
- s.PutCString("");
+ {
+ if (m_error.Fail())
+ s.Printf("<%s>", m_error.AsCString());
+ else if (val_obj_display == eDisplaySummary)
+ s.PutCString("");
+ else if (val_obj_display == eDisplayValue)
+ s.PutCString("");
+ else if (val_obj_display == eDisplayLanguageSpecific)
+ s.PutCString(""); // edit this if we have other runtimes that support a description
+ else
+ s.PutCString("");
+ }
// we should only return false here if we could not do *anything*
// even if we have an error message as output, that's a success
diff --git a/lldb/test/functionalities/data-formatter/rdar-9974002/Makefile b/lldb/test/functionalities/data-formatter/rdar-9974002/Makefile
new file mode 100644
index 000000000000..314f1cb2f077
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/rdar-9974002/Makefile
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/test/functionalities/data-formatter/rdar-9974002/Test-rdar-9974002.py b/lldb/test/functionalities/data-formatter/rdar-9974002/Test-rdar-9974002.py
new file mode 100644
index 000000000000..cbac7c73b5c4
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/rdar-9974002/Test-rdar-9974002.py
@@ -0,0 +1,146 @@
+"""
+Test lldb data formatter subsystem.
+"""
+
+import os, time
+import unittest2
+import lldb
+from lldbtest import *
+
+class DataFormatterTestCase(TestBase):
+
+ # test for rdar://problem/9974002 ()
+ mydir = os.path.join("functionalities", "data-formatter", "rdar-9974002")
+
+ @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+ def test_with_dsym_and_run_command(self):
+ """Test data formatter commands."""
+ self.buildDsym()
+ self.data_formatter_commands()
+
+ def test_with_dwarf_and_run_command(self):
+ """Test data formatter commands."""
+ self.buildDwarf()
+ self.data_formatter_commands()
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line number to break at.
+ self.line = line_number('main.cpp', '// Set break point at this line.')
+
+ def data_formatter_commands(self):
+ """Test that that file and class static variables display correctly."""
+ self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+ self.expect("breakpoint set -f main.cpp -l %d" % self.line,
+ BREAKPOINT_CREATED,
+ startstr = "Breakpoint created: 1: file ='main.cpp', line = %d, locations = 1" %
+ self.line)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ['stopped',
+ 'stop reason = breakpoint'])
+
+ # This is the function to remove the custom formats in order to have a
+ # clean slate for the next test case.
+ def cleanup():
+ self.runCmd('type summary clear', check=False)
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', ''])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%S}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s foo contained")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', 'foo'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', 'foo'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%V}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', ''])
+
+ self.runCmd("type summary delete contained")
+ self.runCmd("n")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', ''])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%S}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s foo contained")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', 'foo'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', 'foo'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%V}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', '0x000000'])
+
+ self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container")
+
+ self.expect('frame variable mine',
+ substrs = ['mine = ',
+ '1', ''])
+
+
+if __name__ == '__main__':
+ import atexit
+ lldb.SBDebugger.Initialize()
+ atexit.register(lambda: lldb.SBDebugger.Terminate())
+ unittest2.main()
diff --git a/lldb/test/functionalities/data-formatter/rdar-9974002/main.cpp b/lldb/test/functionalities/data-formatter/rdar-9974002/main.cpp
new file mode 100644
index 000000000000..03a9f278b7ec
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/rdar-9974002/main.cpp
@@ -0,0 +1,30 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include
+
+struct contained
+{
+ int first;
+ int second;
+};
+
+struct container
+{
+ int scalar;
+ struct contained *pointer;
+};
+
+int main ()
+{
+ struct container mine = {1, 0};
+ printf ("Mine's scalar is the only thing that is good: %d.\n", mine.scalar); // Set break point at this line.
+ return 0;
+}
+
diff --git a/lldb/www/varformats.html b/lldb/www/varformats.html
index 2a1540f06bbb..c873a027b7b2 100755
--- a/lldb/www/varformats.html
+++ b/lldb/www/varformats.html
@@ -159,11 +159,11 @@
(lldb) type format add -f float32[]
int
- (lldb) fr var pointer *pointer -T
+ (lldb) frame variable pointer *pointer -T
(int *) pointer = {1.46991e-39 1.4013e-45}
(int) *pointer = {1.53302e-42}
(lldb) type format add -f float32[] int -p
- (lldb) fr var pointer *pointer -T
+ (lldb) frame variable pointer *pointer -T
(int *) pointer = 0x0000000100100180
(int) *pointer = {1.53302e-42}
@@ -390,7 +390,7 @@
information from classes, structures, ... (aggregate types)
and arranging it in a user-defined format, as in the following example:
before adding a summary...
- (lldb) fr var -T one
+ (lldb) frame variable -T one
(i_am_cool) one = {
(int) integer = 3
(float) floating = 3.14159
@@ -398,7 +398,7 @@
}
after adding a summary...
- (lldb) fr var one
+ (lldb) frame variable one
(i_am_cool) one = int = 3, float = 3.14159, char = 69
@@ -409,7 +409,7 @@
In the example, the command we type was:
|
- (lldb) type summary add -f "int = ${var.integer}, float = ${var.floating}, char = ${var.character%u}" i_am_cool
+ (lldb) type summary add --summary-string "int = ${var.integer}, float = ${var.floating}, char = ${var.character%u}" i_am_cool
|
@@ -546,7 +546,7 @@
dereferencing symbol only applies to the result of the
whole expression path traversing.
e.g.
- (lldb) fr var -T c
+ (lldb) frame variable -T c
(Couple) c = {
(SimpleWithPointers) sp = {
(int *) x = 0x00000001001000b0
@@ -561,7 +561,7 @@
- (lldb) type summary add -f "int = ${*var.sp.x},
+ (lldb) type summary add --summary-string "int = ${*var.sp.x},
float = ${*var.sp.y}, char = ${*var.sp.z%u}, Simple =
${*var.s}" Couple
(lldb) type summary add -c -p Simple
@@ -570,7 +570,7 @@
the output becomes:
- (lldb) fr var c
+ (lldb) frame variable c
(Couple) c = int = 9, float = 9.99, char = 88, Simple
= (x=9, y=9.99, z='X')
@@ -584,7 +584,7 @@
produce the same output:
- (lldb) type summary add -f "int = ${*var.sp.x},
+ (lldb) type summary add --summary-string "int = ${*var.sp.x},
float = ${*var.sp.y}, char = ${*var.sp.z%u}, Simple =
${var.s}" Couple
(lldb) type summary add -c Simple
@@ -612,18 +612,18 @@
similar to that used for arrays, just you can also give
a pair of indices separated by a -.
e.g.
- (lldb) fr var float_point
+ (lldb) frame variable float_point
(float) float_point = -3.14159
|
- (lldb) type summary add -f "Sign: ${var[31]%B}
+ (lldb) type summary add --summary-string "Sign: ${var[31]%B}
Exponent: ${var[30-23]%x} Mantissa: ${var[0-22]%u}"
float
|
- (lldb) fr var float_point
+ (lldb) frame variable float_point
(float) float_point = -3.14159 Sign: true Exponent:
0x00000080 Mantissa: 4788184
In this example, LLDB shows the internal
@@ -645,7 +645,7 @@
LLDB to format arrays in special ways, possibly
independent of the way the array members' datatype is formatted.
e.g.
- (lldb) fr var sarray
+ (lldb) frame variable sarray
(Simple [3]) sarray = {
[0] = {
x = 1
@@ -666,13 +666,13 @@
|
- (lldb) type summary add -f "${var[].x}" "Simple
+ (lldb) type summary add --summary-string "${var[].x}" "Simple
[3]"
|
- (lldb) fr var sarray
+ (lldb) frame variable sarray
(Simple [3]) sarray = [1,4,7]
The [] symbol amounts to: if var
@@ -683,7 +683,7 @@
If you find some of those integers anomalous, you can
then inspect that one item in greater detail, without
the array format getting in the way:
- (lldb) fr var sarray[1]
+ (lldb) frame variable sarray[1]
(Simple) sarray[1] = {
x = 4
y = 5
@@ -695,12 +695,12 @@
for bitfields:
|
- (lldb) type summary add -f "${var[1-2].x}" "Simple
+ (lldb) type summary add --summary-string "${var[1-2].x}" "Simple
[3]"
|
- (lldb) fr var sarray
+ (lldb) frame variable sarray
(Simple [3]) sarray = [4,7]
The same logic works if you are printing a pointer
@@ -716,7 +716,7 @@
omitting square brackets, as in:
|
- (lldb) type summary add -f "${var%s}" "char *"
+ (lldb) type summary add --summary-string "${var%s}" "char *"
|
@@ -734,7 +734,7 @@
type, even if square brackets are omitted.
|
- (lldb) type summary add -f "${var%int32_t[]}" "int [10]"
+ (lldb) type summary add --summary-string "${var%int32_t[]}" "int [10]"
|
@@ -836,7 +836,7 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
If you need to delve into several levels of hierarchy, as you can do with summary
- strings, you must use the method GetValueForExpressionPath(), passing it
+ strings, you can use the method GetValueForExpressionPath(), passing it
an expression path just like those you could use for summary strings. However, if you need
to access array slices, you cannot do that (yet) via this method call, and you must
use GetChildMemberWithName() querying it for the array items one by one.
@@ -845,12 +845,12 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
to input a Python script as a summary:
- - using the -s option to
type summary add and typing the script
+ - using the --python-script option to
type summary add and typing the script
code as an option argument; as in:
- (lldb) type summary add -s "height =
+ (lldb) type summary add --python-script "height =
int(valobj.GetChildMemberWithName('height').GetValue());width =
int(valobj.GetChildMemberWithName('width').GetValue());
return 'Area: ' + str(height*width)" Rectangle
@@ -885,13 +885,13 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
|
- (lldb) type summary add -f "${var[].x}"
+ (lldb) type summary add --summary-string "${var[].x}"
-x "Simple \[[0-9]+\]"
|
- (lldb) fr var sarray
+ (lldb) frame variable sarray
(Simple [3]) sarray = [1,4,7]
The above scenario works for Simple [3]
as well as for any other array of Simple
@@ -905,6 +905,9 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
matching. Thus, if your type has a base class with a
cascading summary, this will be preferred over any
regular expression match for your type itself.
+
+ The regular expression language used by LLDB is the POSIX extended regular expression language, as defined by the SUS.
+
@@ -924,12 +927,12 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
|
- (lldb) type summary add -f "x=${var.integer}" --name NamedSummary
+ (lldb) type summary add --summary-string "x=${var.integer}" --name NamedSummary
|
- (lldb) fr var one
+ (lldb) frame variable one
(i_am_cool) one = int = 3, float = 3.14159, char = 69
- (lldb) fr var one --summary NamedSummary
+ (lldb) frame variable one --summary NamedSummary
(i_am_cool) one = x=3
@@ -959,9 +962,9 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
For instance, consider an STL vector:
(lldb) frame variable numbers -T
- (std::vector) numbers = {
- (std::_Vector_base >) std::_Vector_base > = {
- (std::_Vector_base >::_Vector_impl) _M_impl = {
+ (std::vector<int>) numbers = {
+ (std::_Vector_base<int, std::allocator<int> >) std::_Vector_base<int, std::allocator<int> > = {
+ (std::_Vector_base<int, std::allocator&tl;int> >::_Vector_impl) _M_impl = {
(int *) _M_start = 0x00000001001008a0
(int *) _M_finish = 0x00000001001008a8
(int *) _M_end_of_storage = 0x00000001001008a8
@@ -974,7 +977,7 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
What you would like to see is probably something like:
(lldb) frame variable numbers -T
- (std::vector) numbers = {
+ (std::vector<int>) numbers = {
(int) [0] = 1
(int) [1] = 12
(int) [2] = 123
@@ -1004,8 +1007,83 @@ Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
of LLDB.
For examples of how synthetic children are created, you are encouraged to look at examples/synthetic in the LLDB trunk.
+
+ Once a synthetic children provider is written, one must load it into LLDB before it can be used.
+ Currently, one can use the LLDB script command to type Python code interactively,
+ or use the script import module command to load Python code from a Python module
+ (ordinary rules apply to importing modules this way). A third option is to type the code for
+ the provider class interactively while adding it.
+
+ For example, let's pretend we have a class Foo for which a synthetic children provider class Foo_Provider
+ is available, in a Python module named Foo_Tools. The following interaction sets Foo_Provider as a synthetic
+ children provider in LLDB:
+
+
+
+ (lldb) script import Foo_Tools
+ (lldb) type synthetic add Foo --python-class Foo_Tools.Foo_Provider
+ |
+
+ (lldb) frame variable a_foo
+ (Foo) a_foo = {
+ x = 1
+ y = "Hello world"
+ }
+
+
+ Currently, in LLDB top of tree, synthetic children providers are enabled for
+ std::vector<T>, std::list<T> and std::map<K,V>.
+
+ Synthetic children enable a new symbol for summary strings, ${svar. This symbol tells LLDB to refer expression paths to the
+ synthetic children instead of the real ones. While in certain cases, you can use ${var.synthetic-child-path} and LLDB will
+ access the synthetic child correctly, it is best to always use ${svar to refer to synthetic children. For instance,
+
+
+ |
+ (lldb) type summary add --expand -x "std::vector<" --summary-string "${svar%#} items"
+ |
+
+ (lldb) frame variable numbers
+ (std::vector<int>) numbers = 4 items {
+ (int) [0] = 1
+ (int) [1] = 12
+ (int) [2] = 123
+ (int) [3] = 1234
+ }
+
+
+
+
+
+
+
+ Filters are a solution to the display of complex classes.
+ At times, classes have many member variables but not all of these are actually
+ necessary for the user to see.
+ A filter will solve this issue by only letting the user see those member
+ variables he cares about. Of course, the equivalent of a filter can be implemented easily
+ using synthetic children, but a filter lets you get the job done without having to write
+ Python code.
+ For instance, if your class Foobar has member variables named A thru Z, but you only need to see
+ the ones named B, H and Q, you can define a filter:
+
+ |
+ (lldb) type filter add Foo --child B --child H --child Q
+ |
+
+ (lldb) frame variable a_foobar
+ (Foobar) a_foobar = {
+ (int) B = 1
+ (char) H = 'H'
+ (std::string) Q = "Hello world"
+ }
+
+
+
+
+
|
| |