但是,如果崩溃之后未被拉起,可以正常的看到符号。
后来发现,是运维启动进程的 shell 脚本,每次启动之前,会将需要加载的部分业务相关的 so 文件,文件名字修改(名称里加上了时间戳,类似 lib20200423002608_xxxx.so 这种)。名称被修改之后,gdb 自然没法加载加载这个 so 文件。
info shared
在 gdb 里使用 info shared,可以看到这个 so 文件无对应的地址,因为没有对应的 so 文件被加载。线上环境的 gdb版本是 7.2,启动时没有与 so 文件不存在相关的提示。
当然这是后话。
那么在奔溃时,如何将奔溃时的调用栈记录到日志里呢。
可以借助 backtrace 相关的 3 个函数来实现。
#include <execinfo.h>
int backtrace(void *symaddr[], int size)
char **backtrace_symbols(void *const symaddr[], int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
参数和返回值说明:
backtrace 传入一个数组 symaddr,用来保存符号的地址;size 为数组的大小。size 应该足够大,不然会有部分符号丢失。返回值为实际保存的地址数量。
backtrace_symbols 用来根据符号的地址,得到对应的符号。size 为 backtrace 的返回值,表示实际需要处理的符号数量。
返回的是一个 malloc 得到的字符串数组的起始地址(C 语言中不太严谨的讲,char* 就是字符串),所以最后需要调用者释放内存。
#include <stdlib.h>
#include <string>
#include <execinfo.h>
#include <unistd.h>
void getCallStackInfo(std::string &stackInfo)
{
static const int size = 100 //符号数量,100足够
int nptrs
void *buffer[size]
char **syms
nptrs = backtrace(buffer, size)//返回当前调用栈实际的符号数量
syms = backtrace_symbols(buffer, nptrs)
if (syms == nullptr)
{
perror("backtrace_symbols")
exit(EXIT_FAILURE)
}
for(int i = 0i <nptrs++i)
{
stackInfo.append(syms[i])
stackInfo.append("\n")
}
free(syms)
}
void say(int &n)
{
static int call_count = 0
++n
++call_count
printf("call count %d\n", call_count)
if(call_count == 6)
{
std::string stack_info
getCallStackInfo(stack_info)
printf("%s\n", stack_info.c_str())
return
}
say(n)
}
int main()
{
int n = 3
say(n)
return 0
}
编译运行,clang++ main.cpp -rdynamic -o main.out &&./main.out
call count 1
call count 2
call count 3
call count 4
call count 5
call count 6
./main.out(_Z16getCallStackInfoRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x23) [0x400d53]
./main.out(_Z3sayRi+0x6a) [0x400e8a]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(main+0x1f) [0x400f0f]
/lib64/libc.so.6(__libc_start_main+0xf3) [0x7fe51b555873]
./main.out(_start+0x2e) [0x400c6e]
看到调用栈已经被记录下来,当然符号都是 name mangling 之后的,使用 c++filt _Z3sayRi 可以看到原始名字。
回到记录奔溃时的调用栈到日志里的主题上。通常的奔溃都是由于内存问题,那么可以捕获 SIGSEGV 信号,在信号处理函数中将当前的调用栈记录到日志中就行。写文件可能需要一个 sleep 延时等待日志线程处理完毕。
void sig_log_stack_handler(int sig)
{
std::string stackInfo
getCallStackInfo(stackInfo)
abort()
}
当然严格的来说,在信号处理器函数里处理 IO 是不符合标准的,会 UB.
注意编译时一定要带上 -rdynamic 选项才有用。如果使用的是 qt creator,这个 -rdynamic 参数时需要传给链接器的,需要在 .pro 文件里加上,加到 QMAKE_CXXFLAGS 是没得用的。
QMAKE_LFLAGS += -rdynamic
opyright © 1999-2020, CSDN.NET, All Rights Reserved打开APP
乄夵。
关注
Qt5中动态加载ui文件写的界面(用QtUiTools /QUiLoader实现) 原创
2022-09-13 10:42:00
乄夵。
码龄4年
关注
由QUiLoader类提供的表单加载器对象用于构造用户界面。这个用户界面可以从任何QIODevice中检索例如,QFile对象可用于获取存储在项目资源中的表单。QUiLoader::load()函数接受文件中包含的用户界面描述,并构造表单小部件。
使用QUiLoader 动态加载ui文件可以说是做到了,界面设计和逻辑的相互分离
QUiLoader的实用方法
QTabWidget *tab = new QTabWidget(this)
setCentralWidget(tab)
通过load函数即可根据.ui文件创建出一个对应的QWidget对象,后面的this指针是QWidget *类型的,你可以用其它窗体去作为formWidget的父窗体。父窗体被显示的时候,该formWidget窗体就会被显示,当然更好的方法是用一个QLayout来管理formWidget,因为父窗体可能不止一个子窗体,我这里作为一个演示,将QTabWidget作为窗体的父窗体。
如果你还想对cuteqt.ui中的那些子窗体信号作处理,那么可以用qFindChild去找到它们。
QWidget *leftPanel = Q_NULLPTR
leftPanel = formwidget ->findChild<QWidget *>(“leftPanel”)
ui_setButton = qFindChild<QPushButton*>(this, “setButton”)
ui_clearButton = qFindChild<QPushButton*>(this, “clearButton”)
ui_textEdit = qFindChild<QTextEdit*>(this, “textEdit”)
比如你可以用
connect(ui_setButton,SIGNAL(clicked()),this,SLOT(setButtonClicked()))处理ui_setButton的clicked()信号。
使用QUiLoader要检查pro文件加上CONFIG += uitools
在头文件引用#include <QtUiTools/QUiLoader>
file 中的:/f/test.ui 是需要添加到你的qrc资源文件当中的不然容易造成路径错误
An error has occurred while reading the UI file at line 1, column 0: Premature end of document.
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)