Jump to content

Guidelines and HOWTOs/Debugging/Debugging symbols: Difference between revisions

From KDE Community Wiki
Jriddell (talk | contribs)
Example app: fix ls command
Frinring (talk | contribs)
m Remove explicit use of ECM_KDE_MODULE_DIR, is part of ECM_MODULE_PATH
 
(15 intermediate revisions by 6 users not shown)
Line 1: Line 1:
= Overview =
== Overview ==
Debugging symbols allow you to debug your application better. They are added to your binary by the compiler. For KDE, you have to decide during the cmake step if you want debugging symbols or not. To compile your application with debugging symbols, use
Debugging symbols allow you to debug your application better. They are added to your binary by the compiler. For KDE software, you have to decide during the cmake step if you want debugging symbols or not. To compile your application with debugging symbols, use
cmake . -DCMAKE_BUILD_TYPE=debugfull
<pre>
cmake . -DCMAKE_BUILD_TYPE=debug
</pre>
to compile it without debugging symbols, use
to compile it without debugging symbols, use
cmake . -DCMAKE_BUILD_TYPE=release
<pre>
Depending on your decision, output generated with the command kDebug will also be (debugfull) or not be (release) added to your application.
cmake . -DCMAKE_BUILD_TYPE=release
</pre>
Depending on your decision, output generated with the command kDebug will also be (debug) or not be (release) added to your application.


= Example app =
== Example app ==
As an example, let's write an application that crashes:
As an example, let's write an application that crashes:


'''main.cpp'''
'''main.cpp'''
 
<syntaxhighlight lang="cpp-qt">
<pre>
#include <QApplication>
#include <KApplication>
#include <QCommandLineParser>
#include <KAboutData>
#include <KAboutData>
#include <KCmdLineArgs>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageBox>
#include <iostream>
#include <iostream>
using namespace std;


int main (int argc, char *argv[])
int main (int argc, char *argv[])
{
{
     KAboutData aboutData( "tutorial1", 0, ki18n("Tutorial 1"), "1.0",
     QApplication app(argc, argv);
                          ki18n("Displays a KMessageBox popup"),
    KLocalizedString::setApplicationDomain("tutorial1");
                          KAboutData::License_GPL,
   
                          ki18n("(c) 2009"), ki18n("Some text..."),
                          "http://tutorial.com/",
                          "[email protected]");


     KCmdLineArgs::init( argc, argv, &aboutData );
     KAboutData aboutData(
     KApplication app;
                        // The program name used internally. (componentName)
                        QStringLiteral("tutorial1"),
                        // A displayable program name string. (displayName)
                        i18n("Tutorial 1"),
                        // The program version string. (version)
                        QStringLiteral("1.0"),
                        // Short description of what the app does. (shortDescription)
                        i18n("Displays a KMessageBox popup"),
                        // The license this code is released under
                        KAboutLicense::GPL,
                        // Copyright Statement (copyrightStatement = QString())
                        i18n("(c) 2015"),
                        // Optional text shown in the About box.
                        // Can contain any information desired. (otherText)
                        i18n("Some text..."),
                        // The program homepage string. (homePageAddress = QString())
                        QStringLiteral("http://example.com/"),
                        // The bug report email address
                        // (bugsEmailAddress = QLatin1String("[email protected]")
                        QStringLiteral("[email protected]"));
 
    aboutData.addAuthor(i18n("Name"), i18n("Task"), QStringLiteral("[email protected]"),
                        QStringLiteral("http://your.website.com"), QStringLiteral("OSC Username"));
 
    KAboutData::setApplicationData(aboutData);
 
    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);
    parser.process(app);
    aboutData.processCommandLine(&parser);
      
    KGuiItem yesButton(i18n("Hello"), QString(),
                        i18n("This is a tooltip"),
                        i18n("This is a WhatsThis help text."));


    KMessageBox::questionYesNo( 0, i18n( "Hello World" ) );
     int* i;
     int* i;
     cout << "i is at " << i << " value " << *i << endl;
     std::cout << "i is at " << i << " value " << *i << std::endl;
     i=(int*)0x0;
     i=(int*)0x0;
     cout << "i is at " << i << " value " << *i << endl;
     std::cout << "i is at " << i << " value " << *i << std::endl;
   
    return KMessageBox::questionYesNo(0, i18n("Hello World"), i18n("Hello"), yesButton)
        == KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE;
}
</syntaxhighlight>


    return 0;
}
</pre>
'''CMakeLists.txt'''
'''CMakeLists.txt'''
<pre>
<pre>
cmake_minimum_required(VERSION 3.0)
project (tutorial1)
project (tutorial1)
find_package(KDE4 REQUIRED)
 
include (KDE4Defaults)
set(QT_MIN_VERSION "5.3.0")
include_directories(${KDE4_INCLUDES})
set(KF5_MIN_VERSION "5.2.0")
 
find_package(ECM 1.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
 
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)
 
# Find Qt modules
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
    Core    # QCommandLineParser, QStringLiteral
    Widgets # QApplication
)
 
# Find KDE modules
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
    CoreAddons      # KAboutData
    I18n            # KLocalizedString
    WidgetsAddons  # KMessageBox
)
 
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
 
set(tutorial1_SRCS main.cpp)
set(tutorial1_SRCS main.cpp)
kde4_add_executable(tutorial1 ${tutorial1_SRCS})
add_executable(tutorial1 ${tutorial1_SRCS})
target_link_libraries(tutorial1 ${KDE4_KDEUI_LIBS})
target_link_libraries(tutorial1
install(TARGETS tutorial1 ${INSTALL_TARGETS_DEFAULT_ARGS})
    Qt5::Widgets
    KF5::CoreAddons
    KF5::I18n
    KF5::WidgetsAddons
)
 
install(TARGETS tutorial1 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
</pre>
</pre>
Now let's compile this without debugging symbols:
Now let's compile this without debugging symbols:
cmake . -DCMAKE_BUILD_TYPE=release && make -j4
<pre>
cmake . -DCMAKE_BUILD_TYPE=release && make
</pre>
We see that the resulting file is small:
We see that the resulting file is small:
# ls -lh tutorial1
<pre>
-rwxr-xr-x 1 user user 15K Jul 11 18:07 tutorial1
$ ls -lh tutorial1
-rwxrwxr-x 1 user user 23K abr 18 10:36 tutorial1
</pre>
With debugging symbols, the file is bigger:
With debugging symbols, the file is bigger:
cmake . -DCMAKE_BUILD_TYPE=debugfull && make
<pre>
# ls -lh tutorial1
cmake . -DCMAKE_BUILD_TYPE=debug && make
-rwxr-xr-x 1 root root 250K Jul 11 18:09 tutorial1
$ ls -lh tutorial1
-rwxrwxr-x 1 user user 577K abr 18 10:37 tutorial1
</pre>


= Backtraces =
== Backtraces ==
Now let's start the application and look at the backtrace:
Now let's start the application and look at the backtrace.
cmake . -DCMAKE_BUILD_TYPE=release && make -j4
The execution of
Gives you the backtrace
<pre>
Application: Tutorial 1 (tutorial1), signal SIGSEGV
cmake . -DCMAKE_BUILD_TYPE=release && make
[?1034h[Thread debugging using libthread_db enabled]
./tutorial1
  0x00007f58abba4cb0 in nanosleep () from /lib64/libc.so.6
coredumpctl gdb
[Current thread is 1 (Thread 0x7f58b0cfd750 (LWP 21264))]
</pre>
gives you that backtrace:
<pre>
[...]
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000055f0ad8d660f in main ()
[Current thread is 1 (Thread 0x7f4cb1c78780 (LWP 4101741))]
(gdb)
</pre>
You can type "quit" and press return in order to exit gdb.


Thread 1 (Thread 0x7f58b0cfd750 (LWP 21264)):
The debugging build and its execution:
[KCrash Handler]
<pre>
'''#5  0x00000000004016aa in main ()'''
cmake . -DCMAKE_BUILD_TYPE=debug && make
The debugging build
./tutorial1
cmake . -DCMAKE_BUILD_TYPE=debugfull && make -j4
coredumpctl gdb
Gives you the backtrace
</pre>
Application: Tutorial 1 (tutorial1), signal SIGSEGV
gives you that backtrace:
  �[?1034h[Thread debugging using libthread_db enabled]
<pre>
0x00007fd0b8161cb0 in nanosleep () from /lib64/libc.so.6
Program terminated with signal SIGSEGV, Segmentation fault.
[Current thread is 1 (Thread 0x7fd0bd2ba750 (LWP 21327))]
#0 0x000055c02d7a7b1b in main (argc=1, argv=0x7ffd33ca4768)
 
    at /home/user/test/main.cpp:53
Thread 1 (Thread 0x7fd0bd2ba750 (LWP 21327)):
53          std::cout << "i is at " << i << " value " << *i << std::endl;
[KCrash Handler]
[Current thread is 1 (Thread 0x7fe564d93780 (LWP 4111905))]
'''#5  0x0000000000401625 in main (argc=1, argv=0x7fffc52f5138) at /root/kdehello/main.cpp:25'''
(gdb)
So you see: with debugging symbols, you see the line number where the crash occurred. Without, you do not see this.
</pre>
So you see: with debugging symbols, you see the line number and the line where the crash occurred. Without debugging symbols, you do not see that information.


= Where are they? =
== Where are they? ==
Where are the debugging symbols stored? Use objdump -g to find out:
Where are the debugging symbols stored? Use objdump -g to find out:
# objdump -g tutorial1-release | wc -l
<pre>
511
$ objdump -g tutorial1-release | wc -l
# objdump -g tutorial1-debugfull | wc -l
164
40943
$ objdump -g tutorial1-debug | wc -l
148199
</pre>
It is important to know that the code lines (in assembler) to be executed actually do not differ a lot. We see this when disassembling the code:
It is important to know that the code lines (in assembler) to be executed actually do not differ a lot. We see this when disassembling the code:
# objdump -d tutorial1-debugfull | wc -l
<pre>
658
$ objdump -d tutorial1-debug | wc -l
# objdump -d tutorial1-release | wc -l
1325
697
$ objdump -d tutorial1-release | wc -l
601
</pre>
This gives us hope that there will be no major speed difference between a debug- and a release-version of a binary.
This gives us hope that there will be no major speed difference between a debug- and a release-version of a binary.


= Speed implications =
== Speed implications ==
We remove the lines that cause the crash and the messagebox. Then we execute the program 100 times:
Now we have to remove the lines that cause the crash and the messagebox. After that, we can execute the program 100 times that way:
cmake . -DCMAKE_BUILD_TYPE=debugfull && make -j4
<pre>
time for i in $(seq 1 1 100); do ./tutorial1; done
cmake . -DCMAKE_BUILD_TYPE=debug && make -j4
time for i in $(seq 1 1 100); do ./tutorial1; done


real    0m6.201s
real    0m6.201s
user    0m4.368s
user    0m4.368s
sys    0m1.320s
sys    0m1.320s
Lasts 6 seconds. Now with the release version:
</pre>
cmake . -DCMAKE_BUILD_TYPE=release && make -j4
It lasts 6 seconds. Now with the release version:
time for i in $(seq 1 1 100); do ./tutorial1; done
<pre>
cmake . -DCMAKE_BUILD_TYPE=release && make -j4
time for i in $(seq 1 1 100); do ./tutorial1; done


real    0m6.259s
real    0m6.259s
user    0m4.368s
user    0m4.368s
sys    0m1.328s
sys    0m1.328s
Also lasts 6 seconds. So the main difference is in the binaries size.
</pre>
It also lasts 6 seconds. So the main difference is in the binaries size.


= And make? =
== And make? ==
How does cmake propagate to make if a debug version is wanted? Do a
How does <code>cmake</code> propagate to <code>make</code> if a debug version is wanted? Do a
cmake . -DCMAKE_BUILD_TYPE=debugfull && make VERBOSE=1
<pre>
cmake . -DCMAKE_BUILD_TYPE=debug && make VERBOSE=1
</pre>
You will find a difference during the link step. The parameters
You will find a difference during the link step. The parameters
-DNDEBUG -DQT_NO_DEBUG
<pre>
are unique for the release-version. There are further differences like the O2 optimization.
-DNDEBUG -DQT_NO_DEBUG
</pre>
are unique for the release version. There are further differences like the <code>O3</code> optimization.

Latest revision as of 17:54, 6 September 2022

Overview

Debugging symbols allow you to debug your application better. They are added to your binary by the compiler. For KDE software, you have to decide during the cmake step if you want debugging symbols or not. To compile your application with debugging symbols, use

cmake . -DCMAKE_BUILD_TYPE=debug

to compile it without debugging symbols, use

cmake . -DCMAKE_BUILD_TYPE=release

Depending on your decision, output generated with the command kDebug will also be (debug) or not be (release) added to your application.

Example app

As an example, let's write an application that crashes:

main.cpp

#include <QApplication>
#include <QCommandLineParser>
#include <KAboutData>
#include <KLocalizedString>
#include <KMessageBox>
#include <iostream>

int main (int argc, char *argv[])
{
    QApplication app(argc, argv);
    KLocalizedString::setApplicationDomain("tutorial1");
    

    KAboutData aboutData(
                         // The program name used internally. (componentName)
                         QStringLiteral("tutorial1"),
                         // A displayable program name string. (displayName)
                         i18n("Tutorial 1"),
                         // The program version string. (version)
                         QStringLiteral("1.0"),
                         // Short description of what the app does. (shortDescription)
                         i18n("Displays a KMessageBox popup"),
                         // The license this code is released under
                         KAboutLicense::GPL,
                         // Copyright Statement (copyrightStatement = QString())
                         i18n("(c) 2015"),
                         // Optional text shown in the About box.
                         // Can contain any information desired. (otherText)
                         i18n("Some text..."),
                         // The program homepage string. (homePageAddress = QString())
                         QStringLiteral("http://example.com/"),
                         // The bug report email address
                         // (bugsEmailAddress = QLatin1String("[email protected]")
                         QStringLiteral("[email protected]"));

    aboutData.addAuthor(i18n("Name"), i18n("Task"), QStringLiteral("[email protected]"),
                         QStringLiteral("http://your.website.com"), QStringLiteral("OSC Username"));

    KAboutData::setApplicationData(aboutData);

    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);
    parser.process(app);
    aboutData.processCommandLine(&parser);
    
    KGuiItem yesButton(i18n("Hello"), QString(),
                        i18n("This is a tooltip"),
                        i18n("This is a WhatsThis help text."));

    int* i;
    std::cout << "i is at " << i << " value " << *i << std::endl;
    i=(int*)0x0;
    std::cout << "i is at " << i << " value " << *i << std::endl;
    
    return KMessageBox::questionYesNo(0, i18n("Hello World"), i18n("Hello"), yesButton) 
        == KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

project (tutorial1)

set(QT_MIN_VERSION "5.3.0")
set(KF5_MIN_VERSION "5.2.0")

find_package(ECM 1.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)

# Find Qt modules
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
    Core    # QCommandLineParser, QStringLiteral
    Widgets # QApplication
)

# Find KDE modules
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
    CoreAddons      # KAboutData
    I18n            # KLocalizedString
    WidgetsAddons   # KMessageBox
)

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

set(tutorial1_SRCS main.cpp)
add_executable(tutorial1 ${tutorial1_SRCS})
target_link_libraries(tutorial1
    Qt5::Widgets
    KF5::CoreAddons
    KF5::I18n
    KF5::WidgetsAddons
)

install(TARGETS tutorial1 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

Now let's compile this without debugging symbols:

cmake . -DCMAKE_BUILD_TYPE=release && make

We see that the resulting file is small:

$ ls -lh tutorial1
-rwxrwxr-x 1 user user 23K abr 18 10:36 tutorial1

With debugging symbols, the file is bigger:

cmake . -DCMAKE_BUILD_TYPE=debug && make
$ ls -lh tutorial1
-rwxrwxr-x 1 user user 577K abr 18 10:37 tutorial1

Backtraces

Now let's start the application and look at the backtrace. The execution of

cmake . -DCMAKE_BUILD_TYPE=release && make
./tutorial1
coredumpctl gdb

gives you that backtrace:

[...]
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055f0ad8d660f in main ()
[Current thread is 1 (Thread 0x7f4cb1c78780 (LWP 4101741))]
(gdb) 

You can type "quit" and press return in order to exit gdb.

The debugging build and its execution:

cmake . -DCMAKE_BUILD_TYPE=debug && make
./tutorial1
coredumpctl gdb

gives you that backtrace:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055c02d7a7b1b in main (argc=1, argv=0x7ffd33ca4768)
    at /home/user/test/main.cpp:53
53          std::cout << "i is at " << i << " value " << *i << std::endl;
[Current thread is 1 (Thread 0x7fe564d93780 (LWP 4111905))]
(gdb)

So you see: with debugging symbols, you see the line number and the line where the crash occurred. Without debugging symbols, you do not see that information.

Where are they?

Where are the debugging symbols stored? Use objdump -g to find out:

$ objdump -g tutorial1-release | wc -l
164
$ objdump -g tutorial1-debug | wc -l
148199

It is important to know that the code lines (in assembler) to be executed actually do not differ a lot. We see this when disassembling the code:

$ objdump -d tutorial1-debug | wc -l
1325
$ objdump -d tutorial1-release | wc -l
601

This gives us hope that there will be no major speed difference between a debug- and a release-version of a binary.

Speed implications

Now we have to remove the lines that cause the crash and the messagebox. After that, we can execute the program 100 times that way:

cmake . -DCMAKE_BUILD_TYPE=debug && make -j4
time for i in $(seq 1 1 100); do ./tutorial1; done

real    0m6.201s
user    0m4.368s
sys     0m1.320s

It lasts 6 seconds. Now with the release version:

cmake . -DCMAKE_BUILD_TYPE=release && make -j4
time for i in $(seq 1 1 100); do ./tutorial1; done

real    0m6.259s
user    0m4.368s
sys     0m1.328s

It also lasts 6 seconds. So the main difference is in the binaries size.

And make?

How does cmake propagate to make if a debug version is wanted? Do a

cmake . -DCMAKE_BUILD_TYPE=debug && make VERBOSE=1

You will find a difference during the link step. The parameters

-DNDEBUG -DQT_NO_DEBUG

are unique for the release version. There are further differences like the O3 optimization.