In Xcode, how to debug with external libraries that you have the source for?
Asked Answered
L

2

6

I have a large-ish C/C++/Objective-C project building for OS X in Xcode. The project links to pre-built Qt5 libraries.

That all works very nicely, until something crashes and I get a stack trace with Qt functions in it. If I click on the stack frame for one of the Qt functions, Xcode/lldb displays assembly rather than source - I'm using Qt as an external library, so I don't have any of the Qt source in my project. How can I fix this?

I've tried adding the Qt5 source to the project without adding it to my executable target, but Xcode/lldb still doesn't 'see' the source or figure out that the source files that I added to the project are the same source files referenced in the Qt debug symbols.

How do I tell Xcode/lldb where to find the source for an external library that I'm working with?

EDIT:

Just to add a bit more detail here, when I type 'target modules lookup -t QMenuBar' in the Xcode/lldb console, this is what I see:

Best match found in /Users/ted/Documents/Projects/XXX/_build_osx/Output/Debug/XXX.app/Contents/MacOS/XXX:
id = {0x7100042d49}, name = "QMenuBar", byte-size = 48, decl = qmenubar.h:57, clang_type = "class QMenuBar : public QWidget {
    static const QMetaObject staticMetaObject;
    virtual const QMetaObject *metaObject() const;
    virtual void *qt_metacast(const char *);
    static QString tr(const char *, const char *, int);
    static QString trUtf8(const char *, const char *, int);
    virtual int qt_metacall(QMetaObject::Call, int, void **);
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
    explicit QMenuBar(QWidget *);
    virtual void ~QMenuBar();
    QAction *addAction(const QString &);
    QAction *addAction(const QString &, const QObject *, const char *);
    QAction *addMenu(QMenu *);
    QMenu *addMenu(const QString &);
    QMenu *addMenu(const QIcon &, const QString &);
    QAction *addSeparator();
    QAction *insertSeparator(QAction *);
    QAction *insertMenu(QAction *, QMenu *);
    void clear();
    QAction *activeAction() const;
    void setActiveAction(QAction *);
    void setDefaultUp(bool);
    bool isDefaultUp() const;
    virtual QSize sizeHint() const;
    virtual QSize minimumSizeHint() const;
    virtual int heightForWidth(int) const;
    QRect actionGeometry(QAction *) const;
    QAction *actionAt(const QPoint &) const;
    void setCornerWidget(QWidget *, Qt::Corner);
    QWidget *cornerWidget(Qt::Corner) const;
    NSMenu *toNSMenu();
    bool isNativeMenuBar() const;
    void setNativeMenuBar(bool);
    QPlatformMenuBar *platformMenuBar();
    virtual void setVisible(bool);
    void triggered(QAction *);
    void hovered(QAction *);
    virtual void changeEvent(QEvent *);
    virtual void keyPressEvent(QKeyEvent *);
    virtual void mouseReleaseEvent(QMouseEvent *);
    virtual void mousePressEvent(QMouseEvent *);
    virtual void mouseMoveEvent(QMouseEvent *);
    virtual void leaveEvent(QEvent *);
    virtual void paintEvent(QPaintEvent *);
    virtual void resizeEvent(QResizeEvent *);
    virtual void actionEvent(QActionEvent *);
    virtual void focusOutEvent(QFocusEvent *);
    virtual void focusInEvent(QFocusEvent *);
    virtual void timerEvent(QTimerEvent *);
    virtual bool eventFilter(QObject *, QEvent *);
    virtual bool event(QEvent *);
    void initStyleOption(QStyleOptionMenuItem *, const QAction *) const;
    QMenuBarPrivate *d_func();
    const QMenuBarPrivate *d_func() const;
    QMenuBar(const QMenuBar &);
    QMenuBar &operator=(const QMenuBar &);
}"

Clearly my executable has some sort of symbols in it. It's saying that this definition came from qmenubar.h. I have qmenubar.h on my hard drive somewhere - how do I tell Xcode/lldb where to find it?

In Visual Studio in Windows, if I click on a frame in the stack trace that doesn't have source that Visual Studio can easily find, VS pops up a window asking me to browse for the source file - from then on, Visual Studio seems to intuit where the rest of the source is based on the location of the source you browsed for. For example, if I had a stack trace with QMenuBar::focusInEvent() in it, and I clicked on it, Visual Studio would ask me where qmenubar.cpp is. I could browse to C:\Users\ted\Downloads\qt5-everywhere-src-5.3.2\qtcore\src\qmenubar.cpp (or wherever it is) and Visual Studio then assumes that other sources might be located nearby.

How does this work with Xcode?

Lifelike answered 6/4, 2015 at 18:27 Comment(3)
Just a guess, can it be because you link with qt release libs and not the corresponding debug version? I found something here: qtcentre.org/threads/… Seems you need CONFIG += debug in your .pro file. Also, related: forum.qt.io/topic/24972/…Callipygian
Nope - using the debug libs. I actually have debug symbols in both my debug and release Qt builds. Also, I'm not using qmake - this is a native Xcode project. This is an issue that anyone would have with a library that you're linking to but that you still have source for. We also use google protocol buffers, boost, etc, and I have the same problem with those.Lifelike
Possibly related how to match debugging symbols to sourceSip
L
2

I think I've found the answer to my question, but it actually just leads to more questions. See Xcode equivalent of Visual Studio's "Find Source".

I'm thinking that perhaps my program or my build of Qt doesn't have symbols, or fewer symbols than is necessary. In particular, here's the output of target modules lookup --address <address> --verbose (image is a lldb synonym for target modules) in that linked SO question:

  (lldb) image lookup -va main
    Address: hello[0x0000000100000f40] (hello.__TEXT.__text + 0)
    Summary: hello`main at hello.c:5
     Module: file = "/private/tmp/hello", arch = "x86_64"
CompileUnit: id = {0x00000000}, file = "/tmp/hello.c", language = "ISO C:1999"
   Function: id = {0x00000026}, name = "main", range = [0x0000000100000f40-0x0000000100000f6d)
   FuncType: id = {0x00000026}, decl = hello.c:4, clang_type = "int (void)"
     Blocks: id = {0x00000026}, range = [0x100000f40-0x100000f6d)
  LineEntry: [0x0000000100000f40-0x0000000100000f56): /tmp/hello.c:5
     Symbol: id = {0x00000004}, range = [0x0000000100000f40-0x0000000100000f6d), name="main"

here's an example from the LLVM webpage on "The LLDB Debugger" (http://lldb.llvm.org/symbolication.html):

>(lldb) image lookup --address 0x100123aa3 --verbose
      Address: a.out[0x0000000100000aa3] (a.out.__TEXT.__text + 110)
      Summary: a.out`main + 50 at main.c:13
       Module: file = "/tmp/a.out", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "/tmp/main.c", language = "ISO C:1999"
     Function: id = {0x0000004f}, name = "main", range = [0x0000000100000bc0-0x0000000100000dc9)
     FuncType: id = {0x0000004f}, decl = main.c:9, clang_type = "int (int, const char **, const char **, const char **)"
       Blocks: id = {0x0000004f}, range = [0x100000bc0-0x100000dc9)
               id = {0x000000ae}, range = [0x100000bf2-0x100000dc4)
    LineEntry: [0x0000000100000bf2-0x0000000100000bfa): /tmp/main.c:13:23
       Symbol: id = {0x00000004}, range = [0x0000000100000bc0-0x0000000100000dc9), name="main"
     Variable: id = {0x000000bf}, name = "path", type= "char [1024]", location = DW_OP_fbreg(-1072), decl = main.c:28
     Variable: id = {0x00000072}, name = "argc", type= "int", location = r13, decl = main.c:8
     Variable: id = {0x00000081}, name = "argv", type= "const char **", location = r12, decl = main.c:8
     Variable: id = {0x00000090}, name = "envp", type= "const char **", location = r15, decl = main.c:8
     Variable: id = {0x0000009f}, name = "aapl", type= "const char **", location = rbx, decl = main.c:8

and here's the output of my Qt5Widgets QMenuBar address:

(lldb) image lookup --address 0x103058d57 --verbose
      Address: libQt5Widgets_debug.5.dylib[0x000000000026dd57] (libQt5Widgets_debug.5.dylib.__TEXT.__text + 2525127)
      Summary: libQt5Widgets_debug.5.dylib`QMenuBar::actionEvent(QActionEvent*) + 711
       Module: file = "/Users/ted/Documents/Projects/work/third_party/qt/5.3.2/osx/x86_64/lib/libQt5Widgets_debug.5.dylib", arch = "x86_64"
       Symbol: id = {0x0000c190}, range = [0x0000000103058a90-0x0000000103059260), name="QMenuBar::actionEvent(QActionEvent*)", mangled="_ZN8QMenuBar11actionEventEP12QActionEvent"

Ack! Where's the source file (CompileUnit) reference? Thanks to @vsoftco for the suggestion and anyone else who saw this question. I'm going to have to dig a bit into the Qt5 build system and figure out where those symbols went.

Lifelike answered 7/4, 2015 at 0:34 Comment(0)
G
0

I had a similar problem trying to step into Qt sources. My project was using a pre-built, stripped binary that didn't include sources.

The way I solved this issue was to build the Qt-everywhere sources from here (remember to configure with the debug flag) on my machine and point my project to those binaries instead by inserting their location into my $PATH so CMake could find them. Xcode / lldb was then able to step into the sources for debugging.

>(lldb) image lookup -vn QWidget::showFullScreen
1 match found in /path/to/framework/libQt6Widgets_debug.6.7.0.dylib:
        Address: libQt6Widgets_debug.6.7.0.dylib[0x00000000000b0dc4] (libQt6Widgets_debug.6.7.0.dylib.__TEXT.__text + 700576)
        Summary: libQt6Widgets_debug.6.7.0.dylib`QWidget::showFullScreen() at qwidget.cpp:3033
         Module: file = "/path/to/framework/libQt6Widgets_debug.6.7.0.dylib", arch = "arm64"
    CompileUnit: id = {0x00000000}, file = "/path/to/sources/qt-everywhere-src-6.7.0/qtbase/src/widgets/kernel/qwidget.cpp", language = "c++14"
       Function: id = {0x120012e0cf}, name = "QWidget::showFullScreen()", mangled = "_ZN7QWidget14showFullScreenEv", range = [0x0000000107a90dc4-0x0000000107a90e84)
       FuncType: id = {0x120012e0cf}, byte-size = 0, decl = qwidget.h:489, compiler_type = "void (void)"
         Blocks: id = {0x120012e0cf}, range = [0x107a90dc4-0x107a90e84)
      LineEntry: [0x0000000107a90dc4-0x0000000107a90ddc): /path/to/sources/qt-everywhere-src-6.7.0/qtbase/src/widgets/kernel/qwidget.cpp:3033
         Symbol: id = {0x00014866}, range = [0x0000000107a90dc4-0x0000000107a90e84), name="QWidget::showFullScreen()", mangled="_ZN7QWidget14showFullScreenEv"
       Variable: id = {0x120012e0ea}, name = "this", type = "QWidget *", valid ranges = <block>, location = DW_OP_fbreg -8, decl = 

CompileUnit information is included when using this approach. It seems pre-built Qt binaries are stripped in some way.

Gpo answered 5/5 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.