/*
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "debugger.h"
#include "gdbmidebugger.h"
#include "utils.h"
#include "utils/parsearg.h"
#include "mainwindow.h"
#include "editor.h"
#include "settings.h"
#include "widgets/cpudialog.h"
#include "systemconsts.h"
#include "editorlist.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "widgets/signalmessagedialog.h"
Debugger::Debugger(QObject *parent) : QObject(parent),
mForceUTF8(false),
mDebuggerType(DebuggerType::GDB),
mLastLoadtime(0),
mProjectLastLoadtime(0)
{
//models deleted in the destructor
mBreakpointModel= std::make_shared(this);
mBacktraceModel = std::make_shared(this);
mWatchModel = std::make_shared(this);
mRegisterModel = std::make_shared(this);
mMemoryModel = std::make_shared(16,this);
connect(mMemoryModel.get(),&MemoryModel::setMemoryData,
this, &Debugger::setMemoryData);
connect(mWatchModel.get(), &WatchModel::setWatchVarValue,
this, &Debugger::setWatchVarValue);
mExecuting = false;
mClient = nullptr;
mTarget = nullptr;
mCommandChanged = false;
mLeftPageIndexBackup = -1;
connect(mWatchModel.get(), &WatchModel::fetchChildren,
this, &Debugger::fetchVarChildren);
setIsForProject(false);
}
Debugger::~Debugger()
{
// delete mBreakpointModel;
// delete mBacktraceModel;
// delete mWatchModel;
// delete mRegisterModel;
// delete mMemoryModel;
}
bool Debugger::startClient(int compilerSetIndex,
const QString& inferior,
bool inferiorHasSymbols,
bool inferiorHasBreakpoints,
const QStringList& binDirs,
const QString& sourceFile)
{
mCurrentSourceFile = sourceFile;
Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(compilerSetIndex);
if (!compilerSet) {
compilerSet = pSettings->compilerSets().defaultSet();
}
if (!compilerSet) {
QMessageBox::critical(pMainWindow,
tr("No compiler set"),
tr("No compiler set is configured.")+tr("Can't start debugging."));
return false;
}
setForceUTF8(compilerSet->forceUTF8());
setDebugInfosUsingUTF8(compilerSet->isDebugInfoUsingUTF8());
if (compilerSet->debugger().endsWith(LLDB_MI_PROGRAM))
setDebuggerType(DebuggerType::LLDB_MI);
else
setDebuggerType(DebuggerType::GDB);
// force to lldb-server if using lldb-mi, which creates new console but does not bind inferior’s stdio to the new console on Windows.
setUseDebugServer(pSettings->debugger().useGDBServer() || mDebuggerType == DebuggerType::LLDB_MI);
mExecuting = true;
QString debuggerPath = compilerSet->debugger();
//QFile debuggerProgram(debuggerPath);
// if (!isTextAllAscii(debuggerPath)) {
// mExecuting = false;
// QMessageBox::critical(pMainWindow,
// tr("Debugger path error"),
// tr("Debugger's path \"%1\" contains non-ascii characters.")
// .arg(debuggerPath)
// + "
"
// + tr("This prevents it from executing."));
// return false;
// }
if (!fileExists(debuggerPath)) {
mExecuting = false;
QMessageBox::critical(pMainWindow,
tr("Debugger not exists"),
tr("Can''t find debugger (gdb) in : \"%1\"").arg(debuggerPath)
+"
"
+tr("Please check the \"program\" page of compiler settings."));
return false;
}
if (useDebugServer()) {
if (!isTextAllAscii(compilerSet->debugServer())) {
mExecuting = false;
QMessageBox::critical(pMainWindow,
tr("GDB Server path error"),
tr("GDB Server's path \"%1\" contains non-ascii characters.")
.arg(compilerSet->debugServer())
+ "
"
+ tr("This prevents it from executing."));
return false;
}
if (!fileExists(compilerSet->debugServer())) {
mExecuting = false;
QMessageBox::critical(pMainWindow,
tr("GDB Server not exists"),
tr("Can''t find gdb server in : \"%1\"").arg(compilerSet->debugServer()));
return false;
}
}
mMemoryModel->reset();
mWatchModel->resetAllVarInfos();
if (useDebugServer()) {
//deleted when thread finished
QStringList params;
if (pSettings->executor().useParams())
params = parseArgumentsWithoutVariables(pSettings->executor().params());
mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort(),params);
if (pSettings->executor().redirectInput())
mTarget->setInputFile(pSettings->executor().inputFilename());
connect(mTarget, &QThread::finished,[this](){
if (mExecuting) {
stop();
}
mTarget->deleteLater();
mTarget = nullptr;
});
mTarget->addBinDirs(binDirs);
mTarget->addBinDir(pSettings->dirs().appDir());
mTarget->start();
mTarget->waitStart();
}
//delete when thread finished
mClient = new GDBMIDebuggerClient(this, debuggerType());
mClient->addBinDirs(binDirs);
mClient->addBinDir(pSettings->dirs().appDir());
mClient->setDebuggerPath(debuggerPath);
connect(mClient, &QThread::finished,this,&Debugger::cleanUpReader);
connect(mClient, &QThread::finished,mMemoryModel.get(),&MemoryModel::reset);
connect(mClient, &DebuggerClient::parseFinished,this,&Debugger::syncFinishedParsing,Qt::BlockingQueuedConnection);
connect(mClient, &DebuggerClient::changeDebugConsoleLastLine,this,&Debugger::onChangeDebugConsoleLastline);
connect(mClient, &DebuggerClient::cmdStarted,pMainWindow, &MainWindow::disableDebugActions);
connect(mClient, &DebuggerClient::cmdFinished,pMainWindow, &MainWindow::enableDebugActions);
connect(mClient, &DebuggerClient::inferiorStopped, pMainWindow, &MainWindow::enableDebugActions);
connect(mClient, &DebuggerClient::breakpointInfoGetted, mBreakpointModel.get(),
&BreakpointModel::updateBreakpointNumber);
connect(mClient, &DebuggerClient::localsUpdated, pMainWindow,
&MainWindow::onLocalsReady);
connect(mClient, &DebuggerClient::memoryUpdated,this,
&Debugger::updateMemory);
connect(mClient, &DebuggerClient::evalUpdated,this,
&Debugger::updateEval);
connect(mClient, &DebuggerClient::disassemblyUpdate,this,
&Debugger::updateDisassembly);
connect(mClient, &DebuggerClient::registerNamesUpdated, this,
&Debugger::updateRegisterNames);
connect(mClient, &DebuggerClient::registerValuesUpdated, this,
&Debugger::updateRegisterValues);
connect(mClient, &DebuggerClient::varCreated,mWatchModel.get(),
&WatchModel::updateVarInfo);
connect(mClient, &DebuggerClient::prepareVarChildren,mWatchModel.get(),
&WatchModel::prepareVarChildren);
connect(mClient, &DebuggerClient::addVarChild,mWatchModel.get(),
&WatchModel::addVarChild);
connect(mClient, &DebuggerClient::varValueUpdated,mWatchModel.get(),
&WatchModel::updateVarValue);
connect(mClient, &DebuggerClient::varsValueUpdated,mWatchModel.get(),
&WatchModel::updateAllHasMoreVars);
connect(mClient, &DebuggerClient::inferiorContinued,pMainWindow,
&MainWindow::removeActiveBreakpoints);
connect(mClient, &DebuggerClient::inferiorStopped,pMainWindow,
&MainWindow::setActiveBreakpoint);
connect(mClient, &DebuggerClient::watchpointHitted,pMainWindow,
&MainWindow::onWatchpointHitted);
connect(mClient, &DebuggerClient::errorNoSymbolTable,pMainWindow,
&MainWindow::stopDebugForNoSymbolTable);
connect(mClient, &DebuggerClient::inferiorStopped,this,
&Debugger::refreshAll);
mClient->start();
mClient->waitStart();
mClient->initialize(inferior, inferiorHasSymbols);
includeOrSkipDirsInSymbolSearch(compilerSet->libDirs(), pSettings->debugger().skipCustomLibraries());
includeOrSkipDirsInSymbolSearch(compilerSet->CIncludeDirs(), pSettings->debugger().skipCustomLibraries());
includeOrSkipDirsInSymbolSearch(compilerSet->CppIncludeDirs(), pSettings->debugger().skipCustomLibraries());
//gcc system libraries is auto loaded by gdb
if (pSettings->debugger().skipSystemLibraries()) {
includeOrSkipDirsInSymbolSearch(compilerSet->defaultCIncludeDirs(),true);
includeOrSkipDirsInSymbolSearch(compilerSet->defaultCIncludeDirs(),true);
includeOrSkipDirsInSymbolSearch(compilerSet->defaultCppIncludeDirs(),true);
}
sendAllBreakpointsToDebugger();
pMainWindow->updateAppTitle();
mInferiorHasBreakpoints = inferiorHasBreakpoints;
return true;
}
void Debugger::runInferior()
{
if (mClient)
mClient->runInferior(mInferiorHasBreakpoints);
}
void Debugger::stop() {
if (mExecuting) {
if (mTarget) {
mTarget->stopDebug();
mTarget = nullptr;
}
mClient->stopDebug();
}
mCurrentSourceFile="";
}
void Debugger::cleanUpReader()
{
if (mExecuting) {
mExecuting = false;
//stop debugger
mClient->deleteLater();
mClient=nullptr;
if (pMainWindow->cpuDialog()!=nullptr) {
pMainWindow->cpuDialog()->close();
}
// Free resources
pMainWindow->removeActiveBreakpoints();
pMainWindow->txtLocals()->clear();
pMainWindow->updateAppTitle();
pMainWindow->updateDebugEval("");
mBacktraceModel->clear();
mWatchModel->clearAllVarInfos();
mBreakpointModel->invalidateAllBreakpointNumbers();
pMainWindow->updateEditorActions();
}
}
void Debugger::updateRegisterNames(const QStringList ®isterNames)
{
mRegisterModel->updateNames(registerNames);
}
void Debugger::updateRegisterValues(const QHash &values)
{
mRegisterModel->updateValues(values);
}
void Debugger::refreshAll()
{
refreshWatchVars();
if (mClient)
mClient->refreshStackVariables();
if (memoryModel()->startAddress()>0
&& mClient)
mClient->readMemory(
QString("%1").arg(memoryModel()->startAddress()),
pSettings->debugger().memoryViewRows(),
pSettings->debugger().memoryViewColumns()
);
}
std::shared_ptr Debugger::registerModel() const
{
return mRegisterModel;
}
std::shared_ptr Debugger::watchModel() const
{
return mWatchModel;
}
bool Debugger::commandRunning()
{
if (mClient) {
return mClient->commandRunning();
}
return false;
}
bool Debugger::inferiorRunning()
{
if (mClient) {
return mClient->inferiorRunning();
}
return false;
}
void Debugger::interrupt()
{
if (mClient)
mClient->interrupt();
}
void Debugger::stepOver()
{
if (mClient)
mClient->stepOver();
}
void Debugger::stepInto()
{
if (mClient)
mClient->stepInto();
}
void Debugger::stepOut()
{
if (mClient)
mClient->stepOut();
}
void Debugger::runTo(const QString &filename, int line)
{
if (mClient)
mClient->runTo(filename, line);
}
void Debugger::resume()
{
if (mClient)
mClient->resume();
}
void Debugger::stepOverInstruction()
{
if (mClient)
mClient->stepOverInstruction();
}
void Debugger::stepIntoInstruction()
{
if (mClient)
mClient->stepIntoInstruction();
}
void Debugger::runClientCommand(const QString &command, const QString ¶ms, DebugCommandSource source)
{
if (!mClient)
return;
if (mClient->clientType()!=DebuggerType::GDB
&& mClient->clientType()!=DebuggerType::LLDB_MI)
return;
GDBMIDebuggerClient* gdbmiClient = dynamic_cast(mClient);
gdbmiClient->postCommand(command, params, source);
}
bool Debugger::isForProject() const
{
return mBreakpointModel->isForProject();
}
void Debugger::setIsForProject(bool newIsForProject)
{
if (!executing()) {
mBreakpointModel->setIsForProject(newIsForProject);
mWatchModel->setIsForProject(newIsForProject);
}
}
void Debugger::clearForProject()
{
mBreakpointModel->clear(true);
mWatchModel->clear(true);
}
void Debugger::addBreakpoint(int line, const Editor* editor)
{
addBreakpoint(line,editor->filename(), editor->inProject());
}
void Debugger::addBreakpoint(int line, const QString &filename, bool forProject)
{
PBreakpoint bp=std::make_shared();
bp->number = -1;
bp->line = line;
bp->filename = filename;
bp->condition = "";
bp->enabled = true;
bp->breakpointType = BreakpointType::Breakpoint;
bp->timestamp = QDateTime::currentMSecsSinceEpoch();
mBreakpointModel->addBreakpoint(bp,forProject);
if (mExecuting) {
if (forProject && mBreakpointModel->isForProject()) {
sendBreakpointCommand(bp);
} else if (filename == mCurrentSourceFile) {
sendBreakpointCommand(bp);
}
}
}
void Debugger::deleteBreakpoints(const QString &filename, bool forProject)
{
const QList& list=mBreakpointModel->breakpoints(forProject);
for (int i=list.size()-1;i>=0;i--) {
PBreakpoint bp = list[i];
if (bp->filename == filename) {
mBreakpointModel->removeBreakpoint(i,forProject);
}
}
}
void Debugger::deleteBreakpoints(const Editor *editor)
{
deleteBreakpoints(editor->filename(),editor->inProject());
}
void Debugger::deleteBreakpoints(bool forProject)
{
mBreakpointModel->clear(forProject);
// for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
// removeBreakpoint(i);
// }
}
void Debugger::deleteInvalidProjectBreakpoints(const QSet unitFiles)
{
for(int i=mBreakpointModel->breakpoints(true).count()-1;i>=0;i--) {
const PBreakpoint& bp=mBreakpointModel->breakpoint(i,true);
if (!unitFiles.contains(bp->filename))
mBreakpointModel->removeBreakpoint(i, true);
}
}
void Debugger::removeBreakpoint(int line, const Editor *editor)
{
removeBreakpoint(line,editor->filename(),editor->inProject());
}
void Debugger::removeBreakpoint(int line, const QString &filename, bool forProject)
{
const QList& breakpoints=mBreakpointModel->breakpoints(forProject);
for (int i=breakpoints.size()-1;i>=0;i--) {
PBreakpoint bp = breakpoints[i];
if (bp->filename == filename && bp->line == line) {
removeBreakpoint(i, forProject);
}
}
}
void Debugger::removeBreakpoint(int index, bool forProject)
{
sendClearBreakpointCommand(index, forProject);
mBreakpointModel->removeBreakpoint(index, forProject);
}
PBreakpoint Debugger::breakpointAt(int line, const QString& filename, int *index , bool forProject)
{
const QList& breakpoints=mBreakpointModel->breakpoints(forProject);
for (*index=0;*indexline == line
&& breakpoint->filename == filename)
return breakpoint;
}
*index=-1;
return PBreakpoint();
}
PBreakpoint Debugger::breakpointAt(int line, const Editor *editor, int *index)
{
return breakpointAt(line,editor->filename(),index, editor->inProject());
}
void Debugger::setBreakPointCondition(int index, const QString &condition, bool forProject)
{
PBreakpoint breakpoint=mBreakpointModel->setBreakPointCondition(index,condition, forProject);
if (mClient)
mClient->setBreakpointCondition(breakpoint);
}
void Debugger::sendAllBreakpointsToDebugger()
{
for (PBreakpoint breakpoint:mBreakpointModel->breakpoints(mBreakpointModel->isForProject())) {
if (mBreakpointModel->isForProject()) {
sendBreakpointCommand(breakpoint);
} else if (breakpoint->filename == mCurrentSourceFile) {
sendBreakpointCommand(breakpoint);
}
}
}
void Debugger::saveForNonproject(const QString &filename)
{
save(filename,QString());
}
void Debugger::saveForProject(const QString &filename, const QString &projectFolder)
{
save(filename,projectFolder);
}
void Debugger::loadForNonproject(const QString &filename)
{
bool forProject = false;
mLastLoadtime = 0;
PDebugConfig pConfig = load(filename, forProject);
if (pConfig->timestamp>0) {
mBreakpointModel->setBreakpoints(pConfig->breakpoints,forProject);
mWatchModel->setWatchVars(pConfig->watchVars,forProject);
}
}
void Debugger::loadForProject(const QString &filename, const QString &projectFolder)
{
bool forProject = true;
mProjectLastLoadtime = 0;
PDebugConfig pConfig = load(filename, forProject);
if (pConfig->timestamp>0) {
foreach (const PBreakpoint& breakpoint, pConfig->breakpoints) {
breakpoint->filename = generateAbsolutePath(projectFolder,breakpoint->filename);
}
mBreakpointModel->setBreakpoints(pConfig->breakpoints,forProject);
mWatchModel->setWatchVars(pConfig->watchVars,forProject);
}
}
void Debugger::addWatchpoint(const QString &expression)
{
QString s=expression.trimmed();
if (mClient) {
mClient->addWatchpoint(expression);
}
}
void Debugger::addWatchVar(const QString &expression)
{
// Don't allow duplicates...
PWatchVar oldVar = mWatchModel->findWatchVar(expression);
if (oldVar)
return;
PWatchVar var = std::make_shared();
var->parent= PWatchVar();
var->expression = expression;
var->value = tr("Execute to evaluate");
var->numChild = 0;
var->hasMore = false;
var->timestamp = QDateTime::currentMSecsSinceEpoch();
addWatchVar(var,isForProject());
}
void Debugger::modifyWatchVarExpression(const QString &oldExpr, const QString &newExpr)
{
// check if name already exists;
PWatchVar var = mWatchModel->findWatchVar(newExpr);
if (var)
return;
var = mWatchModel->findWatchVar(oldExpr);
if (var) {
if (mExecuting && !var->expression.isEmpty())
sendRemoveWatchCommand(var);
var->expression = newExpr;
var->type.clear();
var->value.clear();
var->hasMore = false;
var->numChild=0;
var->name.clear();
var->children.clear();
sendWatchCommand(var);
}
}
void Debugger::refreshWatchVars()
{
if (mClient) {
sendAllWatchVarsToDebugger();
if (mDebuggerType==DebuggerType::LLDB_MI) {
for (PWatchVar var:mWatchModel->watchVars()) {
if (!var->name.isEmpty())
mClient->refreshWatch(var);
}
} else {
mClient->refreshWatch();
}
}
}
void Debugger::fetchVarChildren(const QString &varName)
{
if (mClient) {
mClient->fetchWatchVarChildren(varName);
}
}
bool Debugger::useDebugServer() const
{
return mUseDebugServer;
}
void Debugger::setUseDebugServer(bool newUseDebugServer)
{
mUseDebugServer = newUseDebugServer;
}
bool Debugger::debugInfosUsingUTF8() const
{
return mDebugInfosUsingUTF8;
}
void Debugger::setDebugInfosUsingUTF8(bool newDebugInfosUsingUTF8)
{
mDebugInfosUsingUTF8 = newDebugInfosUsingUTF8;
}
DebuggerType Debugger::debuggerType() const
{
return mDebuggerType;
}
void Debugger::setDebuggerType(DebuggerType newDebuggerType)
{
mDebuggerType = newDebuggerType;
}
bool Debugger::forceUTF8() const
{
return mForceUTF8;
}
void Debugger::setForceUTF8(bool newForceUTF8)
{
mForceUTF8 = newForceUTF8;
}
std::shared_ptr Debugger::memoryModel() const
{
return mMemoryModel;
}
void Debugger::removeWatchVars(bool deleteparent)
{
if (deleteparent) {
mWatchModel->clear();
} else {
for(const PWatchVar& var:mWatchModel->watchVars()) {
sendRemoveWatchCommand(var);
}
mWatchModel->clearAllVarInfos();
}
}
void Debugger::removeWatchVar(const QModelIndex &index)
{
PWatchVar var = mWatchModel->findWatchVar(index);
if (!var)
return;
sendRemoveWatchCommand(var);
mWatchModel->removeWatchVar(index);
}
void Debugger::sendAllWatchVarsToDebugger()
{
for (PWatchVar var:mWatchModel->watchVars()) {
if (var->name.isEmpty())
sendWatchCommand(var);
}
}
PWatchVar Debugger::findWatchVar(const QString &expression)
{
return mWatchModel->findWatchVar(expression);
}
PWatchVar Debugger::watchVarAt(const QModelIndex &index)
{
return mWatchModel->findWatchVar(index);
}
void Debugger::readMemory(const QString &startAddress, int rows, int cols)
{
if (mClient)
mClient->readMemory(startAddress, rows, cols);
}
void Debugger::evalExpression(const QString &expression)
{
if (mClient)
mClient->evalExpression(expression);
}
void Debugger::selectFrame(PTrace trace)
{
if (mClient)
mClient->selectFrame(trace);
}
void Debugger::refreshFrame()
{
if (mClient) {
mClient->refreshFrame();
}
}
void Debugger::refreshStackVariables()
{
if (mClient)
mClient->refreshStackVariables();
}
void Debugger::refreshRegisters()
{
if (mClient)
mClient->refreshRegisters();
}
void Debugger::disassembleCurrentFrame(bool blendMode)
{
if (mClient)
mClient->disassembleCurrentFrame(blendMode);
}
void Debugger::setDisassemblyLanguage(bool isIntel)
{
if (mClient)
mClient->setDisassemblyLanguage(isIntel);
}
//void Debugger::notifyWatchVarUpdated(PWatchVar var)
//{
// mWatchModel->notifyUpdated(var);
//}
std::shared_ptr Debugger::backtraceModel()
{
return mBacktraceModel;
}
std::shared_ptr Debugger::breakpointModel()
{
return mBreakpointModel;
}
void Debugger::sendWatchCommand(PWatchVar var)
{
if (mClient)
mClient->addWatch(var->expression);
}
void Debugger::sendRemoveWatchCommand(PWatchVar var)
{
if (mClient)
mClient->removeWatch(var);
}
void Debugger::sendBreakpointCommand(PBreakpoint breakpoint)
{
if (mClient)
mClient->addBreakpoint(breakpoint);
}
void Debugger::sendClearBreakpointCommand(int index, bool forProject)
{
sendClearBreakpointCommand(mBreakpointModel->breakpoints(forProject)[index]);
}
void Debugger::sendClearBreakpointCommand(PBreakpoint breakpoint)
{
if (mClient)
mClient->removeBreakpoint(breakpoint);
}
QJsonArray BreakpointModel::toJson(const QString& projectFolder)
{
bool forProject = !projectFolder.isEmpty();
QJsonArray array;
foreach (const PBreakpoint& breakpoint, breakpoints(forProject)) {
QJsonObject obj;
if (forProject)
obj["filename"]=extractRelativePath(projectFolder, breakpoint->filename);
else
obj["filename"]=breakpoint->filename;
obj["line"]=breakpoint->line;
obj["condition"]=breakpoint->condition;
obj["enabled"]=breakpoint->enabled;
obj["breakpoint_type"] = static_cast(breakpoint->breakpointType);
obj["timestamp"]=QString("%1").arg(breakpoint->timestamp);
array.append(obj);
}
return array;
}
void BreakpointModel::setBreakpoints(const QList &list, bool forProject)
{
if (mIsForProject == forProject)
beginResetModel();
if (forProject) {
mProjectBreakpoints = list;
} else {
mBreakpoints = list;
}
if (mIsForProject == forProject)
endResetModel();
}
void Debugger::save(const QString &filename, const QString& projectFolder)
{
bool forProject=!projectFolder.isEmpty();
QList breakpoints;
QList watchVars=mWatchModel->watchVars(forProject);
QSet breakpointCompareSet;
QSet watchVarCompareSet;
if (forProject) {
//convert project file's absolute path to relative path
foreach (const PBreakpoint& breakpoint, mBreakpointModel->breakpoints(forProject)) {
QString filename = extractRelativePath(projectFolder, breakpoint->filename);
QString key = QString("%1-%2").arg(filename).arg(breakpoint->line);
breakpointCompareSet.insert(key);
}
} else {
foreach (const PBreakpoint& breakpoint, mBreakpointModel->breakpoints(forProject)) {
QString key = QString("%1-%2").arg(breakpoint->filename).arg(breakpoint->line);
breakpointCompareSet.insert(key);
}
}
foreach (const PWatchVar& watchVar, watchVars) {
watchVarCompareSet.insert(watchVar->expression);
}
std::shared_ptr pConfig;
try {
pConfig = load(filename, forProject);
} catch (FileError& e) {
}
QFile file(filename);
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
foreach (const PBreakpoint& breakpoint, pConfig->breakpoints) {
QString key = QString("%1-%2").arg(breakpoint->filename).arg(breakpoint->line);
if (!breakpointCompareSet.contains(key)) {
breakpointCompareSet.insert(key);
if (forProject)
breakpoint->filename=generateAbsolutePath(projectFolder,breakpoint->filename);
mBreakpointModel->addBreakpoint(breakpoint,forProject);
}
}
foreach (const PWatchVar& watchVar, pConfig->watchVars) {
QString key = watchVar->expression;
if (!watchVarCompareSet.contains(key)) {
watchVarCompareSet.insert(key);
addWatchVar(watchVar,forProject);
}
}
qint64 saveTimestamp = QDateTime::currentMSecsSinceEpoch();;
if (forProject) {
mProjectLastLoadtime = saveTimestamp;
} else {
mLastLoadtime = saveTimestamp;
}
QJsonObject rootObj;
rootObj["timestamp"] = QString("%1").arg(saveTimestamp);
if (forProject) {
rootObj["breakpoints"] = mBreakpointModel->toJson(projectFolder);
}
rootObj["watchvars"] = mWatchModel->toJson(forProject);
QJsonDocument doc;
doc.setObject(rootObj);
if (file.write(doc.toJson())<0) {
throw FileError(tr("Save file '%1' failed.")
.arg(filename));
}
} else {
throw FileError(tr("Can't open file '%1' for write.")
.arg(filename));
}
}
PDebugConfig Debugger::load(const QString &filename, bool forProject)
{
qint64 criteriaTimestamp;
if (forProject) {
criteriaTimestamp = mProjectLastLoadtime;
} else {
criteriaTimestamp = mLastLoadtime;
}
std::shared_ptr pConfig=std::make_shared();
pConfig->timestamp=0;
QFile file(filename);
if (!file.exists())
return pConfig;
if (file.open(QFile::ReadOnly)) {
QByteArray content = file.readAll().trimmed();
if (content.isEmpty())
return pConfig;
QJsonParseError error;
QJsonDocument doc(QJsonDocument::fromJson(content,&error));
if (error.error != QJsonParseError::NoError) {
throw FileError(tr("Error in json file '%1':%2 : %3")
.arg(filename)
.arg(error.offset)
.arg(error.errorString()));
}
QJsonObject rootObject = doc.object();
qint64 timestamp = rootObject["timestamp"].toString().toLongLong();
if (timestamp <= criteriaTimestamp)
return pConfig;
pConfig->timestamp = timestamp;
pConfig->breakpoints = mBreakpointModel->loadJson(rootObject["breakpoints"].toArray(),criteriaTimestamp);
pConfig->watchVars = mWatchModel->loadJson(rootObject["watchvars"].toArray(), criteriaTimestamp);
if (forProject) {
mProjectLastLoadtime = QDateTime::currentMSecsSinceEpoch();
} else {
mLastLoadtime = QDateTime::currentMSecsSinceEpoch();
}
} else {
throw FileError(tr("Can't open file '%1' for read.")
.arg(filename));
}
return pConfig;
}
void Debugger::addWatchVar(const PWatchVar &watchVar, bool forProject)
{
mWatchModel->addWatchVar(watchVar,forProject);
if (forProject == isForProject())
sendWatchCommand(watchVar);
}
void Debugger::includeOrSkipDirsInSymbolSearch(const QStringList &dirs, bool skip)
{
if (skip) {
mClient->skipDirectoriesInSymbolSearch(dirs);
} else {
mClient->addSymbolSearchDirectories(dirs);
}
}
void Debugger::syncFinishedParsing()
{
bool spawnedcpuform = false;
// GDB determined that the source code is more recent than the executable. Ask the user if he wants to rebuild.
if (mClient->receivedSFWarning()) {
if (QMessageBox::question(pMainWindow,
tr("Compile"),
tr("Source file is more recent than executable.")+"
" + tr("Recompile?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes
) == QMessageBox::Yes) {
stop();
pMainWindow->compile();
return;
}
}
// show command output
if (pSettings->debugger().enableDebugConsole() ) {
if (pSettings->debugger().showDetailLog()) {
for (const QString& line:mClient->fullOutput()) {
pMainWindow->addDebugOutput(line);
}
//pMainWindow->addDebugOutput("(gdb)");
} else {
// if (mClient->currentCmd() && mClient->currentCmd()->command == "disas") {
// } else {
// for (const QString& line:mClient->consoleOutput()) {
// pMainWindow->addDebugOutput(line);
// }
// if (
// (mClient->currentCmd()
// && mClient->currentCmd()->source== DebugCommandSource::Console)
// || !mClient->consoleOutput().isEmpty() ) {
// pMainWindow->addDebugOutput("(gdb)");
// }
// }
for (const QString& line:mClient->consoleOutput()) {
pMainWindow->addDebugOutput(line);
}
}
}
// The program to debug has stopped. Stop the debugger
if (mClient->processExited()) {
stop();
return;
}
if (mClient->signalReceived()
&& mClient->signalName()!="SIGINT"
&& mClient->signalName()!="SIGTRAP") {
SignalMessageDialog dialog(pMainWindow);
dialog.setOpenCPUInfo(pSettings->debugger().openCPUInfoWhenSignaled());
dialog.setMessage(
tr("Signal \"%1\" Received: ").arg(mClient->signalName())
+ "
"
+ mClient->signalMeaning());
int result = dialog.exec();
if (result == QDialog::Accepted && dialog.openCPUInfo()) {
pMainWindow->showCPUInfoDialog();
}
}
// CPU form updates itself when spawned, don't update twice!
if ((mClient->updateCPUInfo() && !spawnedcpuform) && (pMainWindow->cpuDialog()!=nullptr)) {
pMainWindow->cpuDialog()->updateInfo();
}
}
void Debugger::setMemoryData(qulonglong address, unsigned char data)
{
if (mClient)
mClient->writeMemory(address, data);
refreshAll();
}
void Debugger::setWatchVarValue(const QString &name, const QString &value)
{
if (mClient)
mClient->writeWatchVar(name, value);
refreshAll();
}
void Debugger::updateMemory(const QStringList &value)
{
mMemoryModel->updateMemory(value);
emit memoryExamineReady(value);
}
void Debugger::updateEval(const QString &value)
{
emit evalValueReady(value);
}
void Debugger::updateDisassembly(const QString& file, const QString& func, const QStringList &value)
{
if (pMainWindow->cpuDialog()) {
pMainWindow->cpuDialog()->setDisassembly(file,func,value,mBacktraceModel->backtraces());
}
}
void Debugger::onChangeDebugConsoleLastline(const QString& text)
{
//pMainWindow->changeDebugOutputLastline(text);
pMainWindow->addDebugOutput(text);
}
int Debugger::leftPageIndexBackup() const
{
return mLeftPageIndexBackup;
}
void Debugger::setLeftPageIndexBackup(int leftPageIndexBackup)
{
mLeftPageIndexBackup = leftPageIndexBackup;
}
bool Debugger::executing() const
{
return mExecuting;
}
DebuggerClient::DebuggerClient(Debugger* debugger, QObject *parent) : QThread(parent),
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
mCmdQueueMutex(),
#else
mCmdQueueMutex(QMutex::Recursive),
#endif
mStartSemaphore(0)
{
mDebugger = debugger;
mCmdRunning = false;
}
const QStringList &DebuggerClient::binDirs() const
{
return mBinDirs;
}
void DebuggerClient::addBinDirs(const QStringList &binDirs)
{
mBinDirs.append(binDirs);
}
void DebuggerClient::addBinDir(const QString &binDir)
{
mBinDirs.append(binDir);
}
const QString &DebuggerClient::signalMeaning() const
{
return mSignalMeaning;
}
const QString &DebuggerClient::signalName() const
{
return mSignalName;
}
bool DebuggerClient::inferiorRunning() const
{
return mInferiorRunning;
}
const QStringList &DebuggerClient::fullOutput() const
{
return mFullOutput;
}
bool DebuggerClient::receivedSFWarning() const
{
return mReceivedSFWarning;
}
bool DebuggerClient::updateCPUInfo() const
{
return mUpdateCPUInfo;
}
const QStringList &DebuggerClient::consoleOutput() const
{
return mConsoleOutput;
}
bool DebuggerClient::signalReceived() const
{
return mSignalReceived;
}
bool DebuggerClient::processExited() const
{
return mProcessExited;
}
QString DebuggerClient::debuggerPath() const
{
return mDebuggerPath;
}
void DebuggerClient::setDebuggerPath(const QString &debuggerPath)
{
mDebuggerPath = debuggerPath;
}
void DebuggerClient::waitStart()
{
mStartSemaphore.acquire(1);
}
BreakpointModel::BreakpointModel(QObject *parent):QAbstractTableModel(parent),
mIsForProject(false)
{
}
int BreakpointModel::rowCount(const QModelIndex &) const
{
return breakpoints(mIsForProject).size();
}
int BreakpointModel::columnCount(const QModelIndex &) const
{
return 3;
}
QVariant BreakpointModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
const QList &list=breakpoints(mIsForProject);
if (index.row()<0 || index.row() >= static_cast(list.size()))
return QVariant();
PBreakpoint breakpoint = list[index.row()];
if (!breakpoint)
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0: {
return extractFileName(breakpoint->filename);
}
case 1:
if (breakpoint->line>0)
return breakpoint->line;
else
return "";
case 2:
return breakpoint->condition;
default:
return QVariant();
}
case Qt::ToolTipRole:
switch (index.column()) {
case 0:
return breakpoint->filename;
case 1:
if (breakpoint->line>0)
return breakpoint->line;
else
return "";
case 2:
return breakpoint->condition;
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Filename");
case 1:
return tr("Line");
case 2:
return tr("Condition");
}
}
return QVariant();
}
void BreakpointModel::addBreakpoint(PBreakpoint p, bool forProject)
{
if (forProject) {
if (forProject==mIsForProject)
beginInsertRows(QModelIndex(),mProjectBreakpoints.count(),mProjectBreakpoints.count());
mProjectBreakpoints.push_back(p);
} else {
if (forProject==mIsForProject)
beginInsertRows(QModelIndex(),mBreakpoints.count(),mBreakpoints.count());
mBreakpoints.push_back(p);
}
if (forProject==mIsForProject)
endInsertRows();
}
void BreakpointModel::clear(bool forProject)
{
if (forProject == mIsForProject)
beginResetModel();
if (forProject)
mProjectBreakpoints.clear();
else
mBreakpoints.clear();
if (forProject == mIsForProject)
endResetModel();
}
void BreakpointModel::removeBreakpoint(int row, bool forProject)
{
if (forProject==mIsForProject)
beginRemoveRows(QModelIndex(),row,row);
if (forProject)
mProjectBreakpoints.removeAt(row);
else
mBreakpoints.removeAt(row);
if (forProject==mIsForProject)
endRemoveRows();
}
void BreakpointModel::removeBreakpointsInFile(const QString &fileName, bool forProject)
{
QList & lst=forProject?mProjectBreakpoints:mBreakpoints;
for (int i=lst.count()-1;i>=0;i--) {
if (lst[i]->filename==fileName)
removeBreakpoint(i,forProject);
}
}
void BreakpointModel::renameBreakpointFilenames(const QString &oldFileName, const QString &newFileName, bool forProject)
{
QList & lst=forProject?mProjectBreakpoints:mBreakpoints;
for (int i=lst.count()-1;i>=0;i--) {
if (lst[i]->filename==oldFileName) {
lst[i]->filename=newFileName;
if (forProject == mIsForProject) {
QModelIndex index=createIndex(i,0);
emit dataChanged(index,index);
}
}
}
}
void BreakpointModel::invalidateAllBreakpointNumbers()
{
foreach (PBreakpoint bp,mBreakpoints) {
bp->number = -1;
}
foreach (PBreakpoint bp,mProjectBreakpoints) {
bp->number = -1;
}
//emit dateChanged(createIndex(0,0),)
}
PBreakpoint BreakpointModel::setBreakPointCondition(int index, const QString &condition,bool forProject)
{
PBreakpoint breakpoint = breakpoints(forProject)[index];
breakpoint->condition = condition;
if (forProject==mIsForProject)
emit dataChanged(createIndex(index,0),createIndex(index,2));
return breakpoint;
}
PBreakpoint BreakpointModel::breakpoint(int index, bool forProject) const
{
const QList list=breakpoints(forProject);
if (index<0 && index>=list.count())
return PBreakpoint();
return list[index];
}
void BreakpointModel::updateBreakpointNumber(const QString& filename, int line, int number)
{
foreach (PBreakpoint bp, breakpoints(mIsForProject)) {
if (bp->filename == filename && bp->line == line) {
bp->number = number;
return;
}
}
}
void BreakpointModel::onFileDeleteLines(const QString& filename, int startLine, int count, bool forProject)
{
const QList &list=breakpoints(forProject);
for (int i = list.count()-1;i>=0;i--){
PBreakpoint breakpoint = list[i];
if (breakpoint->filename == filename
&& breakpoint->line>=startLine) {
if (breakpoint->line >= startLine+count) {
breakpoint->line -= count;
if (forProject==mIsForProject)
emit dataChanged(createIndex(i,0),createIndex(i,2));
} else {
removeBreakpoint(i,forProject);
}
}
}
}
void BreakpointModel::onFileInsertLines(const QString& filename, int startLine, int count, bool forProject)
{
const QList &list=breakpoints(forProject);
for (int i = list.count()-1;i>=0;i--){
PBreakpoint breakpoint = list[i];
if (breakpoint->filename == filename
&& breakpoint->line>=startLine) {
breakpoint->line+=count;
if (forProject == mIsForProject)
emit dataChanged(createIndex(i,0),createIndex(i,2));
}
}
}
bool BreakpointModel::isForProject() const
{
return mIsForProject;
}
void BreakpointModel::setIsForProject(bool newIsForProject)
{
if (mIsForProject!=newIsForProject) {
beginResetModel();
mIsForProject = newIsForProject;
endResetModel();
}
}
QList BreakpointModel::loadJson(const QJsonArray& jsonArray, qint64 criteriaTime)
{
QList result;
for (int i=0;i criteriaTime) {
PBreakpoint breakpoint = std::make_shared();
breakpoint->filename = obj["filename"].toString();
breakpoint->line = obj["line"].toInt();
breakpoint->condition = obj["condition"].toString();
breakpoint->enabled = obj["enabled"].toBool();
breakpoint->breakpointType = static_cast(obj["breakpoint_type"].toInt());
breakpoint->timestamp = timestamp;
result.append(breakpoint);
}
}
return result;
}
BacktraceModel::BacktraceModel(QObject *parent):QAbstractTableModel(parent)
{
}
int BacktraceModel::rowCount(const QModelIndex &) const
{
return mList.size();
}
int BacktraceModel::columnCount(const QModelIndex &) const
{
return 3;
}
QVariant BacktraceModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row() >= static_cast(mList.size()))
return QVariant();
PTrace trace = mList[index.row()];
if (!trace)
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return trace->funcname;
case 1:
return trace->filename;
case 2:
if (trace->line>0)
return trace->line;
else
return "";
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant BacktraceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Function");
case 1:
return tr("Filename");
case 2:
return tr("Line");
}
}
return QVariant();
}
void BacktraceModel::addTrace(PTrace p)
{
beginInsertRows(QModelIndex(),mList.size(),mList.size());
mList.push_back(p);
endInsertRows();
}
void BacktraceModel::clear()
{
beginResetModel();
mList.clear();
endResetModel();
}
void BacktraceModel::removeTrace(int row)
{
beginRemoveRows(QModelIndex(),row,row);
mList.removeAt(row);
endRemoveRows();
}
const QList &BacktraceModel::backtraces() const
{
return mList;
}
PTrace BacktraceModel::backtrace(int index) const
{
if (index>=0 && index < mList.count()){
return mList[index];
}
return PTrace();
}
WatchModel::WatchModel(QObject *parent):QAbstractItemModel(parent)
{
mUpdateCount = 0;
mIsForProject = false;
}
QVariant WatchModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
WatchVar* item = static_cast(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
switch(index.column()) {
case 0:
return item->expression;
case 1:
return item->type;
case 2:
return item->value;
}
}
return QVariant();
}
QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row,column,parent))
return QModelIndex();
WatchVar* parentItem;
PWatchVar pChild;
if (!parent.isValid()) {
parentItem = nullptr;
pChild = watchVars(mIsForProject)[row];
} else {
parentItem = static_cast(parent.internalPointer());
pChild = parentItem->children[row];
}
if (pChild) {
return createIndex(row,column,pChild.get());
}
return QModelIndex();
}
static int getWatchIndex(WatchVar* var, const QList &list) {
for (int i=0;i(index.internalPointer());
PWatchVar parentItem = childItem->parent.lock();
//parent is root
if (parentItem == nullptr) {
return QModelIndex();
}
int row;
PWatchVar grandItem = parentItem->parent.lock();
if (grandItem == nullptr) {
row = getWatchIndex(parentItem.get(), watchVars(mIsForProject));
} else {
row = getWatchIndex(parentItem.get(), grandItem->children);
}
return createIndex(row,0,parentItem.get());
}
int WatchModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return watchVars(mIsForProject).count();
} else {
WatchVar* parentItem = static_cast(parent.internalPointer());
return parentItem->children.count();
}
}
int WatchModel::columnCount(const QModelIndex&) const
{
return 3;
}
void WatchModel::addWatchVar(PWatchVar watchVar, bool forProject)
{
QList &vars=(forProject?mProjectWatchVars:mWatchVars);
for (PWatchVar var:vars) {
if (watchVar->expression == var->expression) {
return;
}
}
if (forProject==mIsForProject)
beginInsertRows(QModelIndex(),vars.count(),vars.count());
vars.append(watchVar);
if (forProject==mIsForProject)
endInsertRows();
}
void WatchModel::setWatchVars(const QList list, bool forProject)
{
if (mIsForProject == forProject)
beginResetModel();
if (forProject) {
mProjectWatchVars = list;
} else {
mWatchVars = list;
}
if (mIsForProject == forProject)
endResetModel();
}
void WatchModel::removeWatchVar(const QString &express)
{
QList &vars=(mIsForProject?mProjectWatchVars:mWatchVars);
for (int i=vars.size()-1;i>=0;i--) {
PWatchVar var = vars[i];
if (express == var->expression) {
QModelIndex parentIndex = index(var->parent.lock());
beginRemoveRows(parentIndex,i,i);
if (mVarIndex.contains(var->name))
mVarIndex.remove(var->name);
vars.removeAt(i);
endRemoveRows();
}
}
}
void WatchModel::removeWatchVar(const QModelIndex &index)
{
int r=index.row();
beginRemoveRows(QModelIndex(),r,r);
QList &vars=(mIsForProject?mProjectWatchVars:mWatchVars);
PWatchVar var = vars[r];
if (mVarIndex.contains(var->name))
mVarIndex.remove(var->name);
vars.removeAt(r);
endRemoveRows();
}
void WatchModel::clear()
{
beginResetModel();
QList &vars=(mIsForProject?mProjectWatchVars:mWatchVars);
vars.clear();
endResetModel();
}
void WatchModel::clear(bool forProject)
{
if (mIsForProject == forProject)
beginResetModel();
QList &vars=(forProject?mProjectWatchVars:mWatchVars);
vars.clear();
if (mIsForProject == forProject)
endResetModel();
}
const QList &WatchModel::watchVars() const
{
return watchVars(mIsForProject);
}
PWatchVar WatchModel::findWatchVar(const QModelIndex &index)
{
if (!index.isValid())
return PWatchVar();
int r=index.row();
return watchVars(mIsForProject)[r];
}
PWatchVar WatchModel::findWatchVar(const QString &expr)
{
foreach (const PWatchVar &var, watchVars(mIsForProject)) {
if (expr == QString("\"%1\"").arg(var->expression)) {
return var;
}
}
return PWatchVar();
}
void WatchModel::resetAllVarInfos()
{
beginResetModel();
foreach (PWatchVar var, watchVars(mIsForProject)) {
var->name.clear();
var->value = tr("Not Valid");
var->numChild = 0;
var->hasMore = false;
var->type.clear();
var->children.clear();
}
mVarIndex.clear();
endResetModel();
}
void WatchModel::updateVarInfo(const QString &expression, const QString &name, int numChild, const QString &value, const QString &type, bool hasMore)
{
PWatchVar var = findWatchVar(expression);
if (!var)
return;
var->name = name;
var->value = value;
var->numChild = numChild;
var->hasMore = hasMore;
var->type = type;
mVarIndex.insert(name,var);
QModelIndex idx = index(var);
if (!idx.isValid())
return;
emit dataChanged(idx,createIndex(idx.row(),2,var.get()));
}
void WatchModel::prepareVarChildren(const QString &parentName, int numChild, bool hasMore)
{
PWatchVar var = mVarIndex.value(parentName,PWatchVar());
if (var) {
var->numChild = numChild;
var->hasMore = hasMore;
if (var->children.count()>0) {
beginRemoveRows(index(var),0,var->children.count()-1);
var->children.clear();
endRemoveRows();
}
}
}
void WatchModel::addVarChild(const QString &parentName, const QString &name,
const QString &exp, int numChild, const QString &value,
const QString &type, bool hasMore)
{
PWatchVar var = mVarIndex.value(parentName,PWatchVar());
if (!var)
return;
beginInsertRows(index(var),var->children.count(),var->children.count());
PWatchVar child = std::make_shared();
child->name = name;
child->expression = exp;
child->numChild = numChild;
child->value = value;
child->type = type;
child->hasMore = hasMore;
child->parent = var;
child->timestamp = QDateTime::currentMSecsSinceEpoch();
var->children.append(child);
endInsertRows();
mVarIndex.insert(name,child);
}
void WatchModel::updateVarValue(const QString &name, const QString &val, const QString &inScope, bool typeChanged, const QString &newType, int newNumChildren, bool hasMore)
{
PWatchVar var = mVarIndex.value(name,PWatchVar());
if (!var)
return;
if (inScope == "true") {
var->value = val;
} else{
var->value = tr("Not Valid");
}
if (typeChanged) {
var->type = newType;
}
QModelIndex idx = index(var);
bool oldHasMore = var->hasMore;
var->hasMore = hasMore;
if (newNumChildren>=0
&& var->numChild!=newNumChildren) {
var->numChild = newNumChildren;
fetchMore(idx);
} else if (!oldHasMore && hasMore) {
fetchMore(idx);
}
emit dataChanged(idx,createIndex(idx.row(),2,var.get()));
}
void WatchModel::updateAllHasMoreVars()
{
foreach (const PWatchVar& var, mVarIndex.values()) {
if (var->hasMore) {
QModelIndex idx = index(var);
fetchMore(idx);
}
}
}
bool WatchModel::isForProject() const
{
return mIsForProject;
}
void WatchModel::setIsForProject(bool newIsForProject)
{
if (newIsForProject!=mIsForProject) {
beginResetModel();
mVarIndex.clear();
mIsForProject=newIsForProject;
endResetModel();
}
}
const QList &WatchModel::watchVars(bool forProject) const
{
return forProject?mProjectWatchVars:mWatchVars;
}
void WatchModel::clearAllVarInfos()
{
beginResetModel();
foreach (PWatchVar var, watchVars(mIsForProject)) {
var->name.clear();
var->value = tr("Execute to evaluate");
var->numChild = 0;
var->hasMore = false;
var->type.clear();
var->children.clear();
}
mVarIndex.clear();
endResetModel();
}
void WatchModel::beginUpdate()
{
if (mUpdateCount == 0) {
beginResetModel();
}
mUpdateCount++;
}
void WatchModel::endUpdate()
{
mUpdateCount--;
if (mUpdateCount == 0) {
endResetModel();
}
}
void WatchModel::notifyUpdated(PWatchVar var)
{
if (!var)
return;
int row;
PWatchVar parent = var->parent.lock();
if (parent==nullptr) {
row = watchVars(mIsForProject).indexOf(var);
} else {
row = parent->children.indexOf(var);
}
if (row<0)
return;
//qDebug()<<"dataChanged"<text;
emit dataChanged(createIndex(row,0,var.get()),createIndex(row,0,var.get()));
}
QJsonArray WatchModel::toJson(bool forProject)
{
QJsonArray array;
foreach (const PWatchVar& watchVar, watchVars(forProject)) {
QJsonObject obj;
obj["expression"]=watchVar->expression;
obj["timestamp"]=QString("%1").arg(watchVar->timestamp);
array.append(obj);
}
return array;
}
QModelIndex WatchModel::index(PWatchVar var) const
{
if (!var)
return QModelIndex();
return index(var.get());
}
QModelIndex WatchModel::index(WatchVar* pVar) const {
if (pVar==nullptr)
return QModelIndex();
PWatchVar parent=pVar->parent.lock();
if (parent) {
int row=-1;
for (int i=0;ichildren.count();i++) {
if (parent->children[i].get() == pVar) {
row = i;
break;
}
}
if (row<0)
return QModelIndex();
return createIndex(row,0,pVar);
} else {
const QList &vars=watchVars(mIsForProject);
int row=-1;
for (int i=0;i WatchModel::loadJson(const QJsonArray &jsonArray, qint64 criteriaTimestamp)
{
QList result;
QJsonArray array = jsonArray;
for (int i=0;icriteriaTimestamp) {
PWatchVar var = std::make_shared();
var->parent= PWatchVar();
var->expression = obj["expression"].toString();
var->value = tr("Execute to evaluate");
var->numChild = 0;
var->hasMore=false;
var->timestamp = timestamp;
result.append(var);
}
}
return result;
}
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid()) {
return false;
}
if (index.column()==2 && role == Qt::EditRole) {
WatchVar* item = static_cast(index.internalPointer());
emit setWatchVarValue(item->name,value.toString());
}
return false;
}
Qt::ItemFlags WatchModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (!index.isValid()) {
return Qt::ItemIsEnabled;
}
if (index.column() == 2) {
WatchVar* item = static_cast(index.internalPointer());
if (item->numChild==0 && !item->type.isEmpty())
flags |= Qt::ItemIsEditable;
}
return flags;
}
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Expression");
case 1:
return tr("Type");
case 2:
return tr("Value");
}
}
return QVariant();
}
void WatchModel::fetchMore(const QModelIndex &parent)
{
if (!parent.isValid()) {
return;
}
WatchVar* item = static_cast(parent.internalPointer());
item->hasMore = false;
item->numChild = item->children.count();
emit fetchChildren(item->name);
}
bool WatchModel::canFetchMore(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return false;
}
WatchVar* item = static_cast(parent.internalPointer());
return item->numChild>item->children.count() || item->hasMore;
}
bool WatchModel::hasChildren(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return true;
}
WatchVar* item = static_cast(parent.internalPointer());
return item->numChild>0 || item->hasMore;
}
RegisterModel::RegisterModel(QObject *parent):QAbstractTableModel(parent)
{
#if defined(ARCH_X86_64) || defined(ARCH_X86)
//https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
mRegisterDescriptions.insert("rax",tr("64-bit")+" "+tr("Accumulator for operands and results data"));
mRegisterDescriptions.insert("rbx",tr("64-bit")+" "+tr("Pointer to data in the DS segment"));
mRegisterDescriptions.insert("rcx",tr("64-bit")+" "+tr("Counter for string and loop operations"));
mRegisterDescriptions.insert("rdx",tr("64-bit")+" "+tr("I/O pointer"));
mRegisterDescriptions.insert("rsi",tr("64-bit")+" "+tr("Source index for string operations; Pointer to data in the segment pointed to by the DS register"));
mRegisterDescriptions.insert("rdi",tr("64-bit")+" "+tr("Destination index for string operations; Pointer to data (or destination) in the segment pointed to by the ES register"));
mRegisterDescriptions.insert("rsp",tr("64-bit")+" "+tr("Stack pointer (in the SS segment)"));
mRegisterDescriptions.insert("rbp",tr("64-bit")+" "+tr("Pointer to data on the stack (in the SS segment)"));
mRegisterDescriptions.insert("r8",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r9",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r10",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r11",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r12",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r13",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r14",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r15",tr("64-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("rip",tr("64-bit")+" "+tr("Instruction pointer"));
mRegisterDescriptions.insert("rflags",tr("Flags"));
mRegisterDescriptions.insert("eflags",tr("Flags"));
mRegisterDescriptions.insert("eax",tr("32-bit")+" "+tr("Accumulator for operands and results data"));
mRegisterDescriptions.insert("ebx",tr("32-bit")+" "+tr("Pointer to data in the DS segment"));
mRegisterDescriptions.insert("ecx",tr("32-bit")+" "+tr("Counter for string and loop operations"));
mRegisterDescriptions.insert("edx",tr("32-bit")+" "+tr("I/O pointer"));
mRegisterDescriptions.insert("esi",tr("32-bit")+" "+tr("Source index for string operations; Pointer to data in the segment pointed to by the DS register"));
mRegisterDescriptions.insert("edi",tr("32-bit")+" "+tr("Destination index for string operations; Pointer to data (or destination) in the segment pointed to by the ES register"));
mRegisterDescriptions.insert("esp",tr("32-bit")+" "+tr("Stack pointer (in the SS segment)"));
mRegisterDescriptions.insert("ebp",tr("32-bit")+" "+tr("Pointer to data on the stack (in the SS segment)"));
mRegisterDescriptions.insert("r8d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r9d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r10d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r11d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r12d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r13d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r14d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("r15d",tr("32-bit")+" "+tr("General purpose"));
mRegisterDescriptions.insert("eip",tr("32-bit")+" "+tr("Instruction pointer"));
mRegisterDescriptions.insert("ax",tr("lower 16 bits of %1").arg("rax/eax"));
mRegisterDescriptions.insert("bx",tr("lower 16 bits of %1").arg("rbx/rbx"));
mRegisterDescriptions.insert("cx",tr("lower 16 bits of %1").arg("rcx/ecx"));
mRegisterDescriptions.insert("dx",tr("lower 16 bits of %1").arg("rdx/edx"));
mRegisterDescriptions.insert("si",tr("lower 16 bits of %1").arg("rsi/esi"));
mRegisterDescriptions.insert("di",tr("lower 16 bits of %1").arg("rdi/edi"));
mRegisterDescriptions.insert("sp",tr("lower 16 bits of %1").arg("rsp/esp"));
mRegisterDescriptions.insert("bp",tr("lower 16 bits of %1").arg("rbp/esp"));
mRegisterDescriptions.insert("r8w",tr("lower 16 bits of %1").arg("r8"));
mRegisterDescriptions.insert("r9w",tr("lower 16 bits of %1").arg("r9"));
mRegisterDescriptions.insert("r10w",tr("lower 16 bits of %1").arg("r10"));
mRegisterDescriptions.insert("r11w",tr("lower 16 bits of %1").arg("r11"));
mRegisterDescriptions.insert("r12w",tr("lower 16 bits of %1").arg("r12"));
mRegisterDescriptions.insert("r13w",tr("lower 16 bits of %1").arg("r13"));
mRegisterDescriptions.insert("r14w",tr("lower 16 bits of %1").arg("r14"));
mRegisterDescriptions.insert("r15w",tr("lower 16 bits of %1").arg("r15"));
mRegisterDescriptions.insert("ip",tr("lower 16 bits of %1").arg("rip/eip"));
mRegisterDescriptions.insert("al",tr("lower 8 bits of %1").arg("rax/eax"));
mRegisterDescriptions.insert("bl",tr("lower 8 bits of %1").arg("rbx/rbx"));
mRegisterDescriptions.insert("cl",tr("lower 8 bits of %1").arg("rcx/ecx"));
mRegisterDescriptions.insert("dl",tr("lower 8 bits of %1").arg("rdx/edx"));
mRegisterDescriptions.insert("sil",tr("lower 8 bits of %1").arg("rsi/esi"));
mRegisterDescriptions.insert("dil",tr("lower 8 bits of %1").arg("rdi/edi"));
mRegisterDescriptions.insert("spl",tr("lower 8 bits of %1").arg("rsp/esp"));
mRegisterDescriptions.insert("bpl",tr("lower 8 bits of %1").arg("rbp/esp"));
mRegisterDescriptions.insert("r8b",tr("lower 8 bits of %1").arg("r8"));
mRegisterDescriptions.insert("r9b",tr("lower 8 bits of %1").arg("r9"));
mRegisterDescriptions.insert("r10b",tr("lower 8 bits of %1").arg("r10"));
mRegisterDescriptions.insert("r11b",tr("lower 8 bits of %1").arg("r11"));
mRegisterDescriptions.insert("r12b",tr("lower 8 bits of %1").arg("r12"));
mRegisterDescriptions.insert("r13b",tr("lower 8 bits of %1").arg("r13"));
mRegisterDescriptions.insert("r14b",tr("lower 8 bits of %1").arg("r14"));
mRegisterDescriptions.insert("r15b",tr("lower 8 bits of %1").arg("r15"));
mRegisterDescriptions.insert("ah",tr("8 high bits of lower 16 bits of %1").arg("rax/eax"));
mRegisterDescriptions.insert("bh",tr("8 high bits of lower 16 bits of %1").arg("rbx/rbx"));
mRegisterDescriptions.insert("ch",tr("8 high bits of lower 16 bits of %1").arg("rcx/ecx"));
mRegisterDescriptions.insert("dh",tr("8 high bits of lower 16 bits of %1").arg("rdx/edx"));
mRegisterDescriptions.insert("cs",tr("16-bit")+" "+tr("Code segment selector"));
mRegisterDescriptions.insert("ds",tr("16-bit")+" "+tr("Data segment selector"));
mRegisterDescriptions.insert("es",tr("16-bit")+" "+tr("Extra data segment selector"));
mRegisterDescriptions.insert("fs",tr("16-bit")+" "+tr("Extra data segment selector"));
mRegisterDescriptions.insert("gs",tr("16-bit")+" "+tr("Extra data segment selector"));
mRegisterDescriptions.insert("ss",tr("16-bit")+" "+tr("Stack segment selector"));
//x87 fpu
mRegisterDescriptions.insert("st0",tr("Floating-point data"));
mRegisterDescriptions.insert("st1",tr("Floating-point data"));
mRegisterDescriptions.insert("st2",tr("Floating-point data"));
mRegisterDescriptions.insert("st3",tr("Floating-point data"));
mRegisterDescriptions.insert("st4",tr("Floating-point data"));
mRegisterDescriptions.insert("st5",tr("Floating-point data"));
mRegisterDescriptions.insert("st6",tr("Floating-point data"));
mRegisterDescriptions.insert("st7",tr("Floating-point data"));
mRegisterDescriptions.insert("fctrl",tr("Floating-point control"));
mRegisterDescriptions.insert("fstat",tr("Floating-point status"));
mRegisterDescriptions.insert("ftag",tr("Floating-point tag word"));
mRegisterDescriptions.insert("fop",tr("Floating-point operation"));
mRegisterDescriptions.insert("fiseg",tr("Floating-point last instruction segment"));
mRegisterDescriptions.insert("fioff",tr("Floating-point last instruction offset"));
mRegisterDescriptions.insert("foseg",tr("Floating-point last operand segment"));
mRegisterDescriptions.insert("fooff",tr("Floating-point last operand offset"));
mRegisterDescriptions.insert("mm0",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm1",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm2",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm3",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm4",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm5",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm6",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("mm7",tr("64-bit")+" "+"MMX");
mRegisterDescriptions.insert("xmm0",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm1",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm2",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm3",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm4",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm5",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm6",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm7",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm8",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm9",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm11",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm12",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm13",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm14",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("xmm15",tr("128-bit")+" "+"XMM");
mRegisterDescriptions.insert("ymm0",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm1",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm2",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm3",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm4",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm5",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm6",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm7",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm8",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm9",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm11",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm12",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm13",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm14",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("ymm15",tr("256-bit")+" "+"YMM");
mRegisterDescriptions.insert("mxscr",tr("SSE status and control"));
#endif
}
int RegisterModel::rowCount(const QModelIndex &) const
{
return mRegisterNames.count();
}
int RegisterModel::columnCount(const QModelIndex &) const
{
return 2;
}
QVariant RegisterModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row() >= static_cast(mRegisterNames.size()))
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return mRegisterNames[index.row()];
case 1:
return mRegisterValues.value(
mRegisterNameIndex.value(index.row(),-1)
,"");
}
break;
case Qt::ToolTipRole:
switch (index.column()) {
case 0:
return mRegisterDescriptions.value(mRegisterNames[index.row()],"");
case 1:
return mRegisterValues.value(
mRegisterNameIndex.value(index.row(),-1)
,"");
}
break;
default:
break;
}
return QVariant();
}
QVariant RegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Register");
case 1:
return tr("Value");
}
}
return QVariant();
}
void RegisterModel::updateNames(const QStringList ®Names)
{
beginResetModel();
mRegisterNameIndex.clear();
mRegisterNames.clear();
for (int i=0;i registerValues)
{
mRegisterValues= registerValues;
emit dataChanged(createIndex(0,1),
createIndex(mRegisterNames.count()-1,1));
}
void RegisterModel::clear()
{
beginResetModel();
mRegisterNames.clear();
mRegisterValues.clear();
endResetModel();
}
DebugTarget::DebugTarget(
const QString &inferior,
const QString &GDBServer,
int port,
const QStringList& arguments,
QObject *parent):
QThread(parent),
mInferior(inferior),
mArguments(arguments),
mGDBServer(GDBServer),
mPort(port),
mStop(false),
mStartSemaphore(0),
mErrorOccured(false)
{
mProcess = nullptr;
}
void DebugTarget::setInputFile(const QString &inputFile)
{
mInputFile = inputFile;
}
void DebugTarget::stopDebug()
{
mStop = true;
}
void DebugTarget::waitStart()
{
mStartSemaphore.acquire(1);
}
const QStringList &DebugTarget::binDirs() const
{
return mBinDirs;
}
void DebugTarget::addBinDirs(const QStringList &binDirs)
{
mBinDirs.append(binDirs);
}
void DebugTarget::addBinDir(const QString &binDir)
{
mBinDirs.append(binDir);
}
void DebugTarget::run()
{
mStop = false;
mErrorOccured = false;
//find first available port
QStringList execArgs;
if (mGDBServer.endsWith(LLDB_SERVER_PROGRAM))
execArgs = QStringList{
mGDBServer,
"gdbserver",
QString("localhost:%1").arg(mPort),
//mInferior,
} + mArguments;
else
execArgs = QStringList{
mGDBServer,
QString("localhost:%1").arg(mPort),
mInferior,
} + mArguments;
QString cmd;
QStringList arguments;
std::unique_ptr fileOwner;
#ifdef Q_OS_WIN
if (pSettings->environment().useCustomTerminal()) {
std::tie(cmd, arguments, fileOwner) = wrapCommandForTerminalEmulator(
pSettings->environment().terminalPath(),
pSettings->environment().terminalArgumentsPattern(),
execArgs
);
} else {
cmd = execArgs[0];
arguments = execArgs.mid(1);
}
#else
std::tie(cmd, arguments, fileOwner) = wrapCommandForTerminalEmulator(
pSettings->environment().terminalPath(),
pSettings->environment().terminalArgumentsPattern(),
execArgs
);
#endif
QString workingDir = QFileInfo(mInferior).path();
mProcess = std::make_shared();
auto action = finally([&]{
mProcess.reset();
});
mProcess->setProgram(cmd);
mProcess->setArguments(arguments);
mProcess->setProcessChannelMode(QProcess::MergedChannels);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString path = env.value("PATH");
QStringList pathAdded = mBinDirs;
if (!path.isEmpty()) {
path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path;
} else {
path = pathAdded.join(PATH_SEPARATOR);
}
QString cmdDir = extractFileDir(cmd);
if (!cmdDir.isEmpty()) {
path = cmdDir + PATH_SEPARATOR + path;
}
env.insert("PATH",path);
mProcess->setProcessEnvironment(env);
mProcess->setWorkingDirectory(workingDir);
#ifdef Q_OS_WIN
mProcess->setCreateProcessArgumentsModifier([this](QProcess::CreateProcessArguments * args){
if (programHasConsole(mInferior)) {
args->flags |= CREATE_NEW_CONSOLE;
args->flags &= ~CREATE_NO_WINDOW;
}
if (mInputFile.isEmpty()) {
args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES;
} else {
args->startupInfo->hStdOutput = NULL;
args->startupInfo->hStdError = NULL;
}
});
#endif
connect(mProcess.get(), &QProcess::errorOccurred,
[&](){
mErrorOccured= true;
});
mProcess->start();
mProcess->waitForStarted(5000);
mStartSemaphore.release(1);
if (mProcess->state()==QProcess::Running && !mInputFile.isEmpty()) {
mProcess->write(readFileToByteArray(mInputFile));
mProcess->waitForFinished(0);
}
bool writeChannelClosed = false;
while (true) {
if (mProcess->bytesToWrite()==0 && !writeChannelClosed) {
writeChannelClosed = true;
mProcess->closeWriteChannel();
}
mProcess->waitForFinished(1);
if (mProcess->state()!=QProcess::Running) {
break;
}
if (mStop) {
mProcess->terminate();
mProcess->kill();
break;
}
if (mErrorOccured)
break;
msleep(1);
}
if (mErrorOccured) {
emit processFailed(mProcess->error());
}
}
MemoryModel::MemoryModel(int dataPerLine, QObject *parent):
QAbstractTableModel(parent),
mDataPerLine(dataPerLine),
mStartAddress(0)
{
}
void MemoryModel::updateMemory(const QStringList &value)
{
int maxDataPerLine=-1;
QRegExp delimiter("(\\s+)");
QList newModel;
for (int i=0;i= QT_VERSION_CHECK(5, 15, 0)
QStringList dataLst = line.split(delimiter,Qt::SkipEmptyParts);
#else
QStringList dataLst = line.split(delimiter,QString::SkipEmptyParts);
#endif
PMemoryLine memoryLine = std::make_shared();
memoryLine->startAddress = -1;
if (dataLst.length()>0) {
bool isOk;
memoryLine->startAddress = stringToHex(dataLst[0],isOk);
if (isOk) {
if (dataLst.length()-1>maxDataPerLine)
maxDataPerLine = dataLst.length()-1;
for (int j=1;jdatas.append((unsigned char)data);
else
memoryLine->datas.append(0);
}
} else {
memoryLine->startAddress=0;
}
}
newModel.append(memoryLine);
}
if (newModel.count()>0 && newModel.count()== mLines.count() &&
newModel[0]->startAddress == mLines[0]->startAddress &&
maxDataPerLine==mDataPerLine) {
for (int i=0;idatas.count();j++) {
if (j>=oldLine->datas.count())
break;
if (newLine->datas[j]!=oldLine->datas[j])
newLine->changedDatas.insert(j);
}
}
mLines = newModel;
emit dataChanged(createIndex(0,0),
createIndex(mLines.count()-1,mDataPerLine-1));
} else {
beginResetModel();
if (maxDataPerLine>0)
mDataPerLine=maxDataPerLine;
mLines = newModel;
endResetModel();
}
if (mLines.count()>0) {
mStartAddress = mLines[0]->startAddress;
} else {
mStartAddress = 0;
}
}
int MemoryModel::rowCount(const QModelIndex &/*parent*/) const
{
return mLines.count();
}
int MemoryModel::columnCount(const QModelIndex &/*parent*/) const
{
return mDataPerLine+1;
}
QVariant MemoryModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row()>=mLines.count())
return QVariant();
PMemoryLine line = mLines[index.row()];
int col = index.column();
if (col<0 || col>line->datas.count())
return QVariant();
if (role == Qt::DisplayRole) {
if (col==line->datas.count()) {
QString s;
foreach (unsigned char ch, line->datas) {
if (ch<' ' || ch>=128)
s+='.';
else
s+=ch;
}
return s;
} else
return QString("%1").arg(line->datas[col],2,16,QChar('0'));
} else if (role == Qt::ToolTipRole) {
if (coldatas.count()) {
QString s =
tr("addr: %1").arg(line->startAddress+col,0,16)
+"
"
+tr("dec: %1").arg(line->datas[col])
+"
"
+tr("oct: %1").arg(line->datas[col],0,8)
+"
"
+tr("bin: %1").arg(line->datas[col],8,2,QChar('0'))
+"
";
QString chVal;
if (line->datas[col]==0) {
chVal="\\0";
} else if (line->datas[col]=='\n') {
chVal="\\n";
} else if (line->datas[col]=='\t') {
chVal="\\t";
} else if (line->datas[col]=='\r') {
chVal="\\r";
} else if (line->datas[col]>=' ' && line->datas[col]<127) {
chVal=QChar(line->datas[col]);
}
if (!chVal.isEmpty()) {
s+=tr("ascii: \'%1\'").arg(chVal)
+"
";
}
return s;
}
}
return QVariant();
}
QVariant MemoryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
if (section<0 || section>=mLines.count())
return QVariant();
PMemoryLine line = mLines[section];
return QString("0x%1").arg(line->startAddress,0,16,QChar('0'));
}
return QVariant();
}
bool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (index.row()<0 || index.row()>=mLines.count())
return false;
PMemoryLine line = mLines[index.row()];
int col = index.column();
if (col<0 || col>=line->datas.count())
return false;
if (role == Qt::EditRole && mStartAddress>0) {
bool ok;
unsigned char val = ("0x"+value.toString()).toUInt(&ok,16);
if (!ok)
return false;
emit setMemoryData(mStartAddress+mDataPerLine*index.row()+col,val);
return true;
}
return false;
}
Qt::ItemFlags MemoryModel::flags(const QModelIndex &/*index*/) const
{
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (mStartAddress!=0)
flags |= Qt::ItemIsEditable;
return flags;
}
qulonglong MemoryModel::startAddress() const
{
return mStartAddress;
}
void MemoryModel::reset()
{
mStartAddress=0;
mLines.clear();
}