重构,将GDB控制部分提取为单独模块;符号文件选择框过滤器添加"所有文件"
This commit is contained in:
parent
f3886c3b28
commit
a803f3636c
|
@ -19,6 +19,7 @@ DEFINES += QT_DEPRECATED_WARNINGS \
|
|||
|
||||
SOURCES += \
|
||||
aboutwindow.cpp \
|
||||
gdbprocess.cpp \
|
||||
graphwindow.cpp \
|
||||
helpwindow.cpp \
|
||||
listwindow.cpp \
|
||||
|
@ -27,6 +28,7 @@ SOURCES += \
|
|||
|
||||
HEADERS += \
|
||||
aboutwindow.h \
|
||||
gdbprocess.h \
|
||||
graphwindow.h \
|
||||
helpwindow.h \
|
||||
listwindow.h \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.11.0, 2022-01-25T15:56:21. -->
|
||||
<!-- Written by QtCreator 4.11.0, 2022-01-30T18:29:16. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
#include "gdbprocess.h"
|
||||
|
||||
GDBProcess::GDBProcess(QObject *parent) : QObject(parent)
|
||||
{
|
||||
process=new QProcess();
|
||||
}
|
||||
|
||||
GDBProcess::~GDBProcess()
|
||||
{
|
||||
delete process;
|
||||
}
|
||||
|
||||
//运行一条GDB命令并获取输出字符串
|
||||
QString GDBProcess::runCmd(const QString &cmd)
|
||||
{
|
||||
process->readAllStandardOutput();
|
||||
process->write(cmd.toStdString().c_str());
|
||||
QString res="";
|
||||
do{
|
||||
process->waitForReadyRead();
|
||||
res+=process->readAllStandardOutput();
|
||||
}while(!res.endsWith("(gdb) "));
|
||||
return res;
|
||||
}
|
||||
|
||||
//启动GDB进程
|
||||
void GDBProcess::start()
|
||||
{
|
||||
process->setProgram(QCoreApplication::applicationDirPath()+"/gdb/gdb.exe");//设置程序路径
|
||||
process->setWorkingDirectory(QCoreApplication::applicationDirPath()+"/gdb");//设置工作路径
|
||||
process->setNativeArguments("-q");//设置gdb在安静模式下打开
|
||||
process->start();
|
||||
runCmd("set confirm off\r\n");//设置不要手动确认
|
||||
runCmd("set print pretty on\r\n");//设置结构体规范打印
|
||||
}
|
||||
|
||||
//停止GDB进程
|
||||
void GDBProcess::stop()
|
||||
{
|
||||
if(process->state()!=QProcess::NotRunning)
|
||||
process->kill();
|
||||
}
|
||||
|
||||
//命令GDB连接到远程目标,参数为"地址:端口号"
|
||||
void GDBProcess::connectToRemote(const QString &addr)
|
||||
{
|
||||
runCmd("target remote "+addr+"\r\n");
|
||||
}
|
||||
|
||||
//命令GDB从远处目标断开
|
||||
void GDBProcess::disconnectFromRemote()
|
||||
{
|
||||
runCmd("disconnect\r\n");
|
||||
}
|
||||
|
||||
//设定临时符号文件名,文件位于GDB程序目录下
|
||||
void GDBProcess::setTempSymbolFileName(const QString &name)
|
||||
{
|
||||
tempSymbolFileName=name;
|
||||
}
|
||||
|
||||
//加载符号文件,将指定文件复制到临时文件中,并加载到GDB中
|
||||
void GDBProcess::loadSymbolFile(const QString &path)
|
||||
{
|
||||
unloadSymbolFile();//确保卸载当前的临时文件
|
||||
QString tempPath=QCoreApplication::applicationDirPath()+"/gdb/"+tempSymbolFileName;//拼接临时文件路径
|
||||
QFile::copy(path,tempPath);//将所选符号文件复制为临时文件
|
||||
runCmd("symbol-file "+tempSymbolFileName+"\r\n");//设置符号文件
|
||||
}
|
||||
|
||||
//卸载符号文件
|
||||
void GDBProcess::unloadSymbolFile()
|
||||
{
|
||||
runCmd("symbol-file\r\n");//取消符号文件,解除文件占用
|
||||
QString tempPath=QCoreApplication::applicationDirPath()+"/gdb/"+tempSymbolFileName;//拼接临时文件路径
|
||||
QFile::remove(tempPath);//删除复制过来的临时文件
|
||||
}
|
||||
|
||||
//设置display列表
|
||||
void GDBProcess::setDisplayList(QStringList &list)
|
||||
{
|
||||
runCmd("delete display\r\n");//删除之前发送的变量列表
|
||||
foreach(QString name,list)//向display表中依次添加变量名
|
||||
runCmd("display "+name+"\r\n");
|
||||
}
|
||||
|
||||
//截取display指令所返回字符串中指定变量名的变量值
|
||||
QString GDBProcess::captureValueFromDisplay(const QString &rawDisplay, const QString &name)
|
||||
{
|
||||
QString regName="";
|
||||
for(int i=0;i<name.length();i++)//将变量名每个转换为16进制格式,用于正则匹配
|
||||
regName+=QString("\\x%1").arg(name.at(i).unicode(),0,16);
|
||||
|
||||
QRegExp rx(QString("\\d+:\\s%1\\s=\\s(.*)\\r\\n[\\d\\(]").arg(regName));//正则匹配模板,匹配选中的变量名并截取出变量值
|
||||
rx.setMinimal(true);//使用非贪心模式
|
||||
|
||||
if(rx.indexIn(rawDisplay)!=-1)
|
||||
return rx.cap(1);//正则中截取出的字符串即为变量当前值
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
//尝试将display所返回的变量值转为double,返回值为是否转换成功
|
||||
bool GDBProcess::getDoubleFromDisplayValue(const QString &rawValue, double &result)
|
||||
{
|
||||
if(rawValue.isEmpty())
|
||||
return false;
|
||||
if(rawValue.contains('{')||rawValue.contains('(')
|
||||
||rawValue.contains('<')||rawValue.contains('['))//若含有这些符号,表示该变量可能为复合类型,不能被解析
|
||||
return false;
|
||||
result=rawValue.mid(0,rawValue.indexOf(' ')).toDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
//命令GDB设定变量值
|
||||
void GDBProcess::setVarValue(const QString &varFullName, double value)
|
||||
{
|
||||
runCmd(QString("set %1=%2\r\n").arg(varFullName).arg(value));
|
||||
}
|
||||
|
||||
//判断指定变量名是否为可展开类型
|
||||
bool GDBProcess::checkExpandableType(const QString &varFullName)
|
||||
{
|
||||
QString whatis=runCmd(QString("whatis %1\r\n").arg(varFullName));//先使用whatis指令判断数组和函数指针
|
||||
whatis.remove("type = ");
|
||||
whatis.remove("\r\n(gdb) ");
|
||||
if(whatis.contains("["))
|
||||
return true;
|
||||
if(whatis.contains('('))
|
||||
return false;
|
||||
QString ptype=runCmd(QString("ptype %1\r\n").arg(varFullName));//再使用ptype指令判断是否是其他可展开类型
|
||||
ptype.remove("type = ");
|
||||
if(ptype.startsWith("struct")||ptype.startsWith("union"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//从GDB原始输出(info variables/ptype等指令)中解析变量列表
|
||||
QStringList GDBProcess::getVarListFromRawOutput(const QString &rawVarList)
|
||||
{
|
||||
QStringList list;
|
||||
QRegExp rootRx("\\s([^ (]*\\(\\*|)([^ )]+)(\\)\\(.*\\)|\\s:\\s\\d+|);");//正则匹配模板
|
||||
rootRx.setMinimal(true);//非贪心匹配
|
||||
int pos=0;
|
||||
while((pos=rootRx.indexIn(rawVarList,pos))!=-1)
|
||||
{
|
||||
QString name=rootRx.cap(2);//获取截取出的变量名部分
|
||||
if(name.contains('*'))//手动剔除'*'
|
||||
name.remove('*');
|
||||
if(name.contains('['))//手动剔除数组长度部分
|
||||
name.remove(QRegExp("\\[\\d+\\]"));
|
||||
list.append(name);
|
||||
pos+=rootRx.matchedLength();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//在raw字符串(可能由ptype等指令输出)中删除由'{}'嵌套的部分(跳过前offset个字符)
|
||||
void GDBProcess::removeInnerSection(QString &raw, int offset)
|
||||
{
|
||||
for(int startPos=offset;startPos<raw.length();startPos++)//startPos指针向后移动查找'{'
|
||||
{
|
||||
if(raw.at(startPos)=='{')//若找到'{',开始寻找对应的'}'
|
||||
{
|
||||
int innerLayer=1;//当前endPos所在的嵌套层级
|
||||
for(int endPos=startPos+1;endPos<raw.length();endPos++)//endPos指针向后查找对应的'}'
|
||||
{
|
||||
if(raw.at(endPos)=='{')//每遇到一个'{'则嵌套层级+1
|
||||
innerLayer++;
|
||||
else if(raw.at(endPos)=='}')//每遇到一个'}'则嵌套层级-1
|
||||
innerLayer--;
|
||||
|
||||
if(raw.at(endPos)=='}' && innerLayer==0)//若遇到'}'时嵌套层级为0,表示找到了对应的'}'
|
||||
{
|
||||
raw.remove(startPos,endPos-startPos+1);//删除从'{'到'}'的子串
|
||||
startPos--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef GDBPROCESS_H
|
||||
#define GDBPROCESS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <qcoreapplication.h>
|
||||
#include <qfile.h>
|
||||
#include <qregexp.h>
|
||||
|
||||
class GDBProcess : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GDBProcess(QObject *parent = nullptr);
|
||||
~GDBProcess();
|
||||
QString runCmd(const QString &cmd);
|
||||
void start();
|
||||
void stop();
|
||||
void connectToRemote(const QString &addr);
|
||||
void disconnectFromRemote();
|
||||
void setTempSymbolFileName(const QString &name);
|
||||
void loadSymbolFile(const QString &path);
|
||||
void unloadSymbolFile();
|
||||
void setDisplayList(QStringList &list);
|
||||
QString captureValueFromDisplay(const QString &rawDisplay,const QString &name);
|
||||
bool getDoubleFromDisplayValue(const QString &rawValue,double &result);
|
||||
void setVarValue(const QString &varFullName,double value);
|
||||
bool checkExpandableType(const QString &varFullName);
|
||||
QStringList getVarListFromRawOutput(const QString &rawVarList);
|
||||
void removeInnerSection(QString &raw,int offset);
|
||||
|
||||
private:
|
||||
QProcess *process;
|
||||
QString tempSymbolFileName;
|
||||
};
|
||||
|
||||
#endif // GDBPROCESS_H
|
148
listwindow.cpp
148
listwindow.cpp
|
@ -11,114 +11,20 @@ ListWindow::ListWindow(QWidget *parent) :
|
|||
treeModel=new QStandardItemModel(ui->tree);
|
||||
ui->tree->setModel(treeModel);
|
||||
|
||||
gdbProcess=new QProcess();
|
||||
|
||||
setGDBState(true);//启动GDB进程
|
||||
gdb=new GDBProcess();//创建并启动GDB
|
||||
gdb->setTempSymbolFileName("tmp_list");//设定临时符号文件名
|
||||
gdb->start();
|
||||
}
|
||||
|
||||
ListWindow::~ListWindow()
|
||||
{
|
||||
deleteTempFile();//删除临时文件
|
||||
setGDBState(false);//结束GDB进程
|
||||
delete gdbProcess;
|
||||
gdb->unloadSymbolFile();//删除临时文件
|
||||
gdb->stop();//结束GDB进程
|
||||
delete gdb;
|
||||
delete treeModel;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
//设置GDB进程状态
|
||||
void ListWindow::setGDBState(bool run)
|
||||
{
|
||||
if(run)//启动进程
|
||||
{
|
||||
gdbProcess->setProgram(QCoreApplication::applicationDirPath()+"/gdb/gdb.exe");//设置程序路径
|
||||
gdbProcess->setWorkingDirectory(QCoreApplication::applicationDirPath()+"/gdb");//设置工作路径
|
||||
gdbProcess->setNativeArguments("-q");//设置gdb在安静模式下打开
|
||||
gdbProcess->start();
|
||||
runGDBCmd("set confirm off\r\n");//设置不要手动确认
|
||||
runGDBCmd("set print pretty on\r\n");//设置结构体规范打印
|
||||
}
|
||||
else//结束进程
|
||||
{
|
||||
gdbProcess->kill();
|
||||
}
|
||||
}
|
||||
|
||||
//向GDB进程发送命令并获取标准输出
|
||||
QString ListWindow::runGDBCmd(const QString &input)
|
||||
{
|
||||
gdbProcess->readAllStandardOutput();
|
||||
gdbProcess->write(input.toStdString().c_str());
|
||||
QString res="";
|
||||
do{
|
||||
gdbProcess->waitForReadyRead();
|
||||
res+=gdbProcess->readAllStandardOutput();
|
||||
}while(!res.endsWith("(gdb) "));
|
||||
return res;
|
||||
}
|
||||
|
||||
//使用GDB检查变量名是否为可展开类型
|
||||
bool ListWindow::checkCanExpand(const QString &varFullName)
|
||||
{
|
||||
QString whatis=runGDBCmd(QString("whatis %1\r\n").arg(varFullName));//先使用whatis指令判断数组和函数指针
|
||||
whatis.remove("type = ");
|
||||
whatis.remove("\r\n(gdb) ");
|
||||
if(whatis.contains("["))
|
||||
return true;
|
||||
if(whatis.contains('('))
|
||||
return false;
|
||||
QString ptype=runGDBCmd(QString("ptype %1\r\n").arg(varFullName));//再使用ptype指令判断是否是其他可展开类型
|
||||
ptype.remove("type = ");
|
||||
if(ptype.startsWith("struct")||ptype.startsWith("union"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//从原始变量列表字符串中解析出变量列表
|
||||
QStringList ListWindow::parseVarList(const QString &raw)
|
||||
{
|
||||
QStringList list;
|
||||
QRegExp rootRx("\\s([^ (]*\\(\\*|)([^ )]+)(\\)\\(.*\\)|\\s:\\s\\d+|);");//正则匹配模板
|
||||
rootRx.setMinimal(true);//非贪心匹配
|
||||
int pos=0;
|
||||
while((pos=rootRx.indexIn(raw,pos))!=-1)
|
||||
{
|
||||
QString name=rootRx.cap(2);//获取截取出的变量名部分
|
||||
if(name.contains('*'))//手动剔除'*'
|
||||
name.remove('*');
|
||||
if(name.contains('['))//手动剔除数组长度部分
|
||||
name.remove(QRegExp("\\[\\d+\\]"));
|
||||
list.append(name);
|
||||
pos+=rootRx.matchedLength();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//删除一个原始变量列表字符串中的嵌套部分,跳过前offset个字符
|
||||
void ListWindow::removeInnerSection(QString &raw,int offset)
|
||||
{
|
||||
for(int startPos=offset;startPos<raw.length();startPos++)//startPos指针向后移动查找'{'
|
||||
{
|
||||
if(raw.at(startPos)=='{')//若找到'{',开始寻找对应的'}'
|
||||
{
|
||||
int innerLayer=1;//当前endPos所在的嵌套层级
|
||||
for(int endPos=startPos+1;endPos<raw.length();endPos++)//endPos指针向后查找对应的'}'
|
||||
{
|
||||
if(raw.at(endPos)=='{')//每遇到一个'{'则嵌套层级+1
|
||||
innerLayer++;
|
||||
else if(raw.at(endPos)=='}')//每遇到一个'}'则嵌套层级-1
|
||||
innerLayer--;
|
||||
|
||||
if(raw.at(endPos)=='}' && innerLayer==0)//若遇到'}'时嵌套层级为0,表示找到了对应的'}'
|
||||
{
|
||||
raw.remove(startPos,endPos-startPos+1);//删除从'{'到'}'的子串
|
||||
startPos--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//计算一个节点的全名(沿parent指针向上回溯)
|
||||
QString ListWindow::getVarFullName(const VarNode &node)
|
||||
{
|
||||
|
@ -140,18 +46,18 @@ void ListWindow::parseVarChildren(VarNode &node)
|
|||
{
|
||||
if(node.parent==NULL)//传入的是根节点
|
||||
{
|
||||
QString rawVarList=runGDBCmd("info variables\r\n");//使用info variables指令列出axf中所有变量
|
||||
QStringList varList=parseVarList(rawVarList);//解析出变量列表
|
||||
QString rawVarList=gdb->runCmd("info variables\r\n");//使用info variables指令列出axf中所有变量
|
||||
QStringList varList=gdb->getVarListFromRawOutput(rawVarList);//解析出变量列表
|
||||
foreach(QString name,varList)//依次添加子节点并更新可展开状态
|
||||
{
|
||||
node.append(name);
|
||||
node.children.last().canExpand=checkCanExpand(getVarFullName(node.children.last()));
|
||||
node.children.last().expandable=gdb->checkExpandableType(getVarFullName(node.children.last()));
|
||||
}
|
||||
}
|
||||
else//传入的是普通节点
|
||||
{
|
||||
QString fullName=getVarFullName(node);//计算节点全名
|
||||
QString rawVarType=runGDBCmd(QString("whatis %1\r\n").arg(fullName));//使用whatis指令获取类型
|
||||
QString rawVarType=gdb->runCmd(QString("whatis %1\r\n").arg(fullName));//使用whatis指令获取类型
|
||||
rawVarType.remove("type = ");
|
||||
rawVarType.remove("\r\n(gdb) ");
|
||||
if(rawVarType.contains("["))//判定是否为数组类型
|
||||
|
@ -164,24 +70,24 @@ void ListWindow::parseVarChildren(VarNode &node)
|
|||
{
|
||||
node.append(QString("[%1]").arg(i));
|
||||
if(i==0)//若是第一个元素,计算是否可展开
|
||||
node.children[0].canExpand=checkCanExpand(getVarFullName(node.children[0]));
|
||||
node.children[0].expandable=gdb->checkExpandableType(getVarFullName(node.children[0]));
|
||||
else//若不是第一个元素,直接用前一个元素的可展开状态即可
|
||||
node.children[i].canExpand=node.children[i-1].canExpand;
|
||||
node.children[i].expandable=node.children[i-1].expandable;
|
||||
}
|
||||
}
|
||||
else if(!rawVarType.contains('('))//不是数组类型,排除函数类型,判定是否为可展开类型
|
||||
{
|
||||
QString detailRawVarType=runGDBCmd(QString("ptype %1\r\n").arg(fullName));//用ptype指令获取详细类型
|
||||
QString detailRawVarType=gdb->runCmd(QString("ptype %1\r\n").arg(fullName));//用ptype指令获取详细类型
|
||||
detailRawVarType.remove("type = ");
|
||||
detailRawVarType.remove("\r\n(gdb) ");
|
||||
if(detailRawVarType.startsWith("struct")||detailRawVarType.startsWith("union"))//判定为可展开类型
|
||||
{
|
||||
removeInnerSection(detailRawVarType,detailRawVarType.indexOf('{')+1);//移除内部直接嵌套的部分
|
||||
QStringList varList=parseVarList(detailRawVarType);//解析出变量列表
|
||||
gdb->removeInnerSection(detailRawVarType,detailRawVarType.indexOf('{')+1);//移除内部直接嵌套的部分
|
||||
QStringList varList=gdb->getVarListFromRawOutput(detailRawVarType);//解析出变量列表
|
||||
foreach(QString name,varList)//依次添加子节点及其可展开状态
|
||||
{
|
||||
node.append(name);
|
||||
node.children.last().canExpand=checkCanExpand(getVarFullName(node.children.last()));
|
||||
node.children.last().expandable=gdb->checkExpandableType(getVarFullName(node.children.last()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,31 +98,15 @@ void ListWindow::parseVarChildren(VarNode &node)
|
|||
//加载AXF文件
|
||||
void ListWindow::loadNewAxfFile(const QString &path)
|
||||
{
|
||||
updateTempFile(path);//更新AXF文件配置
|
||||
gdb->loadSymbolFile(path);
|
||||
updateTree();//更新树状图
|
||||
}
|
||||
|
||||
//更新临时文件
|
||||
void ListWindow::updateTempFile(const QString &path)
|
||||
{
|
||||
QString tmpFilePath=QCoreApplication::applicationDirPath()+"/gdb/tmp_list";//临时符号文件路径
|
||||
deleteTempFile();//确保删除当前的临时文件
|
||||
QFile::copy(path,tmpFilePath);//将所选符号文件复制为临时文件
|
||||
runGDBCmd("symbol-file tmp_list \r\n");//设置符号文件
|
||||
}
|
||||
|
||||
//删除临时文件
|
||||
void ListWindow::deleteTempFile()
|
||||
{
|
||||
runGDBCmd("symbol-file\r\n");//取消符号文件,解除文件占用
|
||||
QFile::remove(QCoreApplication::applicationDirPath()+"/gdb/tmp_list");//删除复制过来的临时文件
|
||||
}
|
||||
|
||||
//更新整个树状图和节点树
|
||||
void ListWindow::updateTree()
|
||||
{
|
||||
varTree.clear();//从根节点开始递归清理
|
||||
varTree.canExpand=true;//设定根节点可展开
|
||||
varTree.expandable=true;//设定根节点可展开
|
||||
|
||||
QStandardItem *rootItem=new QStandardItem("");//创建根节点对应的item
|
||||
rootItem->setData((qlonglong)&varTree);//将item的数据指向根节点
|
||||
|
@ -241,7 +131,7 @@ void ListWindow::on_tree_expanded(const QModelIndex &index)
|
|||
QStandardItem *childItem=new QStandardItem();//创建item
|
||||
childItem->setText(childNode.name);//设定item显示变量名
|
||||
childItem->setData((qlonglong)&childNode);//建立item到对应节点的寻址方式
|
||||
if(childNode.canExpand)//若该子节点可展开,则添加一个空item使其显示为可展开状态
|
||||
if(childNode.expandable)//若该子节点可展开,则添加一个空item使其显示为可展开状态
|
||||
childItem->appendRow(new QStandardItem());
|
||||
item->appendRow(childItem);//子节点item附加到当前被展开的item下
|
||||
}
|
||||
|
|
13
listwindow.h
13
listwindow.h
|
@ -8,6 +8,7 @@
|
|||
#include <qdebug.h>
|
||||
#include <QStandardItem>
|
||||
#include <qmessagebox.h>
|
||||
#include <gdbprocess.h>
|
||||
|
||||
namespace Ui {
|
||||
class ListWindow;
|
||||
|
@ -19,7 +20,7 @@ public:
|
|||
QString name;//变量名
|
||||
QList<VarNode> children;//子节点
|
||||
VarNode *parent=NULL;//父节点
|
||||
bool canExpand=false;//可展开状态,即该变量类型是否可被展开
|
||||
bool expandable=false;//可展开状态,即该变量类型是否可被展开
|
||||
bool parsed=false;//是否已经解析出子节点信息
|
||||
VarNode(const QString &_name="",VarNode *_parent=NULL)
|
||||
{
|
||||
|
@ -35,7 +36,7 @@ public:
|
|||
{
|
||||
children.clear();
|
||||
name="";
|
||||
canExpand=parsed=false;
|
||||
expandable=parsed=false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -60,17 +61,11 @@ private slots:
|
|||
private:
|
||||
Ui::ListWindow *ui;
|
||||
QProcess *gdbProcess;
|
||||
GDBProcess *gdb;
|
||||
VarNode varTree;
|
||||
QStandardItemModel *treeModel;
|
||||
void setGDBState(bool run);
|
||||
QString runGDBCmd(const QString &input);
|
||||
QStringList parseVarList(const QString &raw);
|
||||
void removeInnerSection(QString &raw,int offset=0);
|
||||
QString getVarFullName(const VarNode &node);
|
||||
bool checkCanExpand(const QString &varFullName);
|
||||
void parseVarChildren(VarNode &node);
|
||||
void updateTempFile(const QString &path);
|
||||
void deleteTempFile();
|
||||
void updateTree();
|
||||
};
|
||||
|
||||
|
|
172
mainwindow.cpp
172
mainwindow.cpp
|
@ -38,8 +38,9 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
ocdProcess=new QProcess(0);//创建openocd进程
|
||||
connect(ocdProcess,SIGNAL(readyReadStandardError()),this,SLOT(slotOCDErrorReady()));
|
||||
|
||||
gdbProcess=new QProcess(0);//创建并运行gdb进程
|
||||
setGDBState(true);
|
||||
gdb=new GDBProcess();//创建并启动GDB
|
||||
gdb->setTempSymbolFileName("tmp");//设定临时符号文件名
|
||||
gdb->start();
|
||||
|
||||
loadConfFileList();//从openocd文件夹中读取配置文件列表
|
||||
}
|
||||
|
@ -47,12 +48,15 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
MainWindow::~MainWindow()
|
||||
{
|
||||
if(connected)//若处于连接状态则断开连接
|
||||
setConnState(false);
|
||||
setGDBState(false);//结束gdb进程
|
||||
{
|
||||
gdb->disconnectFromRemote();
|
||||
gdb->unloadSymbolFile();
|
||||
}
|
||||
gdb->stop();//结束gdb进程
|
||||
delete ui;
|
||||
delete tableModel;
|
||||
delete ocdProcess;
|
||||
delete gdbProcess;
|
||||
delete gdb;
|
||||
delete stampTimer;
|
||||
delete watchTimer;
|
||||
delete tableTimer;
|
||||
|
@ -121,7 +125,12 @@ void MainWindow::slotTableEdit(QModelIndex topleft, QModelIndex bottomright)
|
|||
redrawTable();//重绘表格
|
||||
|
||||
if(connected)//若正在连接状态则向gdb发送新的变量列表
|
||||
setGDBDispList();
|
||||
{
|
||||
QStringList nameList;
|
||||
for(int index=0;index<varList.size();index++)
|
||||
nameList<<varList.at(index).name;
|
||||
gdb->setDisplayList(nameList);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(topleft.column()==2 && topleft.row()!=varList.size())//若编辑的是第二列,表示需进行变量值修改
|
||||
|
@ -130,7 +139,7 @@ void MainWindow::slotTableEdit(QModelIndex topleft, QModelIndex bottomright)
|
|||
QString valueStr=tableModel->item(topleft.row(),2)->text();
|
||||
if(!valueStr.isEmpty())
|
||||
{
|
||||
setVar(name,valueStr.toDouble());//向gdb发送命令写入变量值
|
||||
gdb->setVarValue(name,valueStr.toDouble());//向gdb发送命令写入变量值
|
||||
tableModel->item(topleft.row(),2)->setText("");//清空编辑框
|
||||
}
|
||||
}
|
||||
|
@ -143,9 +152,18 @@ void MainWindow::slotWatchTimerTrig()
|
|||
return;
|
||||
isWatchProcessing=true;
|
||||
|
||||
QString rawGDB;
|
||||
getGDBRawDisp(rawGDB);//获取gdb查看得到的原始值
|
||||
parseGDBRawDisp(rawGDB);//进行正则匹配,更新变量列表内容
|
||||
qint64 timestamp=stampTimer->nsecsElapsed()/1000;//获取时间戳
|
||||
QString rawDisplay=gdb->runCmd("display\r\n");//获取gdb查看得到的原始值
|
||||
for(int index=0;index<varList.size();index++)//依次进行每个变量的匹配
|
||||
{
|
||||
varList[index].rawValue=gdb->captureValueFromDisplay(rawDisplay,varList.at(index).name);//进行正则匹配,截取出变量值部分
|
||||
SamplePoint sample;
|
||||
if(gdb->getDoubleFromDisplayValue(varList[index].rawValue,sample.value))//尝试转换为double,生成采样点并写入采样点列表
|
||||
{
|
||||
sample.timestamp=timestamp;
|
||||
varList[index].samples.append(sample);
|
||||
}
|
||||
}
|
||||
|
||||
isWatchProcessing=false;
|
||||
}
|
||||
|
@ -179,7 +197,9 @@ void MainWindow::slotOnVarAdd2Edit(const QString &name)
|
|||
//选择器窗口添加到列表槽函数
|
||||
void MainWindow::slotOnVarAdd2List(const QString &name)
|
||||
{
|
||||
tableModel->item(tableModel->rowCount()-1,0)->setText(name);
|
||||
QStandardItem *editItem=tableModel->item(tableModel->rowCount()-1,0);
|
||||
editItem->setText("");//清空编辑框
|
||||
editItem->setText(name);//在编辑框中填入变量名,会直接添加到列表中
|
||||
}
|
||||
|
||||
//连接按钮点击,触发连接状态切换
|
||||
|
@ -207,14 +227,23 @@ void MainWindow::setConnState(bool connect)
|
|||
sleep(500);//等待500ms
|
||||
if(ocdProcess->state()==QProcess::Running)//若ocd成功启动
|
||||
{
|
||||
setGDBConnState(true);//连接gdb到ocd
|
||||
gdb->loadSymbolFile(ui->txt_axf_path->text());//设置gdb符号文件
|
||||
gdb->connectToRemote("localhost:3333");//连接gdb到ocd
|
||||
QStringList nameList;
|
||||
for(int index=0;index<varList.size();index++)//向gdb发送变量列表
|
||||
nameList<<varList.at(index).name;
|
||||
gdb->setDisplayList(nameList);
|
||||
|
||||
for(int i=0;i<varList.size();i++)//清空变量列表历史采样点数据
|
||||
varList[i].samples.clear();
|
||||
|
||||
stampTimer->restart();//开启时间戳计时
|
||||
watchTimer->start();//开启定时查看变量值
|
||||
tableTimer->start();//开启表格定时刷新
|
||||
|
||||
ui->bt_conn->setText("断开连接");
|
||||
ui->bt_reset->setEnabled(true);//使能复位按钮
|
||||
|
||||
connected=true;//更新连接标志
|
||||
}
|
||||
ui->bt_conn->setEnabled(true);//恢复连接按钮
|
||||
|
@ -223,8 +252,9 @@ void MainWindow::setConnState(bool connect)
|
|||
{
|
||||
watchTimer->stop();//停止定时查看和定时刷新表格
|
||||
tableTimer->stop();
|
||||
setGDBConnState(false);//断开gdb,停止ocd
|
||||
setOCDState(false);
|
||||
gdb->disconnectFromRemote();//断开gdb
|
||||
gdb->unloadSymbolFile();//卸载符号文件
|
||||
setOCDState(false);//结束ocd进程
|
||||
ui->bt_conn->setText("连接目标");
|
||||
ui->bt_reset->setEnabled(false);//禁用复位按钮
|
||||
connected=false;//更新连接标志
|
||||
|
@ -256,96 +286,6 @@ void MainWindow::setOCDState(bool connect)
|
|||
}
|
||||
}
|
||||
|
||||
//参数true:运行gdb进程;参数false:结束gdb进程
|
||||
void MainWindow::setGDBState(bool run)
|
||||
{
|
||||
if(run)
|
||||
{
|
||||
gdbProcess->setProgram(QCoreApplication::applicationDirPath()+"/gdb/gdb.exe");//设置程序路径
|
||||
gdbProcess->setWorkingDirectory(QCoreApplication::applicationDirPath()+"/gdb");//设置工作路径
|
||||
gdbProcess->setNativeArguments("-q");//设置gdb在安静模式下打开
|
||||
gdbProcess->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
gdbProcess->kill();
|
||||
}
|
||||
}
|
||||
|
||||
//参数true:gdb连接到本机3333端口,设置调试参数;参数false:断开gdb连接
|
||||
void MainWindow::setGDBConnState(bool connect)
|
||||
{
|
||||
QString tmpFilePath=QCoreApplication::applicationDirPath()+"/gdb/tmp";//临时符号文件路径
|
||||
if(connect)
|
||||
{
|
||||
gdbProcess->write("target remote localhost:3333\r\n");//连接到3333端口
|
||||
QFile::remove(tmpFilePath);//确保删除当前的临时文件
|
||||
QFile::copy(ui->txt_axf_path->text(),tmpFilePath);//将所选符号文件复制为临时文件
|
||||
gdbProcess->write(QString("symbol-file %1 \r\n").arg("tmp").toStdString().c_str());//设置符号文件
|
||||
gdbProcess->write("set confirm off\r\n");//设置不要手动确认
|
||||
gdbProcess->write("set print pretty on\r\n");//设置结构体规范打印
|
||||
setGDBDispList();//向gdb发送当前的变量列表
|
||||
}
|
||||
else
|
||||
{
|
||||
gdbProcess->write("symbol-file\r\n");//取消符号文件
|
||||
gdbProcess->write("disconnect\r\n");//断开gdb连接
|
||||
sleep(10);//确保对临时文件取消占用
|
||||
QFile::remove(tmpFilePath);//删除复制过来的临时文件
|
||||
}
|
||||
}
|
||||
|
||||
//向gdb发送变量列表
|
||||
void MainWindow::setGDBDispList()
|
||||
{
|
||||
gdbProcess->write("delete display\r\n");//删除之前发送的变量列表
|
||||
foreach(VarInfo info,varList)
|
||||
gdbProcess->write(QString("display %1 \r\n").arg(info.name).toStdString().c_str());//向display表中依次添加变量名
|
||||
}
|
||||
|
||||
//向gdb请求读取所有变量,获取返回的原始字符串
|
||||
void MainWindow::getGDBRawDisp(QString &raw)
|
||||
{
|
||||
if(!connected)
|
||||
return;
|
||||
gdbProcess->readAllStandardOutput();
|
||||
gdbProcess->write("display\r\n");//向gdb发送display指令
|
||||
raw="";
|
||||
do{
|
||||
gdbProcess->waitForReadyRead();
|
||||
raw+=gdbProcess->readAllStandardOutput();
|
||||
}while(!raw.endsWith("(gdb) "));//不断读取直到读到字符串"gdb ",表示指令执行完成
|
||||
}
|
||||
|
||||
//解析由gdb返回的display原始字符串,更新变量值
|
||||
void MainWindow::parseGDBRawDisp(QString &raw)
|
||||
{
|
||||
qint64 timestamp=stampTimer->nsecsElapsed()/1000;//获取时间戳
|
||||
|
||||
for(int index=0;index<varList.size();index++)//依次进行每个变量的匹配
|
||||
{
|
||||
VarInfo &info=varList[index];
|
||||
|
||||
QString regName="";
|
||||
for(int i=0;i<info.name.length();i++)//将变量名每个转换为16进制格式,用于正则匹配
|
||||
regName+=QString("\\x%1").arg(info.name.at(i).unicode(),0,16);
|
||||
|
||||
QRegExp rx(QString("\\d+:\\s%1\\s=\\s(.*)\\r\\n[\\d\\(]").arg(regName));//正则匹配模板,匹配选中的变量名并截取出变量值
|
||||
rx.setMinimal(true);//使用非贪心模式
|
||||
|
||||
if(rx.indexIn(raw)!=-1)
|
||||
{
|
||||
info.rawValue=rx.cap(1);//正则中截取出的字符串即为变量当前值
|
||||
SamplePoint sample;
|
||||
if(getValueFromRaw(info.rawValue,sample.value))//尝试转换为double,生成采样点并写入采样点列表
|
||||
{
|
||||
sample.timestamp=timestamp;
|
||||
info.samples.append(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//延时指定毫秒数,进行子任务循环
|
||||
void MainWindow::sleep(uint32_t ms)
|
||||
{
|
||||
|
@ -375,7 +315,7 @@ void MainWindow::on_bt_set_axf_clicked()
|
|||
QFileDialog *fileDialog = new QFileDialog(this);//弹出文件选择框
|
||||
fileDialog->setWindowTitle(QStringLiteral("选中文件"));
|
||||
fileDialog->setDirectory(".");
|
||||
fileDialog->setNameFilter(tr("AXF/ELF File (*.axf *.elf)"));//设置文件过滤器为axf/elf
|
||||
fileDialog->setNameFilters(QStringList()<<tr("AXF/ELF File (*.axf *.elf)")<<tr("所有文件 (*)"));//设置文件过滤器
|
||||
fileDialog->setFileMode(QFileDialog::ExistingFile);
|
||||
fileDialog->setViewMode(QFileDialog::Detail);
|
||||
if(fileDialog->exec())
|
||||
|
@ -427,26 +367,6 @@ void MainWindow::redrawTable()
|
|||
tableModel->setItem(lastRow,0,new QStandardItem(""));//末尾添加空行,用于用户添加变量
|
||||
}
|
||||
|
||||
//发送gdb指令,修改变量值
|
||||
void MainWindow::setVar(const QString &name, double value)
|
||||
{
|
||||
if(!connected)
|
||||
return;
|
||||
gdbProcess->write(QString("set %1=%2\r\n").arg(name).arg(value).toStdString().c_str());
|
||||
}
|
||||
|
||||
//尝试从变量值原始字符串中解析出double值,返回是否解析成功
|
||||
bool MainWindow::getValueFromRaw(const QString &rawValue,double &value)
|
||||
{
|
||||
if(rawValue.isEmpty())
|
||||
return false;
|
||||
if(rawValue.contains('{')||rawValue.contains('(')
|
||||
||rawValue.contains('<')||rawValue.contains('['))//若含有这些符号,表示该变量可能为复合类型,不能被解析
|
||||
return false;
|
||||
value=rawValue.mid(0,rawValue.indexOf(' ')).toDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
//保存配置到指定路径的文件
|
||||
void MainWindow::saveToFile(const QString &filename)
|
||||
{
|
||||
|
@ -541,7 +461,7 @@ bool MainWindow::exportCSV(const QString &filename)
|
|||
void MainWindow::on_bt_reset_clicked()
|
||||
{
|
||||
if(connected)
|
||||
gdbProcess->write("monitor reset\r\n");
|
||||
gdb->runCmd("monitor reset\r\n");
|
||||
}
|
||||
|
||||
//表格双击事件,进行图线颜色修改
|
||||
|
|
11
mainwindow.h
11
mainwindow.h
|
@ -24,6 +24,7 @@
|
|||
#include <aboutwindow.h>
|
||||
#include <qnetworkaccessmanager.h>
|
||||
#include <qnetworkreply.h>
|
||||
#include <gdbprocess.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class MainWindow; }
|
||||
|
@ -65,7 +66,8 @@ private slots:
|
|||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
QProcess *ocdProcess,*gdbProcess;//ocd和gdb进程指针
|
||||
QProcess *ocdProcess;
|
||||
GDBProcess *gdb;//GDB进程控制
|
||||
bool connected=false;//标记当前是否已连接
|
||||
QStandardItemModel *tableModel;//表格数据
|
||||
QList<VarInfo> varList;//变量列表
|
||||
|
@ -79,17 +81,10 @@ private:
|
|||
void setStylesheet();
|
||||
void setConnState(bool connect);
|
||||
void setOCDState(bool connect);
|
||||
void setGDBState(bool run);
|
||||
void setGDBConnState(bool connect);
|
||||
void setGDBDispList();
|
||||
void getGDBRawDisp(QString &raw);
|
||||
void parseGDBRawDisp(QString &raw);
|
||||
void sleep(uint32_t ms);
|
||||
void loadConfFileList();
|
||||
void initTable();
|
||||
void redrawTable();
|
||||
void setVar(const QString &name,double value);
|
||||
bool getValueFromRaw(const QString &rawValue,double &value);
|
||||
void saveToFile(const QString &filename);
|
||||
void loadFromFile(const QString &filename);
|
||||
bool exportCSV(const QString &filename);
|
||||
|
|
Loading…
Reference in New Issue