Guidelines and HOWTOs/Debugging/Debugging symbols: Difference between revisions
m Fix syntax highlighting. |
m Remove explicit use of ECM_KDE_MODULE_DIR, is part of ECM_MODULE_PATH |
||
(11 intermediate revisions by 4 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 | ||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE= | cmake . -DCMAKE_BUILD_TYPE=debug | ||
</pre> | </pre> | ||
to compile it without debugging symbols, use | to compile it without debugging symbols, use | ||
Line 8: | Line 8: | ||
cmake . -DCMAKE_BUILD_TYPE=release | cmake . -DCMAKE_BUILD_TYPE=release | ||
</pre> | </pre> | ||
Depending on your decision, output generated with the command kDebug will also be ( | 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 == | ||
Line 15: | Line 15: | ||
'''main.cpp''' | '''main.cpp''' | ||
<syntaxhighlight lang="cpp-qt"> | <syntaxhighlight lang="cpp-qt"> | ||
#include < | #include <QApplication> | ||
#include <QCommandLineParser> | |||
#include <KAboutData> | #include <KAboutData> | ||
#include < | #include <KLocalizedString> | ||
#include <KMessageBox> | #include <KMessageBox> | ||
#include <iostream> | #include <iostream> | ||
int main (int argc, char *argv[]) | 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; | 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 0; | return KMessageBox::questionYesNo(0, i18n("Hello World"), i18n("Hello"), yesButton) | ||
== KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 47: | Line 76: | ||
'''CMakeLists.txt''' | '''CMakeLists.txt''' | ||
<pre> | <pre> | ||
cmake_minimum_required(VERSION 3.0) | |||
project (tutorial1) | project (tutorial1) | ||
find_package( | |||
include ( | 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) | set(tutorial1_SRCS main.cpp) | ||
add_executable(tutorial1 ${tutorial1_SRCS}) | |||
target_link_libraries(tutorial1 | target_link_libraries(tutorial1 | ||
install(TARGETS tutorial1 | 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: | ||
Line 62: | Line 123: | ||
We see that the resulting file is small: | We see that the resulting file is small: | ||
<pre> | <pre> | ||
$ ls -lh tutorial1 | |||
- | -rwxrwxr-x 1 user user 23K abr 18 10:36 tutorial1 | ||
</pre> | </pre> | ||
With debugging symbols, the file is bigger: | With debugging symbols, the file is bigger: | ||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE= | cmake . -DCMAKE_BUILD_TYPE=debug && make | ||
$ ls -lh tutorial1 | |||
- | -rwxrwxr-x 1 user user 577K abr 18 10:37 tutorial1 | ||
</pre> | </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. | ||
The execution of | |||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE=release && make | cmake . -DCMAKE_BUILD_TYPE=release && make | ||
./tutorial1 | |||
coredumpctl gdb | |||
</pre> | </pre> | ||
gives you that backtrace: | |||
<pre> | <pre> | ||
[...] | |||
Program terminated with signal SIGSEGV, Segmentation fault. | |||
#0 0x000055f0ad8d660f in main () | |||
[Current thread is 1 (Thread | [Current thread is 1 (Thread 0x7f4cb1c78780 (LWP 4101741))] | ||
(gdb) | |||
</pre> | |||
You can type "quit" and press return in order to exit gdb. | |||
The debugging build and its execution: | |||
The debugging build | |||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE= | cmake . -DCMAKE_BUILD_TYPE=debug && make | ||
./tutorial1 | |||
coredumpctl gdb | |||
</pre> | </pre> | ||
gives you that backtrace: | |||
<pre> | <pre> | ||
Program terminated with signal SIGSEGV, Segmentation fault. | |||
#0 0x000055c02d7a7b1b in main (argc=1, argv=0x7ffd33ca4768) | |||
at /home/user/test/main.cpp:53 | |||
[Current thread is 1 (Thread | 53 std::cout << "i is at " << i << " value " << *i << std::endl; | ||
[Current thread is 1 (Thread 0x7fe564d93780 (LWP 4111905))] | |||
(gdb) | |||
</pre> | </pre> | ||
So you see: with debugging symbols, you see the line number where the crash occurred. Without, you do not see | 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: | ||
<pre> | <pre> | ||
$ objdump -g tutorial1-release | wc -l | |||
164 | |||
$ objdump -g tutorial1-debug | wc -l | |||
148199 | |||
</pre> | </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: | ||
<pre> | <pre> | ||
$ objdump -d tutorial1-debug | wc -l | |||
1325 | |||
$ objdump -d tutorial1-release | wc -l | |||
601 | |||
</pre> | </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 == | ||
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: | |||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE= | cmake . -DCMAKE_BUILD_TYPE=debug && make -j4 | ||
time for i in $(seq 1 1 100); do ./tutorial1; done | time for i in $(seq 1 1 100); do ./tutorial1; done | ||
Line 132: | Line 195: | ||
sys 0m1.320s | sys 0m1.320s | ||
</pre> | </pre> | ||
It lasts 6 seconds. Now with the release version: | |||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE=release && make -j4 | cmake . -DCMAKE_BUILD_TYPE=release && make -j4 | ||
Line 141: | Line 204: | ||
sys 0m1.328s | sys 0m1.328s | ||
</pre> | </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 | ||
<pre> | <pre> | ||
cmake . -DCMAKE_BUILD_TYPE= | cmake . -DCMAKE_BUILD_TYPE=debug && make VERBOSE=1 | ||
</pre> | </pre> | ||
You will find a difference during the link step. The parameters | You will find a difference during the link step. The parameters | ||
Line 152: | Line 215: | ||
-DNDEBUG -DQT_NO_DEBUG | -DNDEBUG -DQT_NO_DEBUG | ||
</pre> | </pre> | ||
are unique for the release | 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.