[lldb-dev] Using data formatters to display QString

Enrico Granata egranata at apple.com
Tue Apr 29 10:15:53 PDT 2014


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?

> 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? 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)

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?
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?

> 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



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20140429/ea18f7c9/attachment.html>


More information about the lldb-dev mailing list