Debug Qt app with Xcode 11 and DataFormatter

Viewed 811

To see content of QString/QList etc. while debugging a Qt app in Xcode I set the lldbinit-Xcode to use the QtCreator's lldbbridge.py. With previous Xcode version this worked.

Using

  • Xcode 11.4
  • Qt 5.14.2 or 5.9.9
  • Qt Creator 4.11.2
  • macOS 10.15.4

lldbinit-Xcode content:

command script import /Development/Qt/Qt\ Creator.app/Contents/Resources/debugger/lldbbridge.py

Set the 'LLDB Init File' in the projects Scheme to the 'lldbinit-Xcode' shows:

error: type summary add takes one or more args.
  File "<string>", line 1
    <class.__doc__
    ^
SyntaxError: invalid syntax
warning: The provided class does not exist - please define it before attempting to use this synthetic provider
@
lldbversion="lldb-1103.0.22.4
Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)"@
@
state="enginesetupok"@
error: type summary add takes one or more args.
  File "<string>", line 1
    <class.__doc__
    ^
SyntaxError: invalid syntax
warning: The provided class does not exist - please define it before attempting to use this synthetic provider
Qt summary provider requires the pygdbmi module, please install using 'sudo /usr/bin/easy_install pygdbmi', and then restart Xcode.

In Qt Creator 4.10.0 there was a problem with lldbbridge.py and lldb's python version, but this has been fixed (https://bugreports.qt.io/browse/QTCREATORBUG-22955).

Setting the lldb python version to 2 (defaults write com.apple.dt.lldb DefaultPythonVersion 2) does not show any error but also no QString contents.

Do I also have to easy_install pygdbmi? But in which python to which install location?

3 Answers

Yes, you need to install pygdbmi:

pip3 install pygdbmi

For some reason, it cannot be found in standard locations, so just copy it from site-packages to the debuggers folder where lldbbridge.py resides.

Then, switch lldb back to Python3:

defaults write com.apple.dt.lldb DefaultPythonVersion 3

Finally, since lldbbridge.py is based on Python2, it is needed to patch it a little to work correctly:

@@ -2214,7 +2214,7 @@
 
             if encoding in text_encodings:
                 try:
-                    binaryvalue = summaryValue.decode('hex')
+                    binaryvalue = bytearray.fromhex(summaryValue)
                     # LLDB expects UTF-8
                     return "\"%s\"" % (binaryvalue.decode(encoding).encode('utf8'))
                 except:

Restart Xcode, and Qt debugging helpers should work now (tested with Xcode 12.3 on macOS 10.15.7).

This is not a new answer per se, but an update to those that precede it. denis.gz suggests that perhaps there's a bug in Xcode or its lldb whereby its python interpreter cannot find the module pygdbmi. This is sort of my experience as well, because it didn't work for me either, but the folks who answered this question Why does "pip install" inside Python raise a SyntaxError? had some interesting ideas about how to run pip from within a python interpreter. Why would you want to do this, especially when you would have to quit that session anyway to import the installed module? Because the Xcode lldb Python interpreter is an entire environment onto itself, with its own sys.path, pointing entirely within the Xcode.app package. That's why it can't "see" your installed version of pygdbmi. One can argue about whether this is a bug, but it begs for some solutions, because pygdbmi is required by Qt's lldbbridge.py tool. I'm sure denis.gz's suggestion of copying the installed module from where pip installed it to within the contents of the Qt Creator.app bundle would work, but that didn't sit too well with me, so I looked for other solutions. When lldb is active within an Xcode debug session, you can fire up an interactive interpreter, exactly like the experience of using python from the command line, by using the lldb command script. You are then running a python interpreter within Xcode's lldb python environment. Using the solution that was suggested by the answerer of the above SO question, you can install directly into this environment by the following sequence within the lldb python interpreter -

import pip._internal as pip
pip.main([ "install", "pygdbmi" ])

Now pygdbmi is tucked away within the Xcode's bundle and is accessible to all lldb python. Now that's cool and all, but it's really not much better than copying the installed pygdbmi into Qt Creator.app's bundle. If you were to update your Xcode (and how often does THAT happen?), this fix would probably not stick any more than when updating Qt Creator. So something I thought of after (but haven't tried yet), is the following: you can run a script in lldb python from the lldb command line with something like the following that you could put in your .lldbinit file -

script -l python -- sys.path += [ "directory/with/your/modules" ]

The path given will be outside of the Xcode bundle, and Xcode might nix this, but there's a setting for Xcode that says something about relaxing the "sanitary" build system model and allowing stuff from the larger environment in. If you were to use that, I feel confident the above would do the trick in the best way possible.

Edit:

Towards the end, I suggested a third path available by updating your .lldbinit file to augment the sys.path variable. I tried it, and it worked. First a correction from above. It is not wrong, but is unnecessary to import the pip in the way described above (import pip._internal as pip) to get access to the main method. It is sufficient to simply import pip. The single line I added to my .lldbinit file was -

script -l python -- import sys; sys.path += [ "/usr/local/lib/python3.9/site-packages" ]

After that I chose a module that had not been installed in Xcode's environment, but that had been in my larger environment in site-packages - mercurial for a random example. Back in Xcode, I started the script interpreter and was able to import mercurial without a complaint and without any special settings to "warn" Xcode that it's pristine environment was being opened up, so I just wanted to close that loop, having thrown it out there without a working example.

I have read in other answers while researching this, that there can be a per-scheme lldb init file specified somewhere in the scheme settings. I leave finding that as an exercise for the reader, but that might be a good place to play with these options as well.

Thanks to @cycollins I found a working solution. In the meantime I'm using

  • Xcode 12.5
  • Qt 5.15.2
  • Qt Creator 5.0
  • macOS 11.6

The .lldbinit-Xcode

# pygdbmi isn't part of Xcodes/lldb/python config. Import it from an existing path
# make sure to have a similar python version for Xcode:
# script import sys; print(sys.version); print(sys.path)
# install pygdbmi for this installation: /usr/local/Cellar/python@3.8/3.8.12/bin/pip3 install pygdbmi
script import sys; sys.path += [ "/usr/local/lib/python3.8/site-packages" ]

# ATTENTION, disables automatic loading of Python modules for DSYM!
settings set target.load-script-from-symbol-file false
command script import "/usr/local/Trolltech/Qt/Qt Creator.app/Contents/Resources/debugger/lldbbridge.py"
# requires 'defaults write com.apple.dt.lldb DefaultPythonVersion 3'

/usr/local/Trolltech/Qt/Qt Creator.app/Contents/Resources/debugger/lldbbridge.py has an output issue for QString (https://forum.qt.io/topic/118520/debugger-lldbbridge-py-used-with-xcode-11-6-issues-from-qt-creator-4-13). Add import codecs at the beginning and apply this modification:

            if encoding in text_encodings:
                try:
-                    binaryvalue = summaryValue.decode('hex')
-                    # LLDB expects UTF-8
-                    return "\"%s\"" % (binaryvalue.decode(encoding).encode('utf8'))
+                    binaryvalue = bytes.fromhex(summaryValue)
+                    return "\"%s\"" % (codecs.decode(binaryvalue, encoding))
                except:
                    return "<failed to decode '%s' as '%s'>" % (summaryValue, encoding)

At least it is working again for QString, QVector, QList, QVariant. Still not working for QVector, QMap, QVariantList, etc.

Related