重构,将GDB控制部分提取为单独模块;符号文件选择框过滤器添加"所有文件"

This commit is contained in:
skythinker 2022-01-30 18:32:39 +08:00
parent f3886c3b28
commit a803f3636c
8 changed files with 294 additions and 273 deletions

View File

@ -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 \

View File

@ -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>

182
gdbprocess.cpp Normal file
View File

@ -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;
}
}
}
}
}

37
gdbprocess.h Normal file
View File

@ -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

View File

@ -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下
}

View File

@ -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();
};

View File

@ -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();
}
}
//参数truegdb连接到本机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");
}
//表格双击事件,进行图线颜色修改

View File

@ -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);