[lldb-dev] Using data formatters to display QString

Enrico Granata egranata at apple.com
Tue Apr 29 13:30:05 PDT 2014


Well that seems to imply we don't know what the qstring module is when trying to use it to run the formatter

How are you loading the python script from your plugin?

Another thing that you could try is debug LLDB and see what happens when we try fetching the summary. I am expecting we will fail to find the function object and then we can take it from there.

Sent from the iPhone of
Enrico Granata <egranata@🍎.com>

> On Apr 29, 2014, at 1:20 PM, Eran Ifrah <eran.ifrah at gmail.com> wrote:
> 
> 
> 
> 
>> On Tue, Apr 29, 2014 at 8:15 PM, Enrico Granata <egranata at apple.com> wrote:
>> 
>>> On Apr 29, 2014, at 6:50 AM, Eran Ifrah <eran.ifrah at gmail.com> wrote:
>>> 
>>> Hi Enrico and all,
>>> Thanks for the references, it helped me a lot.
>>> I am now able to view QString within lldb from the command line, but not from within my plugin :P (this was my intention all the way)
>>> 
>>> I created the following 2 files:
>>> ~/.lldbinit, with this single line:
>>> 
>>> command script import /home/eran/.lldb/qstring.py
>>> 
>>> In the script: ~/.lldb/qstring.py, I placed the following content (a slightly modified printer based on your example):
>>> 
>>> import lldb
>>> 
>>> def utf16string_summary(value, *rest):
>>>     f = open('/tmp/py.log','w+b')
>>>     f.write('inside utf16string_summary\n')
>>>     f.close()
>>>     str_data = value.GetChildMemberWithName("d").GetChildMemberWithName("data")
>>>     length_vo = value.GetChildMemberWithName("d").GetChildMemberWithName("size")
>>>     length = length_vo.GetValueAsUnsigned(0)
>>>     if length == 0:
>>>         return '""'
>>>     data = str_data.GetPointeeData(0, length)
>>>     error = lldb.SBError()
>>>     bytes = data.ReadRawData(error, 0, 2*length)
>>>     return '"%s"' % (bytes.decode('utf-16').encode('utf-8'))
>>> 
>>> def __lldb_init_module(debugger, *rest):
>>>     summary = lldb.SBTypeSummary.CreateWithFunctionName("qstring.utf16string_summary")
>>>     summary.SetOptions(lldb.eTypeOptionHideChildren)
>>>     debugger.GetDefaultCategory().AddTypeSummary( lldb.SBTypeNameSpecifier("QString", False), summary )
>>> 
>>> This setup works when I am using lldb-3.5 from the command line (i.e. QString is displayed in the following format: m_stringMemeber = "some content")
>> 
>> How recent/non recent is lldb-3.5?
>> Did you try with trunk at all?
> 
> On this VirtualBox, ​I am using the Ubuntu 14.04 packages (the package name is lldb-3.5-dev, but I am not sure of which revision is it...)
>> 
>>> Now, this is how I set it up from within my plugin:
>>> 
>>> I tried both:
>>> m_debugger = lldb::SBDebugger::Create(true); // source init files
>>> 
>>> and I have also tried this:
>>> 
>>> m_debugger = lldb::SBDebugger::Create();
>>> ...
>>> lldb::SBCommandReturnObject ret;
>>> m_debugger.GetCommandInterpreter().HandleCommand("command source /home/eran/.lldbinit", ret);
>>> if ( !ret.Succeeded() ) {
>>>     // print error here if any
>>> }
>>> 
>>> Both did not have any affect, i.e. when I view the content of QString in the IDE, I don't see the summary as it should
>>> Any hints?
>> 
>> Well, I see two - or rather three - potential issues
>> First of all, Is the summary loaded?
> ​Yes, it does. 
> After the debug session I added a call for debug purposes:
> m_debugger.GetCommandInterpreter().HandleCommand("type summary list", ret);
> And here are the relevant parts:
>> ​[ 22:55:49:193 DBG ] codelite-lldb: type summary list returned:
> [ 22:55:49:193 DBG ] -----------------------
> [ 22:55:49:193 DBG ] Category: default (enabled)
> [ 22:55:49:193 DBG ] -----------------------
> [ 22:55:49:193 DBG ] QString:  (not cascading)
> [ 22:55:49:193 DBG ] wxPoint: `x = ${var.x}, y = ${var.y}`
> [ 22:55:49:193 DBG ] wxRect: `(x = ${var.x}, y = ${var.y}) (width = ${var.width}, height = ${var.height})`
> [ 22:55:49:193 DBG ] wxString: `${var.m_impl._M_dataplus._M_p}`
> [ 22:55:49:193 DBG ] -----------------------
> [ 22:55:49:193 DBG ] Category: objc (enabled)
> [ 22:55:49:193 DBG ] -----------------------
> 
> ​The first one "QString" was added using python script, the following 3 (wxPoint, wxString and wxRect) were added using 'type summary add..' simple command all 3 are working (from within my plugin). The fact that the wx* summaries are working properly within the plugin give me confident that the method I am using to retrieve the data is correct.
> 
> 
> 
>> To check if it is, run “type summary list” and make sure you see a formatter for QString listed in the default category - and that the default category is enabled
>> Second, is the summary working? If it is loaded, we can test it manually: do something like this while you’re stopped in a frame with a QString variable
>> (lldb) ​​script value = lldb.frame.FindVariable(“myQStringThingNameHere”)
>> (lldb) ​​script print qstring.utf16string_summary(value,None)
> ​Arg, I don't have a 'console' where I can free type commands ( I will definitely add one to the plugin)​
> For now, I added the above commands manually for debug purposes and here are the results:
> 
> [ 23:13:24:029 DBG ] codelite-lldb: output of command 'script print qstring.utf16string_summary(value, None)':
> [ 23:13:24:029 DBG ] Traceback (most recent call last):
> [ 23:13:24:029 DBG ]   File "<input>", line 1, in <module>
> [ 23:13:24:029 DBG ] NameError: name 'qstring' is not defined
> 
> The log clearly shows that it does not know what 'qstring' is... but what?
> 
>> Does anything come out with a manual invocation?
>> 
>> Assuming the summary is registered, and manually calling it works - how are you fetching the summary in your plugin?
> ​I don't think that this is the case as it works for other summaries (e.g. wxString)
>>> Since it works at the command line, I assume the type name for QString is just plain QString with no namespaces in front. Is that correct?
> ​Yes, the same code (with the same ~/.lldbinit) running from command line:
> 
> (lldb) 
> Process 21809 stopped
> * thread #1: tid = 21809, 0x0000000000400ae5 TestQString`main(argc=1, argv=0x00007fff922a8688) + 184 at main.cpp:11, name = 'TestQString', stop reason = step over
>     frame #0: 0x0000000000400ae5 TestQString`main(argc=1, argv=0x00007fff922a8688) + 184 at main.cpp:11
>    8        Q_UNUSED( argv );
>    9        QString str;
>    10       str = QString("Hello %1").arg("world");
> -> 11       printf("%s\n", str.toLocal8Bit().constData());
>    12       return 0;
>    13   }
> (lldb) p str
> (QString) $0 = "Hello world"
> 
> Note that the 'lldb' from the command line is was also installed from the Ubuntu package lldb-3.5-dev, so we can also rule that one out...
> 
>> 
>>> Thanks
>>> 
>>> 
>>>> On Tue, Apr 29, 2014 at 3:38 AM, Enrico Granata <egranata at apple.com> wrote:
>>>>> On Apr 28, 2014, at 3:01 PM, Poenitz Andre <Andre.Poenitz at digia.com> wrote:
>>>>> 
>>>>> 
>>>>> Enrico Granata wrote:
>>>>> > Eran Ifrah wrote:
>>>>> > You can't use expressions in summary strings.
>>>>> > We have thought about this several times and have a couple ideas on how 
>>>>> > it could be done but for now it's not there.
>>>>> >
>>>>> > If you need to resort to an expression, you can use a python formatter
>>>>> > instead and then you are free to call as many expressions as you like.
>>>>> >
>>>>> > However, this will cause a slowdown - running expressions is not
>>>>> > free - and if you ever need to make sure nothing is altering your
>>>>> > program state, running expressions might not be a safe bet.
>>>>> > Is there really no other way to get to those UTF8 bytes?
>>>>> 
>>>>> QString is stored in UTF16 internally. It can be accessed directly 
>>>>> through structure member access and pointer arithmetic and converted 
>>>>> using Python. "Running expressions" is not needed.
>>>>> 
>>>>> Andre'
>>>> 
>>>> 
>>>> Here’s a small example for general reference:
>>>> Assume I have the following data structure:
>>>> 
>>>> #include <string>
>>>> #include <memory>
>>>> 
>>>> class UTF16String {
>>>> public:
>>>>   UTF16String (const char16_t *data) {
>>>>     len = std::char_traits<char16_t>::length(data);
>>>>     str_data.reset(new char16_t[len]);
>>>>     memcpy(str_data.get(),data,sizeof(char16_t)*(len+1));
>>>>   }
>>>>   
>>>> private:
>>>>   std::unique_ptr<char16_t[]> str_data;
>>>>   size_t len;
>>>> };
>>>> 
>>>> int main() {
>>>>   UTF16String string {u"Just some data in UTF16 here"};
>>>>   return 0;
>>>> }
>>>> 
>>>> 
>>>> This is what it looks like “raw” on OS X:
>>>> (UTF16String) string = {
>>>>   str_data = {
>>>>     __ptr_ = {
>>>>       std::__1::__libcpp_compressed_pair_imp<char16_t *, std::__1::default_delete<char16_t> > = {
>>>>         __first_ = 0x00000001001037e0
>>>>       }
>>>>     }
>>>>   }
>>>>   len = 28
>>>> }
>>>> 
>>>> To define a formatter for it you essentially want to grab two elements: the data pointer (__first_ = 0x00000001001037e0) and the length (len = 30)
>>>> In our example, the length is defined in UTF16-characters rather than bytes. This is something you want to know when writing a formatter
>>>> 
>>>> So let’s delve right in:
>>>> def utf16string_summary(value,*rest):
>>>>   str_data = value.GetChildMemberWithName("str_data").GetChildMemberWithName("__ptr_").GetChildMemberWithName("__first_")
>>>>   length_vo = value.GetChildMemberWithName("len")
>>>> 
>>>> Now we have SBValues for the string data and for the length - we want the “number stored inside” the length:
>>>>   length = length_vo.GetValueAsUnsigned(0)
>>>>   if length == 0:
>>>>     return '""'
>>>> 
>>>> As a special case - if the length is zero, just return an empty string. I am not going to go in detail over all the possible checks here (hint: what if str_data’s value is zero?)
>>>> 
>>>> Now let’s grab the bytes - we want length char16_t at the location pointed to by our str_data:
>>>>   data = str_data.GetPointeeData(0,length)
>>>> 
>>>> And now let’s grab a Python string out of those bytes:
>>>>   error = lldb.SBError()
>>>>   bytes = data.ReadRawData(error,0,2*length)
>>>> 
>>>> The 2*length argument is carefully crafted to ensure we get all the bytes we need - it of course depends on sizeof(char16_t) == 2
>>>> 
>>>> Python is pretty good at string management, all we have to do now is tell it to turn our string from UTF16 into UTF8:
>>>>   return '"%s"' % (bytes.decode('utf-16').encode('utf-8'))
>>>> 
>>>> You’re done. To add the summary automatically to LLDB whenever you command script import the python file with the formatter:
>>>> def __lldb_init_module(debugger,*rest):
>>>>   summary = lldb.SBTypeSummary.CreateWithFunctionName("qstring.utf16string_summary")
>>>>   summary.SetOptions(lldb.eTypeOptionHideChildren)
>>>>   debugger.GetDefaultCategory().AddTypeSummary(lldb.SBTypeNameSpecifier("UTF16String",False),summary)
>>>> 
>>>> This is what you get with the summary enabled:
>>>> (UTF16String) string = "Just some data in UTF16 here”
>>>> 
>>>> Find the C++ and the Python parts of the example attached for reference - and of course feel free to ping back with any additional questions
>>>> 
>>>> 
>>>> 
>>>> 
>>>> - Enrico
>>>> 📩 egranata@.com ☎️ 27683
>>> 
>>> 
>>> 
>>> -- 
>>> Eran Ifrah
>>> Author of codelite, a cross platform open source C/C++ IDE: http://www.codelite.org
>>> wxCrafter, a wxWidgets RAD: http://wxcrafter.codelite.org
>> 
>> 
>> - Enrico
>> 📩 egranata@.com ☎️ 27683
> 
> 
> 
> -- 
> Eran Ifrah
> Author of codelite, a cross platform open source C/C++ IDE: http://www.codelite.org
> wxCrafter, a wxWidgets RAD: http://wxcrafter.codelite.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20140429/9e4ca61f/attachment.html>


More information about the lldb-dev mailing list