logo头像

技术引领生活!

有关Qt的一些记录

windows下QtCreator的大内存改造(32位)

因为要查看Qt的源代码,因为我用的Qt版本是5.7.0是32位的,所以会出现加载文件过多而崩溃的现象,方法为

1
editbin /largeaddressaware qtcreator.exe

修改前先备份原程序,修改好以后通过dumpbin /headers来查看是否多了一条Application can handle large (>2GB) addresses

Unable to set geometry的bug修复

Qt有个bug:当使用QSystemTrayIcon时,可能会用到拦截关闭按钮后隐藏的需求,此时如果最大化后点击关闭按钮那么恢复窗口时则会出现setGeometry: Unable to set geometry的问题.官方的示例也存在该问题.折腾了好久才勉强解决
开始想重载窗口的close()函数,直接调用hide()函数,结果失败,通过阅读帮助发现该函数底层不管如何会首先发送一个QCloseEvent时间(在重载函数调用之前)这就尴尬了.

1
bool QWidget::close()

Closes this widget. Returns true if the widget was closed; otherwise returns false.
First it sends the widget a QCloseEvent. The widget is hidden if it accepts the close event. If it ignores the event, nothing happens. The default implementation of QWidget::closeEvent() accepts the close event.

经过一阵乱撸找到最终的解决方案如下,在隐藏前需要改变窗口的状态(或者调用showNormal()也可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//拦截关闭事件
void MainWidget::closeEvent(QCloseEvent *event)
{
//隐藏之前记录是否为最大化
m_lastIsMax = isMaximized();

//必须黑科技一把,清除掉最大化状态....
setWindowState(windowState() & ~Qt::WindowMaximized);
//showNormal();

//显示为正常窗口之后才能隐藏,否则会导致最大化窗口的问题...
hide();
event->ignore();
}

//恢复时,先判断是否最后关闭时为最大化
void MainWidget::showWindowNormal()
{
if(m_lastIsMax){
showMaximized();
}
else{
show();
}

activateWindow();
raise();
}

程序带上Qt源码调试

打开QtCreator,在工具->选项->调试器->概要 找到添加Qt源码…然后点击找到Qt安装目录下的Src文件夹即可
加入Qt源码

崩溃收集

  1. 使用谷歌的breakpad
    google的breakpad

  2. 编写一个.pri文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    # ***************************************************************************
    # Implemented using http://blog.inventic.eu/2012/08/qt-and-google-breakpad/
    # as a the reference.
    #
    # Get Google Breakpad here: https://code.google.com/p/google-breakpad/
    #
    # The required breakpad sources have been copied into /src in order to make
    # integration with the application smooth and easy.
    #
    # To use source from Google Breakpad SVN checkout instead, change $$PWD/src
    # to path where it was checked out.
    #
    # ***************************************************************************

    HEADERS += $$PWD/crashhandler.h
    SOURCES += $$PWD/crashhandler.cpp

    INCLUDEPATH += $$PWD
    INCLUDEPATH += $$PWD/src

    # Windows
    win32:HEADERS += $$PWD/src/common/windows/string_utils-inl.h
    win32:HEADERS += $$PWD/src/common/windows/guid_string.h
    win32:HEADERS += $$PWD/src/client/windows/handler/exception_handler.h
    win32:HEADERS += $$PWD/src/client/windows/common/ipc_protocol.h
    win32:HEADERS += $$PWD/src/google_breakpad/common/minidump_format.h
    win32:HEADERS += $$PWD/src/google_breakpad/common/breakpad_types.h
    win32:HEADERS += $$PWD/src/client/windows/crash_generation/crash_generation_client.h
    win32:HEADERS += $$PWD/src/common/scoped_ptr.h
    win32:SOURCES += $$PWD/src/client/windows/handler/exception_handler.cc
    win32:SOURCES += $$PWD/src/common/windows/string_utils.cc
    win32:SOURCES += $$PWD/src/common/windows/guid_string.cc
    win32:SOURCES += $$PWD/src/client/windows/crash_generation/crash_generation_client.cc

    # Linux
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/cpu_set.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/proc_cpuinfo_reader.h
    unix:HEADERS += $$PWD/src/client/linux/handler/exception_handler.h
    unix:HEADERS += $$PWD/src/client/linux/crash_generation/crash_generation_client.h
    unix:HEADERS += $$PWD/src/client/linux/handler/minidump_descriptor.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/minidump_writer.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/line_reader.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/linux_dumper.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/linux_ptrace_dumper.h
    unix:HEADERS += $$PWD/src/client/linux/minidump_writer/directory_reader.h
    unix:HEADERS += $$PWD/src/client/linux/log/log.h
    unix:HEADERS += $$PWD/src/client/minidump_file_writer-inl.h
    unix:HEADERS += $$PWD/src/client/minidump_file_writer.h
    unix:HEADERS += $$PWD/src/common/linux/linux_libc_support.h
    unix:HEADERS += $$PWD/src/common/linux/eintr_wrapper.h
    unix:HEADERS += $$PWD/src/common/linux/ignore_ret.h
    unix:HEADERS += $$PWD/src/common/linux/file_id.h
    unix:HEADERS += $$PWD/src/common/linux/memory_mapped_file.h
    unix:HEADERS += $$PWD/src/common/linux/safe_readlink.h
    unix:HEADERS += $$PWD/src/common/linux/guid_creator.h
    unix:HEADERS += $$PWD/src/common/linux/elfutils.h
    unix:HEADERS += $$PWD/src/common/linux/elfutils-inl.h
    unix:HEADERS += $$PWD/src/common/linux/elf_gnu_compat.h
    unix:HEADERS += $$PWD/src/common/using_std_string.h
    unix:HEADERS += $$PWD/src/common/memory.h
    unix:HEADERS += $$PWD/src/common/basictypes.h
    unix:HEADERS += $$PWD/src/common/memory_range.h
    unix:HEADERS += $$PWD/src/common/string_conversion.h
    unix:HEADERS += $$PWD/src/common/convert_UTF.h
    unix:HEADERS += $$PWD/src/google_breakpad/common/minidump_format.h
    unix:HEADERS += $$PWD/src/google_breakpad/common/minidump_size.h
    unix:HEADERS += $$PWD/src/google_breakpad/common/breakpad_types.h
    unix:HEADERS += $$PWD/src/common/scoped_ptr.h
    unix:HEADERS += $$PWD/src/third_party/lss/linux_syscall_support.h
    unix:SOURCES += $$PWD/src/client/linux/crash_generation/crash_generation_client.cc
    unix:SOURCES += $$PWD/src/client/linux/handler/exception_handler.cc
    unix:SOURCES += $$PWD/src/client/linux/handler/minidump_descriptor.cc
    unix:SOURCES += $$PWD/src/client/linux/minidump_writer/minidump_writer.cc
    unix:SOURCES += $$PWD/src/client/linux/minidump_writer/linux_dumper.cc
    unix:SOURCES += $$PWD/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
    unix:SOURCES += $$PWD/src/client/linux/log/log.cc
    unix:SOURCES += $$PWD/src/client/minidump_file_writer.cc
    unix:SOURCES += $$PWD/src/common/linux/linux_libc_support.cc
    unix:SOURCES += $$PWD/src/common/linux/file_id.cc
    unix:SOURCES += $$PWD/src/common/linux/memory_mapped_file.cc
    unix:SOURCES += $$PWD/src/common/linux/safe_readlink.cc
    unix:SOURCES += $$PWD/src/common/linux/guid_creator.cc
    unix:SOURCES += $$PWD/src/common/linux/elfutils.cc
    unix:SOURCES += $$PWD/src/common/string_conversion.cc
    unix:SOURCES += $$PWD/src/common/convert_UTF.c
    #breakpad app need debug info inside binaries
    unix:QMAKE_CXXFLAGS+=-g
  3. 添加头文件crashhandler.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #pragma once
    #include <QtCore/QString>

    namespace Breakpad {
    class CrashHandlerPrivate;
    class CrashHandler
    {
    public:
    static CrashHandler* instance();
    void Init(const QString& reportPath);

    void setReportCrashesToSystem(bool report);
    bool writeMinidump();

    private:
    CrashHandler();
    ~CrashHandler();
    Q_DISABLE_COPY(CrashHandler)
    CrashHandlerPrivate* d;
    };
    }
  4. 编写cpp文件crashhandler.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    #include "crashhandler.h"
    #include <QtCore/QDir>
    #include <QtCore/QProcess>
    #include <QtCore/QCoreApplication>
    #include <QString>

    #if defined(Q_OS_LINUX)
    #include "client/linux/handler/exception_handler.h"
    #elif defined(Q_OS_WIN32)
    #include "client/windows/handler/exception_handler.h"
    #endif

    namespace Breakpad {
    /************************************************************************/
    /* CrashHandlerPrivate */
    /************************************************************************/
    class CrashHandlerPrivate
    {
    public:
    CrashHandlerPrivate()
    {
    pHandler = NULL;
    }

    ~CrashHandlerPrivate()
    {
    delete pHandler;
    }

    void InitCrashHandler(const QString& dumpPath);
    static google_breakpad::ExceptionHandler* pHandler;
    static bool bReportCrashesToSystem;
    };

    google_breakpad::ExceptionHandler* CrashHandlerPrivate::pHandler = NULL;
    bool CrashHandlerPrivate::bReportCrashesToSystem = false;

    /************************************************************************/
    /* DumpCallback */
    /************************************************************************/
    #if defined(Q_OS_WIN32)
    bool DumpCallback(const wchar_t* _dump_dir,const wchar_t* _minidump_id,void* context,EXCEPTION_POINTERS* exinfo,MDRawAssertionInfo* assertion,bool success)
    #elif defined(Q_OS_LINUX)
    bool DumpCallback(const google_breakpad::MinidumpDescriptor &md,void *context, bool success)
    #endif
    {
    Q_UNUSED(context);
    #if defined(Q_OS_WIN32)
    Q_UNUSED(_dump_dir);
    Q_UNUSED(_minidump_id);
    Q_UNUSED(assertion);
    Q_UNUSED(exinfo);
    #endif
    qDebug("BreakpadQt crash");

    /*
    NO STACK USE, NO HEAP USE THERE !!!
    Creating QString's, using qDebug, etc. - everything is crash-unfriendly.
    */
    return CrashHandlerPrivate::bReportCrashesToSystem ? success : true;
    }

    void CrashHandlerPrivate::InitCrashHandler(const QString& dumpPath)
    {
    if ( pHandler != NULL )
    return;

    #if defined(Q_OS_WIN32)
    std::wstring pathAsStr = (const wchar_t*)dumpPath.utf16();
    pHandler = new google_breakpad::ExceptionHandler(
    pathAsStr,
    /*FilterCallback*/ 0,
    DumpCallback,
    /*context*/
    0,
    true
    );
    #elif defined(Q_OS_LINUX)
    std::string pathAsStr = dumpPath.toStdString();
    google_breakpad::MinidumpDescriptor md(pathAsStr);
    pHandler = new google_breakpad::ExceptionHandler(
    md,
    /*FilterCallback*/ 0,
    DumpCallback,
    /*context*/ 0,
    true,
    -1
    );
    #endif
    }

    /************************************************************************/
    /* CrashHandler */
    /************************************************************************/
    CrashHandler* CrashHandler::instance()
    {
    static CrashHandler globalHandler;
    return &globalHandler;
    }

    CrashHandler::CrashHandler()
    {
    d = new CrashHandlerPrivate();
    }

    CrashHandler::~CrashHandler()
    {
    delete d;
    }

    void CrashHandler::setReportCrashesToSystem(bool report)
    {
    d->bReportCrashesToSystem = report;
    }

    bool CrashHandler::writeMinidump()
    {
    bool res = d->pHandler->WriteMinidump();
    if (res) {
    qDebug("BreakpadQt: writeMinidump() success.");
    } else {
    qWarning("BreakpadQt: writeMinidump() failed.");
    }
    return res;
    }

    void CrashHandler::Init( const QString& reportPath )
    {
    d->InitCrashHandler(reportPath);
    }
    }
  1. 测试程序
    新建一个工程在将breakpad拷贝到3rdparty目录下,然后pro文件中添加
    1
    include($$PWD/3rdparty/breakpad/breakpad.pri)

测试程序main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "crashhandler.h"
//see https://github.com/JPNaude/dev_notes/wiki/Using-Google-Breakpad-with-Qt

int buggyFunc() {
delete reinterpret_cast<QString*>(0xFEE1DEAD);
return 0;
}

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// We put the dumps in the user's home directory for this example:
Breakpad::CrashHandler::instance()->Init(QCoreApplication::applicationDirPath());

buggyFunc();
return a.exec();
}

//more
//minidump_stackwalk.exe xxxx.dmp symbols > foo.txt 2>&1
  1. 运行minidump_stackwalk.exe程序分析dmp文件
    1
    minidump_stackwalk.exe xxxx.dmp symbols > foo.txt 2>&1

7.打开 foo.txt文件
记住该文件下跟exe相关的地方,找到如下图中的eip寄存器地址(该寄存器应该为堆栈地址指针)
加入Qt源码

  1. 重新编译文件

在.pro工程中添加以下内容后重新编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
QMAKE_CXXFLAGS_RELEASE += -g

QMAKE_CFLAGS_RELEASE += -g

QMAKE_LFLAGS_RELEASE = -mthreads -Wl, #其中最后的逗号注意添加,如果不添加可能编译不过

#禁止优化(存疑)

QMAKE_CFLAGS_RELEASE -= -O2

QMAKE_CXXFLAGS_RELEASE -= -O2

#或者使用以下语句
#QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
#QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO
#QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
  1. 运行addr2line(该软件为Qt自带xxx\Tools\mingw530_32\bin\addr2line.exe)找到源文件中的地址
    1
    addr2line.exe -e untitled.exe -a 40eaf4 404774
    加入Qt源码

10.效果如下
加入Qt源码
你就说巧不巧吧……

如果不加调试信息则找不到,但是还可以使用调试工具打开

加入Qt源码
找到第一个地址4b59然后在调试工具中找到地址发现为窗口显示的下一条语句,嗯听天由命吧
加入Qt源码

增加一条指令,将文件转为汇编…

1
objdump -S untilted.exe > test.asm

静态Qt编译Mysql驱动

  1. 按照其他教程,下载相应版本的Qt源码放到对应的路径下
  2. 建立qt_plugin_qsqlmysql.pri
    模仿sqlite在路径 …Qt\Qt5.7.1\5.7\mingw53_32_release_static\mkspecs\modules 下建立一个文件内容如下
1
2
3
4
QT_PLUGIN.qsqlmysql.TYPE = sqldrivers
QT_PLUGIN.qsqlmysql.EXTENDS =
QT_PLUGIN.qsqlmysql.CLASS_NAME = QMYSQLDriverPlugin
QT_PLUGINS += qsqlmysql

这样Qt编译的时候会扫描该目录下的文件,自动导入

  1. 编译完成后生成的文件为
  • libqsqlmysql.a
  • qsqlmysql.prl
    1
    2
    3
    4
    5
    QMAKE_PRL_BUILD_DIR = C:/Qt/Qt5.7.1/5.7/Src/qtbase/src/plugins/sqldrivers/mysql
    QMAKE_PRO_INPUT = mysql.pro
    QMAKE_PRL_TARGET = libqsqlmysql.a
    QMAKE_PRL_CONFIG = lex yacc depend_includepath testcase_targets import_plugins import_qpa_plugin incremental_off windows qt_build_extra file_copies qt warn_on release link_prl debug_and_release precompile_header release static static_runtime rtti no_plugin_manifest directwrite qpa win32 mingw gcc copy_dir_files precompile_header pcre release sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2 largefile prefix_build force_independent create_prl link_prl prepare_docs qt_docs_targets no_private_qt_headers_warning QTDIR_build qt_example_installs exceptions_off testcase_exceptions warning_clean release ReleaseBuild Release build_pass c++11 plugin relative_qt_rpath create_cmake c++11 strict_c++ c++14 c++1z release ReleaseBuild Release build_pass have_target staticlib exclusive_builds no_autoqmake thread moc resources
    QMAKE_PRL_LIBS = -llibmysql -LC:/Qt/Qt5.7.1/5.7/mingw53_32_release_static/lib C:/Qt/Qt5.7.1/5.7/mingw53_32_release_static/lib\\libQt5Sql.a -LC:/Qt/Qt5.7.1/5.7/OpenSSL_MinGW32_Static/lib C:/Qt/Qt5.7.1/5.7/mingw53_32_release_static/lib\\libQt5Core.a -lole32 -luuid -lws2_32 -ladvapi32 -lshell32 -luser32 -lkernel32 -lmpr C:/Qt/Qt5.7.1/5.7/mingw53_32_release_static/lib\\libqtpcre.a
  1. 编译完成后就行了,但此时还需要拷贝动态链接库libmysql.dll到可执行目录留下,不然运行的时候会报错,一直想解决这个问题(不能完全做到打包到一个文件中)

  2. 下载mingw转化工具(reimp.exe)重新编译也许可以(暂时未做尝试) dlltool.exe在Qt的工具目录下就有

    1
    2
    reimp -d libmysql.lib
    dlltool -k -d libmysql.def -l libmysql.a

    有需要的自取(转化的版本我试了下5.7的不行,低版本的可以,未继续深入,同时把第二步中的qt_plugin_qsqlmysql.pri改名后既可以不加载mysql了

附件下载

minidump_stackwalk下载

reimp下载

参考文档

qt mingw 创建dump 并查找crash 出错行
JPNaude的教程

支付宝打赏 微信打赏

您的支持是我前行的动力!