在项目开发中,当程序崩溃时,如何快速的定位bug,一直是程序员最头疼的问题,为此,也有很多有经验的人士总结了很多办法。
今天小豆君就来分享个我自己创立的代码跟踪器。
1 打印开始和结束信息
一般的,在每个调用接口的开始和结束处加入打印信息。当程序崩溃时,就能够快速判断是在哪个函数的调用中出了问题,从而快速缩小范围。
像这样:
void MyTest::createWidget() { qDebug() << "Enter createWidget"; //代码段... qDebug() << "Leave createWidget"; }
但是,有一个缺点,不简洁,有时候我也很懒,不愿意每个接口都加。
好,那可以改进一下。
对于一个类,在创建的时候会调用构造函数,在释放的时候会调用析构函数。所以利用这个特性,我就可以将"Enter..."写在构造函数中,将"Leave..."写在析构函数中。在函数开始处创建这个局部对象(当然不要new,因为还需要多一句delete,而且很容易忘记)自动调用构造函数,这时就会打印“Enter”,而当函数调用结束后,系统就会自动释放该局部对象前调用析构函数,从而打印“Leave”。
为此我们创建一个类,取名为Tracer(跟踪器)。
class Tracer { public: Tracer(QString msg = "") { m_msg = msg; qDebug() << "Enter:" << m_msg; } ~Tracer() { qDebug() << "Leave:" << m_msg; } private: QString m_msg; };
调用:
void MyTest::createWidget() { Tracer tracer("createWidget"); //代码段... }
打印结果:
但是,我想在打印信息中加入更多的信息,例如行号,文件名,函数名等,这又该怎么做呢?
2 使用Qt打印行号,文件名,函数名。
在Qt中,qDebug还可以打印行号,文件名,函数名等信息。但先要使用
qSetMessagePattern(const QString& pattern)来进行设置;
(小豆君这里讲的是Qt,当然你也可以使用__FILE__ __LINE__ __FUNCTION__宏)。
例如:
int main(int argc, char *argv[]) { QString pattern("Debug:%{function}@%{line},%{file}"); qSetMessagePattern(pattern); qDebug(); return 0; }
运行输出:
qSetMessagePattern具体的用法及语法很简单,大家可以直接在帮助文档中搜索该关键词即可。
3 在函数首尾加入详细调试信息
好,现在,我们可以为我们的程序添加详细的调试信息了。
在main函数中首先调用qSetMessagePattern对qDebug进行输出格式设置。
然后直接在函数开头创建Tracer对象即可,但是,这会有个问题,打印出的信息是Tracer中的调用信息。
所以,我们不能将qDebug写在Tracer的构造函数内部。
而是这样写:
void MyTest::createWidget() { Tracer tracer("createWidget"); qDebug(); //代码段... }
但是,这又多了一句qDebug(),这还是有一点啰嗦,那如何把它变成一句话呢。
办法当然有了,宏,宏就是可以把多句话变成一句话。
下面,小豆君就给出这个代码跟踪器的完整版。
新建tracer.h头文件
#ifndef TRACER_H #define TRACER_H #include <QDebug> #include <iostream> //如果想要去掉打印, //把下一行注掉即可 #define TRACER_DEBUG #ifdef TRACER_DEBUG #define TRACER \ Tracer tracer(__func__) #else #define TRACER #endif class Tracer { public: Tracer(const char msg[]): m_msg(msg) { fprintf(stderr, "Enter: %s", m_msg); } ~Tracer() { fprintf(stderr, "Leave: %s", m_msg); } private: const char* m_msg; }; #endif // TRACER_H
测试:
main.cpp
#include <QString> #include "tracer.h" int main(int argc, char *argv[]) { QString pattern("%{function}@%{line},%{file}"); qSetMessagePattern(pattern); TRACER return 0; }
运行结果:
好啦,终于创建好了,结果正如我们想象,真是太棒了,它可以用在任何函数调用中,这里小豆君就偷个懒放在了main函数中啦。
当然,这里主要是为了介绍下Qt的打印文件名,行号知识点,qDebug的打印完全可以更换成 __func__, __FILE__, __LINE__ 之类的东东,这样就更具有适用性啦。
好的,关于追踪器的分享就到这里啦。
更多干货,可关注公众号:小豆君,关注后还可申请加入C++\Qt交流群,一起学习。