1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
3 | * |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation, either version 3 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "debugger.h" |
18 | #include "utils.h" |
19 | #include "mainwindow.h" |
20 | #include "editor.h" |
21 | #include "settings.h" |
22 | #include "widgets/cpudialog.h" |
23 | #include "systemconsts.h" |
24 | |
25 | #include <QFile> |
26 | #include <QFileInfo> |
27 | #include <QMessageBox> |
28 | #include <QPlainTextEdit> |
29 | #include <QDebug> |
30 | #include <QDir> |
31 | #include <QJsonDocument> |
32 | #include <QJsonArray> |
33 | #include <QJsonObject> |
34 | #include "widgets/signalmessagedialog.h" |
35 | |
36 | Debugger::Debugger(QObject *parent) : QObject(parent), |
37 | mForceUTF8(false) |
38 | { |
39 | mBreakpointModel=new BreakpointModel(this); |
40 | mBacktraceModel=new BacktraceModel(this); |
41 | mWatchModel = new WatchModel(this); |
42 | mRegisterModel = new RegisterModel(this); |
43 | mMemoryModel = new MemoryModel(8,this); |
44 | connect(mMemoryModel,&MemoryModel::setMemoryData, |
45 | this, &Debugger::setMemoryData); |
46 | connect(mWatchModel, &WatchModel::setWatchVarValue, |
47 | this, &Debugger::setWatchVarValue); |
48 | mExecuting = false; |
49 | mReader = nullptr; |
50 | mTarget = nullptr; |
51 | mCommandChanged = false; |
52 | mLeftPageIndexBackup = -1; |
53 | |
54 | connect(mWatchModel, &WatchModel::fetchChildren, |
55 | this, &Debugger::fetchVarChildren); |
56 | } |
57 | |
58 | bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStringList& binDirs) |
59 | { |
60 | |
61 | Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(compilerSetIndex); |
62 | if (!compilerSet) { |
63 | compilerSet = pSettings->compilerSets().defaultSet(); |
64 | } |
65 | if (!compilerSet) { |
66 | QMessageBox::critical(pMainWindow, |
67 | tr("No compiler set" ), |
68 | tr("No compiler set is configured." )+tr("Can't start debugging." )); |
69 | return false; |
70 | } |
71 | setForceUTF8(CompilerInfoManager::forceUTF8InDebugger(compilerSet->compilerType())); |
72 | mExecuting = true; |
73 | QString debuggerPath = compilerSet->debugger(); |
74 | //QFile debuggerProgram(debuggerPath); |
75 | // if (!isTextAllAscii(debuggerPath)) { |
76 | // mExecuting = false; |
77 | // QMessageBox::critical(pMainWindow, |
78 | // tr("Debugger path error"), |
79 | // tr("Debugger's path \"%1\" contains non-ascii characters.") |
80 | // .arg(debuggerPath) |
81 | // + "<br />" |
82 | // + tr("This prevents it from executing.")); |
83 | // return false; |
84 | // } |
85 | if (!fileExists(debuggerPath)) { |
86 | mExecuting = false; |
87 | QMessageBox::critical(pMainWindow, |
88 | tr("Debugger not exists" ), |
89 | tr("Can''t find debugger in : \"%1\"" ).arg(debuggerPath)); |
90 | return false; |
91 | } |
92 | if (pSettings->debugger().useGDBServer()) { |
93 | if (!isTextAllAscii(compilerSet->debugServer())) { |
94 | mExecuting = false; |
95 | QMessageBox::critical(pMainWindow, |
96 | tr("GDB Server path error" ), |
97 | tr("GDB Server's path \"%1\" contains non-ascii characters." ) |
98 | .arg(compilerSet->debugServer()) |
99 | + "<br />" |
100 | + tr("This prevents it from executing." )); |
101 | return false; |
102 | } |
103 | if (!fileExists(compilerSet->debugServer())) { |
104 | mExecuting = false; |
105 | QMessageBox::critical(pMainWindow, |
106 | tr("GDB Server not exists" ), |
107 | tr("Can''t find gdb server in : \"%1\"" ).arg(compilerSet->debugServer())); |
108 | return false; |
109 | } |
110 | |
111 | } |
112 | mMemoryModel->reset(); |
113 | mWatchModel->resetAllVarInfos(); |
114 | if (pSettings->debugger().useGDBServer()) { |
115 | mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort()); |
116 | if (pSettings->executor().redirectInput()) |
117 | mTarget->setInputFile(pSettings->executor().inputFilename()); |
118 | connect(mTarget, &QThread::finished,[this](){ |
119 | if (mExecuting) { |
120 | stop(); |
121 | } |
122 | mTarget->deleteLater(); |
123 | mTarget = nullptr; |
124 | }); |
125 | mTarget->addBinDirs(binDirs); |
126 | mTarget->addBinDir(pSettings->dirs().appDir()); |
127 | mTarget->start(); |
128 | mTarget->waitStart(); |
129 | } |
130 | mReader = new DebugReader(this); |
131 | mReader->addBinDirs(binDirs); |
132 | mReader->addBinDir(pSettings->dirs().appDir()); |
133 | mReader->setDebuggerPath(debuggerPath); |
134 | connect(mReader, &QThread::finished,this,&Debugger::cleanUpReader); |
135 | connect(mReader, &QThread::finished,mMemoryModel,&MemoryModel::reset); |
136 | connect(mReader, &DebugReader::parseFinished,this,&Debugger::syncFinishedParsing,Qt::BlockingQueuedConnection); |
137 | connect(mReader, &DebugReader::changeDebugConsoleLastLine,this,&Debugger::onChangeDebugConsoleLastline); |
138 | connect(mReader, &DebugReader::cmdStarted,pMainWindow, &MainWindow::disableDebugActions); |
139 | connect(mReader, &DebugReader::cmdFinished,pMainWindow, &MainWindow::enableDebugActions); |
140 | connect(mReader, &DebugReader::inferiorStopped, pMainWindow, &MainWindow::enableDebugActions); |
141 | |
142 | connect(mReader, &DebugReader::breakpointInfoGetted, mBreakpointModel, |
143 | &BreakpointModel::updateBreakpointNumber); |
144 | connect(mReader, &DebugReader::localsUpdated, pMainWindow, |
145 | &MainWindow::onLocalsReady); |
146 | connect(mReader, &DebugReader::memoryUpdated,this, |
147 | &Debugger::updateMemory); |
148 | connect(mReader, &DebugReader::evalUpdated,this, |
149 | &Debugger::updateEval); |
150 | connect(mReader, &DebugReader::disassemblyUpdate,this, |
151 | &Debugger::updateDisassembly); |
152 | connect(mReader, &DebugReader::registerNamesUpdated, this, |
153 | &Debugger::updateRegisterNames); |
154 | connect(mReader, &DebugReader::registerValuesUpdated, this, |
155 | &Debugger::updateRegisterValues); |
156 | connect(mReader, &DebugReader::varCreated,mWatchModel, |
157 | &WatchModel::updateVarInfo); |
158 | connect(mReader, &DebugReader::prepareVarChildren,mWatchModel, |
159 | &WatchModel::prepareVarChildren); |
160 | connect(mReader, &DebugReader::addVarChild,mWatchModel, |
161 | &WatchModel::addVarChild); |
162 | connect(mReader, &DebugReader::varValueUpdated,mWatchModel, |
163 | &WatchModel::updateVarValue); |
164 | connect(mReader, &DebugReader::varsValueUpdated,mWatchModel, |
165 | &WatchModel::updateAllHasMoreVars); |
166 | connect(mReader, &DebugReader::inferiorContinued,pMainWindow, |
167 | &MainWindow::removeActiveBreakpoints); |
168 | connect(mReader, &DebugReader::inferiorStopped,pMainWindow, |
169 | &MainWindow::setActiveBreakpoint); |
170 | connect(mReader, &DebugReader::inferiorStopped,this, |
171 | &Debugger::refreshAll); |
172 | |
173 | mReader->registerInferiorStoppedCommand("-stack-list-frames" ,"" ); |
174 | mReader->start(); |
175 | mReader->waitStart(); |
176 | |
177 | pMainWindow->updateAppTitle(); |
178 | |
179 | //Application.HintHidePause := 5000; |
180 | return true; |
181 | } |
182 | void Debugger::stop() { |
183 | if (mExecuting) { |
184 | if (mTarget) { |
185 | mTarget->stopDebug(); |
186 | mTarget = nullptr; |
187 | } |
188 | mReader->stopDebug(); |
189 | } |
190 | } |
191 | void Debugger::cleanUpReader() |
192 | { |
193 | if (mExecuting) { |
194 | mExecuting = false; |
195 | |
196 | //stop debugger |
197 | mReader->deleteLater(); |
198 | mReader=nullptr; |
199 | |
200 | if (pMainWindow->cpuDialog()!=nullptr) { |
201 | pMainWindow->cpuDialog()->close(); |
202 | } |
203 | |
204 | // Free resources |
205 | pMainWindow->removeActiveBreakpoints(); |
206 | |
207 | pMainWindow->txtLocals()->clear(); |
208 | |
209 | pMainWindow->updateAppTitle(); |
210 | |
211 | pMainWindow->updateDebugEval("" ); |
212 | |
213 | mBacktraceModel->clear(); |
214 | |
215 | mWatchModel->clearAllVarInfos(); |
216 | |
217 | mBreakpointModel->invalidateAllBreakpointNumbers(); |
218 | |
219 | pMainWindow->updateEditorActions(); |
220 | } |
221 | } |
222 | |
223 | void Debugger::updateRegisterNames(const QStringList ®isterNames) |
224 | { |
225 | mRegisterModel->updateNames(registerNames); |
226 | } |
227 | |
228 | void Debugger::updateRegisterValues(const QHash<int, QString> &values) |
229 | { |
230 | mRegisterModel->updateValues(values); |
231 | } |
232 | |
233 | void Debugger::refreshAll() |
234 | { |
235 | refreshWatchVars(); |
236 | sendCommand("-stack-list-variables" , "--all-values" ); |
237 | if (memoryModel()->startAddress()>0) |
238 | sendCommand("-data-read-memory" ,QString("%1 x 1 %2 %3 " ) |
239 | .arg(memoryModel()->startAddress()) |
240 | .arg(pSettings->debugger().memoryViewRows()) |
241 | .arg(pSettings->debugger().memoryViewColumns()) |
242 | ); |
243 | } |
244 | |
245 | RegisterModel *Debugger::registerModel() const |
246 | { |
247 | return mRegisterModel; |
248 | } |
249 | |
250 | WatchModel *Debugger::watchModel() const |
251 | { |
252 | return mWatchModel; |
253 | } |
254 | |
255 | void Debugger::sendCommand(const QString &command, const QString ¶ms, DebugCommandSource source) |
256 | { |
257 | if (mExecuting && mReader) { |
258 | mReader->postCommand(command,params,source); |
259 | } |
260 | } |
261 | |
262 | bool Debugger::commandRunning() |
263 | { |
264 | if (mExecuting && mReader) { |
265 | return mReader->commandRunning(); |
266 | } |
267 | return false; |
268 | } |
269 | |
270 | bool Debugger::inferiorRunning() |
271 | { |
272 | if (mExecuting && mReader) { |
273 | return mReader->inferiorRunning(); |
274 | } |
275 | return false; |
276 | } |
277 | |
278 | void Debugger::interrupt() |
279 | { |
280 | sendCommand("-exec-interrupt" , "" ); |
281 | } |
282 | |
283 | void Debugger::addBreakpoint(int line, const Editor* editor) |
284 | { |
285 | addBreakpoint(line,editor->filename()); |
286 | } |
287 | |
288 | void Debugger::addBreakpoint(int line, const QString &filename) |
289 | { |
290 | PBreakpoint bp=std::make_shared<Breakpoint>(); |
291 | bp->number = -1; |
292 | bp->line = line; |
293 | bp->filename = filename; |
294 | bp->condition = "" ; |
295 | bp->enabled = true; |
296 | bp->breakpointType = BreakpointType::Breakpoint; |
297 | mBreakpointModel->addBreakpoint(bp); |
298 | if (mExecuting) { |
299 | sendBreakpointCommand(bp); |
300 | } |
301 | } |
302 | |
303 | void Debugger::deleteBreakpoints(const QString &filename) |
304 | { |
305 | for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) { |
306 | PBreakpoint bp = mBreakpointModel->breakpoints()[i]; |
307 | if (bp->filename == filename) { |
308 | mBreakpointModel->removeBreakpoint(i); |
309 | } |
310 | } |
311 | } |
312 | |
313 | void Debugger::deleteBreakpoints(const Editor *editor) |
314 | { |
315 | deleteBreakpoints(editor->filename()); |
316 | } |
317 | |
318 | void Debugger::deleteBreakpoints() |
319 | { |
320 | for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) { |
321 | removeBreakpoint(i); |
322 | } |
323 | } |
324 | |
325 | void Debugger::removeBreakpoint(int line, const Editor *editor) |
326 | { |
327 | removeBreakpoint(line,editor->filename()); |
328 | } |
329 | |
330 | void Debugger::removeBreakpoint(int line, const QString &filename) |
331 | { |
332 | for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) { |
333 | PBreakpoint bp = mBreakpointModel->breakpoints()[i]; |
334 | if (bp->filename == filename && bp->line == line) { |
335 | removeBreakpoint(i); |
336 | } |
337 | } |
338 | } |
339 | |
340 | void Debugger::removeBreakpoint(int index) |
341 | { |
342 | sendClearBreakpointCommand(index); |
343 | mBreakpointModel->removeBreakpoint(index); |
344 | } |
345 | |
346 | PBreakpoint Debugger::breakpointAt(int line, const QString& filename, int &index) |
347 | { |
348 | const QList<PBreakpoint>& breakpoints=mBreakpointModel->breakpoints(); |
349 | for (index=0;index<breakpoints.count();index++){ |
350 | PBreakpoint breakpoint = breakpoints[index]; |
351 | if (breakpoint->line == line |
352 | && breakpoint->filename == filename) |
353 | return breakpoint; |
354 | } |
355 | index=-1; |
356 | return PBreakpoint(); |
357 | } |
358 | |
359 | PBreakpoint Debugger::breakpointAt(int line, const Editor *editor, int &index) |
360 | { |
361 | return breakpointAt(line,editor->filename(),index); |
362 | } |
363 | |
364 | void Debugger::setBreakPointCondition(int index, const QString &condition) |
365 | { |
366 | PBreakpoint breakpoint=mBreakpointModel->setBreakPointCondition(index,condition); |
367 | if (condition.isEmpty()) { |
368 | sendCommand("-break-condition" , |
369 | QString("%1" ).arg(breakpoint->number)); |
370 | } else { |
371 | sendCommand("-break-condition" , |
372 | QString("%1 %2" ).arg(breakpoint->number).arg(condition)); |
373 | } |
374 | } |
375 | |
376 | void Debugger::sendAllBreakpointsToDebugger() |
377 | { |
378 | for (PBreakpoint breakpoint:mBreakpointModel->breakpoints()) { |
379 | sendBreakpointCommand(breakpoint); |
380 | } |
381 | } |
382 | |
383 | void Debugger::addWatchVar(const QString &expression) |
384 | { |
385 | // Don't allow duplicates... |
386 | PWatchVar oldVar = mWatchModel->findWatchVar(expression); |
387 | if (oldVar) |
388 | return; |
389 | |
390 | PWatchVar var = std::make_shared<WatchVar>(); |
391 | var->parent= nullptr; |
392 | var->expression = expression; |
393 | var->value = tr("Execute to evaluate" ); |
394 | var->numChild = 0; |
395 | var->hasMore = false; |
396 | var->parent = nullptr; |
397 | |
398 | mWatchModel->addWatchVar(var); |
399 | sendWatchCommand(var); |
400 | } |
401 | |
402 | void Debugger::modifyWatchVarExpression(const QString &oldExpr, const QString &newExpr) |
403 | { |
404 | // check if name already exists; |
405 | PWatchVar var = mWatchModel->findWatchVar(newExpr); |
406 | if (var) |
407 | return; |
408 | |
409 | var = mWatchModel->findWatchVar(oldExpr); |
410 | if (var) { |
411 | if (mExecuting && !var->expression.isEmpty()) |
412 | sendRemoveWatchCommand(var); |
413 | var->expression = newExpr; |
414 | var->type.clear(); |
415 | var->value.clear(); |
416 | var->hasMore = false; |
417 | var->numChild=0; |
418 | var->name.clear(); |
419 | var->children.clear(); |
420 | |
421 | if (mExecuting) { |
422 | sendWatchCommand(var); |
423 | } |
424 | } |
425 | } |
426 | |
427 | void Debugger::refreshWatchVars() |
428 | { |
429 | if (mExecuting) { |
430 | sendAllWatchVarsToDebugger(); |
431 | sendCommand("-var-update" ," --all-values *" ); |
432 | } |
433 | } |
434 | |
435 | void Debugger::fetchVarChildren(const QString &varName) |
436 | { |
437 | if (mExecuting) { |
438 | sendCommand("-var-list-children" ,varName); |
439 | } |
440 | } |
441 | |
442 | bool Debugger::forceUTF8() const |
443 | { |
444 | return mForceUTF8; |
445 | } |
446 | |
447 | void Debugger::setForceUTF8(bool newForceUTF8) |
448 | { |
449 | mForceUTF8 = newForceUTF8; |
450 | } |
451 | |
452 | MemoryModel *Debugger::memoryModel() const |
453 | { |
454 | return mMemoryModel; |
455 | } |
456 | |
457 | void Debugger::removeWatchVars(bool deleteparent) |
458 | { |
459 | if (deleteparent) { |
460 | mWatchModel->clear(); |
461 | } else { |
462 | for(const PWatchVar& var:mWatchModel->watchVars()) { |
463 | sendRemoveWatchCommand(var); |
464 | } |
465 | mWatchModel->clearAllVarInfos(); |
466 | } |
467 | } |
468 | |
469 | void Debugger::removeWatchVar(const QModelIndex &index) |
470 | { |
471 | PWatchVar var = mWatchModel->findWatchVar(index); |
472 | if (!var) |
473 | return; |
474 | sendRemoveWatchCommand(var); |
475 | mWatchModel->removeWatchVar(index); |
476 | } |
477 | |
478 | void Debugger::sendAllWatchVarsToDebugger() |
479 | { |
480 | for (PWatchVar var:mWatchModel->watchVars()) { |
481 | if (var->name.isEmpty()) |
482 | sendWatchCommand(var); |
483 | } |
484 | } |
485 | |
486 | PWatchVar Debugger::findWatchVar(const QString &expression) |
487 | { |
488 | return mWatchModel->findWatchVar(expression); |
489 | } |
490 | |
491 | PWatchVar Debugger::watchVarAt(const QModelIndex &index) |
492 | { |
493 | return mWatchModel->findWatchVar(index); |
494 | } |
495 | |
496 | //void Debugger::notifyWatchVarUpdated(PWatchVar var) |
497 | //{ |
498 | // mWatchModel->notifyUpdated(var); |
499 | //} |
500 | BacktraceModel* Debugger::backtraceModel() |
501 | { |
502 | return mBacktraceModel; |
503 | } |
504 | |
505 | BreakpointModel *Debugger::breakpointModel() |
506 | { |
507 | return mBreakpointModel; |
508 | } |
509 | |
510 | void Debugger::sendWatchCommand(PWatchVar var) |
511 | { |
512 | sendCommand("-var-create" , var->expression); |
513 | } |
514 | |
515 | void Debugger::sendRemoveWatchCommand(PWatchVar var) |
516 | { |
517 | sendCommand("-var-delete" ,QString("%1" ).arg(var->name)); |
518 | } |
519 | |
520 | void Debugger::sendBreakpointCommand(PBreakpoint breakpoint) |
521 | { |
522 | if (breakpoint && mExecuting) { |
523 | // break "filename":linenum |
524 | QString condition; |
525 | if (!breakpoint->condition.isEmpty()) { |
526 | condition = " -c " + breakpoint->condition; |
527 | } |
528 | QString filename = breakpoint->filename; |
529 | filename.replace('\\','/'); |
530 | sendCommand("-break-insert" , |
531 | QString("%1 --source \"%2\" --line %3" ) |
532 | .arg(condition,filename) |
533 | .arg(breakpoint->line)); |
534 | } |
535 | } |
536 | |
537 | void Debugger::sendClearBreakpointCommand(int index) |
538 | { |
539 | sendClearBreakpointCommand(mBreakpointModel->breakpoints()[index]); |
540 | } |
541 | |
542 | void Debugger::sendClearBreakpointCommand(PBreakpoint breakpoint) |
543 | { |
544 | // Debugger already running? Remove it from GDB |
545 | if (breakpoint && breakpoint->number>=0 && mExecuting) { |
546 | //clear "filename":linenum |
547 | QString filename = breakpoint->filename; |
548 | filename.replace('\\','/'); |
549 | sendCommand("-break-delete" , |
550 | QString("%1" ).arg(breakpoint->number)); |
551 | } |
552 | } |
553 | |
554 | void Debugger::syncFinishedParsing() |
555 | { |
556 | bool spawnedcpuform = false; |
557 | |
558 | // GDB determined that the source code is more recent than the executable. Ask the user if he wants to rebuild. |
559 | if (mReader->receivedSFWarning()) { |
560 | if (QMessageBox::question(pMainWindow, |
561 | tr("Compile" ), |
562 | tr("Source file is more recent than executable." )+"<BR /><BR />" + tr("Recompile?" ), |
563 | QMessageBox::Yes | QMessageBox::No, |
564 | QMessageBox::Yes |
565 | ) == QMessageBox::Yes) { |
566 | stop(); |
567 | pMainWindow->compile(); |
568 | return; |
569 | } |
570 | } |
571 | |
572 | // show command output |
573 | if (pSettings->debugger().enableDebugConsole() ) { |
574 | if (pSettings->debugger().showDetailLog()) { |
575 | for (const QString& line:mReader->fullOutput()) { |
576 | pMainWindow->addDebugOutput(line); |
577 | } |
578 | } else { |
579 | if (mReader->currentCmd() && mReader->currentCmd()->command == "disas" ) { |
580 | |
581 | } else { |
582 | for (const QString& line:mReader->consoleOutput()) { |
583 | pMainWindow->addDebugOutput(line); |
584 | } |
585 | if ( |
586 | (mReader->currentCmd() |
587 | && mReader->currentCmd()->source== DebugCommandSource::Console) |
588 | || !mReader->consoleOutput().isEmpty() ) { |
589 | pMainWindow->addDebugOutput("(gdb)" ); |
590 | } |
591 | } |
592 | } |
593 | } |
594 | |
595 | // The program to debug has stopped. Stop the debugger |
596 | if (mReader->processExited()) { |
597 | stop(); |
598 | return; |
599 | } |
600 | |
601 | if (mReader->signalReceived() |
602 | && mReader->signalName()!="SIGINT" |
603 | && mReader->signalName()!="SIGTRAP" ) { |
604 | SignalMessageDialog dialog(pMainWindow); |
605 | dialog.setOpenCPUInfo(pSettings->debugger().openCPUInfoWhenSignaled()); |
606 | dialog.setMessage( |
607 | tr("Signal \"%1\" Received: " ).arg(mReader->signalName()) |
608 | + "<br />" |
609 | + mReader->signalMeaning()); |
610 | int result = dialog.exec(); |
611 | if (result == QDialog::Accepted && dialog.openCPUInfo()) { |
612 | pMainWindow->showCPUInfoDialog(); |
613 | } |
614 | } |
615 | |
616 | // CPU form updates itself when spawned, don't update twice! |
617 | if ((mReader->updateCPUInfo() && !spawnedcpuform) && (pMainWindow->cpuDialog()!=nullptr)) { |
618 | pMainWindow->cpuDialog()->updateInfo(); |
619 | } |
620 | } |
621 | |
622 | void Debugger::setMemoryData(qulonglong address, unsigned char data) |
623 | { |
624 | sendCommand("-data-write-memory-bytes" , QString("%1 \"%2\"" ).arg(address).arg(data,2,16,QChar('0'))); |
625 | refreshAll(); |
626 | } |
627 | |
628 | void Debugger::setWatchVarValue(const QString &name, const QString &value) |
629 | { |
630 | sendCommand("-var-assign" ,QString("%1 %2" ).arg(name,value)); |
631 | refreshAll(); |
632 | } |
633 | |
634 | void Debugger::updateMemory(const QStringList &value) |
635 | { |
636 | mMemoryModel->updateMemory(value); |
637 | emit memoryExamineReady(value); |
638 | } |
639 | |
640 | void Debugger::updateEval(const QString &value) |
641 | { |
642 | emit evalValueReady(value); |
643 | } |
644 | |
645 | void Debugger::updateDisassembly(const QString& file, const QString& func, const QStringList &value) |
646 | { |
647 | if (pMainWindow->cpuDialog()) { |
648 | pMainWindow->cpuDialog()->setDisassembly(file,func,value); |
649 | } |
650 | } |
651 | |
652 | void Debugger::onChangeDebugConsoleLastline(const QString& text) |
653 | { |
654 | //pMainWindow->changeDebugOutputLastline(text); |
655 | pMainWindow->addDebugOutput(text); |
656 | } |
657 | |
658 | int Debugger::leftPageIndexBackup() const |
659 | { |
660 | return mLeftPageIndexBackup; |
661 | } |
662 | |
663 | void Debugger::setLeftPageIndexBackup(int leftPageIndexBackup) |
664 | { |
665 | mLeftPageIndexBackup = leftPageIndexBackup; |
666 | } |
667 | |
668 | bool Debugger::executing() const |
669 | { |
670 | return mExecuting; |
671 | } |
672 | |
673 | DebugReader::DebugReader(Debugger* debugger, QObject *parent) : QThread(parent), |
674 | mCmdQueueMutex(QMutex::Recursive), |
675 | mStartSemaphore(0) |
676 | { |
677 | mDebugger = debugger; |
678 | mProcess = std::make_shared<QProcess>(); |
679 | mCmdRunning = false; |
680 | mAsyncUpdated = false; |
681 | } |
682 | |
683 | void DebugReader::postCommand(const QString &Command, const QString &Params, |
684 | DebugCommandSource Source) |
685 | { |
686 | QMutexLocker locker(&mCmdQueueMutex); |
687 | PDebugCommand pCmd = std::make_shared<DebugCommand>(); |
688 | pCmd->command = Command; |
689 | pCmd->params = Params; |
690 | pCmd->source = Source; |
691 | mCmdQueue.enqueue(pCmd); |
692 | // if (!mCmdRunning) |
693 | // runNextCmd(); |
694 | } |
695 | |
696 | void DebugReader::registerInferiorStoppedCommand(const QString &Command, const QString &Params) |
697 | { |
698 | QMutexLocker locker(&mCmdQueueMutex); |
699 | PDebugCommand pCmd = std::make_shared<DebugCommand>(); |
700 | pCmd->command = Command; |
701 | pCmd->params = Params; |
702 | pCmd->source = DebugCommandSource::Other; |
703 | mInferiorStoppedHookCommands.append(pCmd); |
704 | } |
705 | |
706 | void DebugReader::clearCmdQueue() |
707 | { |
708 | QMutexLocker locker(&mCmdQueueMutex); |
709 | mCmdQueue.clear(); |
710 | } |
711 | |
712 | void DebugReader::processConsoleOutput(const QByteArray& line) |
713 | { |
714 | if (line.length()>3 && line.startsWith("~\"" ) && line.endsWith("\"" )) { |
715 | QByteArray s=line.mid(2,line.length()-3); |
716 | QByteArray stringValue; |
717 | const char *p=s.data(); |
718 | while (*p!=0) { |
719 | if (*p=='\\' && *(p+1)!=0) { |
720 | p++; |
721 | switch (*p) { |
722 | case '\'': |
723 | stringValue+=0x27; |
724 | p++; |
725 | break; |
726 | case '"': |
727 | stringValue+=0x22; |
728 | p++; |
729 | break; |
730 | case '?': |
731 | stringValue+=0x3f; |
732 | p++; |
733 | break; |
734 | case '\\': |
735 | stringValue+=0x5c; |
736 | p++; |
737 | break; |
738 | case 'a': |
739 | stringValue+=0x07; |
740 | p++; |
741 | break; |
742 | case 'b': |
743 | stringValue+=0x08; |
744 | p++; |
745 | break; |
746 | case 'f': |
747 | stringValue+=0x0c; |
748 | p++; |
749 | break; |
750 | case 'n': |
751 | stringValue+=0x0a; |
752 | p++; |
753 | break; |
754 | case 'r': |
755 | stringValue+=0x0d; |
756 | p++; |
757 | break; |
758 | case 't': |
759 | stringValue+=0x09; |
760 | p++; |
761 | break; |
762 | case 'v': |
763 | stringValue+=0x0b; |
764 | p++; |
765 | break; |
766 | case '0': |
767 | case '1': |
768 | case '2': |
769 | case '3': |
770 | case '4': |
771 | case '5': |
772 | case '6': |
773 | case '7': |
774 | { |
775 | int i=0; |
776 | for (i=0;i<3;i++) { |
777 | if (*(p+i)<'0' || *(p+i)>'7') |
778 | break; |
779 | } |
780 | QByteArray numStr(p,i); |
781 | bool ok; |
782 | unsigned char ch = numStr.toInt(&ok,8); |
783 | stringValue+=ch; |
784 | p+=i; |
785 | break; |
786 | } |
787 | } |
788 | } else { |
789 | stringValue+=*p; |
790 | p++; |
791 | } |
792 | } |
793 | mConsoleOutput.append(QString::fromLocal8Bit(stringValue)); |
794 | } |
795 | } |
796 | |
797 | void DebugReader::processResult(const QByteArray &result) |
798 | { |
799 | GDBMIResultParser parser; |
800 | GDBMIResultType resultType; |
801 | GDBMIResultParser::ParseObject multiValues; |
802 | if (!mCurrentCmd) |
803 | return; |
804 | bool parseOk = parser.parse(result, mCurrentCmd->command, resultType,multiValues); |
805 | if (!parseOk) |
806 | return; |
807 | switch(resultType) { |
808 | case GDBMIResultType::BreakpointTable: |
809 | case GDBMIResultType::Frame: |
810 | case GDBMIResultType::Locals: |
811 | break; |
812 | case GDBMIResultType::Breakpoint: |
813 | handleBreakpoint(multiValues["bkpt" ].object()); |
814 | return; |
815 | case GDBMIResultType::FrameStack: |
816 | handleStack(multiValues["stack" ].array()); |
817 | return; |
818 | case GDBMIResultType::LocalVariables: |
819 | handleLocalVariables(multiValues["variables" ].array()); |
820 | return; |
821 | case GDBMIResultType::Evaluation: |
822 | handleEvaluation(multiValues["value" ].value()); |
823 | return; |
824 | case GDBMIResultType::Memory: |
825 | handleMemory(multiValues["memory" ].array()); |
826 | return; |
827 | case GDBMIResultType::RegisterNames: |
828 | handleRegisterNames(multiValues["register-names" ].array()); |
829 | return; |
830 | case GDBMIResultType::RegisterValues: |
831 | handleRegisterValue(multiValues["register-values" ].array()); |
832 | return; |
833 | case GDBMIResultType::CreateVar: |
834 | handleCreateVar(multiValues); |
835 | return; |
836 | case GDBMIResultType::ListVarChildren: |
837 | handleListVarChildren(multiValues); |
838 | return; |
839 | case GDBMIResultType::UpdateVarValue: |
840 | handleUpdateVarValue(multiValues["changelist" ].array()); |
841 | return; |
842 | default: |
843 | return; |
844 | } |
845 | |
846 | } |
847 | |
848 | void DebugReader::processExecAsyncRecord(const QByteArray &line) |
849 | { |
850 | QByteArray result; |
851 | GDBMIResultParser::ParseObject multiValues; |
852 | GDBMIResultParser parser; |
853 | if (!parser.parseAsyncResult(line,result,multiValues)) |
854 | return; |
855 | if (result == "running" ) { |
856 | mInferiorRunning = true; |
857 | mCurrentAddress=0; |
858 | mCurrentFile.clear(); |
859 | mCurrentLine=-1; |
860 | mCurrentFunc.clear(); |
861 | emit inferiorContinued(); |
862 | return; |
863 | } |
864 | if (result == "stopped" ) { |
865 | mInferiorRunning = false; |
866 | QByteArray reason = multiValues["reason" ].value(); |
867 | if (reason == "exited" ) { |
868 | //inferior exited, gdb should terminate too |
869 | mProcessExited = true; |
870 | return; |
871 | } |
872 | if (reason == "exited-normally" ) { |
873 | //inferior exited, gdb should terminate too |
874 | mProcessExited = true; |
875 | return; |
876 | } |
877 | if (reason == "exited-signalled" ) { |
878 | //inferior exited, gdb should terminate too |
879 | mProcessExited = true; |
880 | mSignalReceived = true; |
881 | return; |
882 | } |
883 | mUpdateCPUInfo = true; |
884 | GDBMIResultParser::ParseValue frame(multiValues["frame" ]); |
885 | if (frame.isValid()) { |
886 | GDBMIResultParser::ParseObject frameObj = frame.object(); |
887 | mCurrentAddress = frameObj["addr" ].hexValue(); |
888 | mCurrentLine = frameObj["line" ].intValue(); |
889 | if (mDebugger->forceUTF8()) |
890 | mCurrentFile = frameObj["fullname" ].utf8PathValue(); |
891 | else |
892 | mCurrentFile = frameObj["fullname" ].pathValue(); |
893 | mCurrentFunc = frameObj["func" ].value(); |
894 | } |
895 | if (reason == "signal-received" ) { |
896 | mSignalReceived = true; |
897 | mSignalName = multiValues["signal-name" ].value(); |
898 | mSignalMeaning = multiValues["signal-meaning" ].value(); |
899 | } |
900 | runInferiorStoppedHook(); |
901 | if (mCurrentCmd && mCurrentCmd->source == DebugCommandSource::Console) |
902 | emit inferiorStopped(mCurrentFile, mCurrentLine,false); |
903 | else |
904 | emit inferiorStopped(mCurrentFile, mCurrentLine,true); |
905 | } |
906 | } |
907 | |
908 | void DebugReader::processError(const QByteArray &errorLine) |
909 | { |
910 | mConsoleOutput.append(QString::fromLocal8Bit(errorLine)); |
911 | } |
912 | |
913 | void DebugReader::processResultRecord(const QByteArray &line) |
914 | { |
915 | if (line.startsWith("^exit" )) { |
916 | mProcessExited = true; |
917 | return; |
918 | } |
919 | if (line.startsWith("^error" )) { |
920 | processError(line); |
921 | return; |
922 | } |
923 | if (line.startsWith("^done" ) |
924 | || line.startsWith("^running" )) { |
925 | int pos = line.indexOf(','); |
926 | if (pos>=0) { |
927 | QByteArray result = line.mid(pos+1); |
928 | processResult(result); |
929 | } else if (mCurrentCmd && !(mCurrentCmd->command.startsWith('-'))) { |
930 | if (mCurrentCmd->command == "disas" ) { |
931 | QStringList disOutput = mConsoleOutput; |
932 | if (disOutput.length()>=3) { |
933 | disOutput.pop_back(); |
934 | disOutput.pop_front(); |
935 | disOutput.pop_front(); |
936 | } |
937 | emit disassemblyUpdate(mCurrentFile,mCurrentFunc, disOutput); |
938 | } |
939 | } |
940 | return ; |
941 | } |
942 | if (line.startsWith("^connected" )) { |
943 | //TODO: connected to remote target |
944 | return; |
945 | } |
946 | } |
947 | |
948 | void DebugReader::processDebugOutput(const QByteArray& debugOutput) |
949 | { |
950 | // Only update once per update at most |
951 | //WatchView.Items.BeginUpdate; |
952 | |
953 | emit parseStarted(); |
954 | |
955 | mConsoleOutput.clear(); |
956 | mFullOutput.clear(); |
957 | |
958 | mSignalReceived = false; |
959 | mUpdateCPUInfo = false; |
960 | mReceivedSFWarning = false; |
961 | QList<QByteArray> lines = splitByteArrayToLines(debugOutput); |
962 | |
963 | for (int i=0;i<lines.count();i++) { |
964 | QByteArray line = lines[i]; |
965 | if (pSettings->debugger().showDetailLog()) |
966 | mFullOutput.append(line); |
967 | line = removeToken(line); |
968 | if (line.isEmpty()) { |
969 | continue; |
970 | } |
971 | switch (line[0]) { |
972 | case '~': // console stream output |
973 | processConsoleOutput(line); |
974 | break; |
975 | case '@': // target stream output |
976 | case '&': // log stream output |
977 | break; |
978 | case '^': // result record |
979 | processResultRecord(line); |
980 | break; |
981 | case '*': // exec async output |
982 | processExecAsyncRecord(line); |
983 | break; |
984 | case '+': // status async output |
985 | case '=': // notify async output |
986 | break; |
987 | } |
988 | } |
989 | emit parseFinished(); |
990 | mConsoleOutput.clear(); |
991 | mFullOutput.clear(); |
992 | } |
993 | |
994 | void DebugReader::runInferiorStoppedHook() |
995 | { |
996 | foreach (const PDebugCommand& cmd, mInferiorStoppedHookCommands) { |
997 | mCmdQueue.push_front(cmd); |
998 | } |
999 | } |
1000 | |
1001 | void DebugReader::runNextCmd() |
1002 | { |
1003 | QMutexLocker locker(&mCmdQueueMutex); |
1004 | |
1005 | if (mCurrentCmd) { |
1006 | DebugCommandSource commandSource = mCurrentCmd->source; |
1007 | mCurrentCmd=nullptr; |
1008 | if (commandSource!=DebugCommandSource::HeartBeat) |
1009 | emit cmdFinished(); |
1010 | } |
1011 | if (mCmdQueue.isEmpty()) { |
1012 | if (pSettings->debugger().useGDBServer() && mInferiorRunning && !mAsyncUpdated) { |
1013 | mAsyncUpdated = true; |
1014 | QTimer::singleShot(50,this,&DebugReader::asyncUpdate); |
1015 | } |
1016 | return; |
1017 | } |
1018 | |
1019 | PDebugCommand pCmd = mCmdQueue.dequeue(); |
1020 | mCmdRunning = true; |
1021 | mCurrentCmd = pCmd; |
1022 | if (pCmd->source!=DebugCommandSource::HeartBeat) |
1023 | emit cmdStarted(); |
1024 | |
1025 | QByteArray s; |
1026 | QByteArray params; |
1027 | s=pCmd->command.toLocal8Bit(); |
1028 | if (!pCmd->params.isEmpty()) { |
1029 | params = pCmd->params.toLocal8Bit(); |
1030 | } |
1031 | |
1032 | //clang compatibility |
1033 | if (pCmd->command == "-break-insert" && mDebugger->forceUTF8()) { |
1034 | params = pCmd->params.toUtf8(); |
1035 | } |
1036 | if (pCmd->command == "-var-create" ) { |
1037 | //hack for variable creation,to easy remember var expression |
1038 | params = " - @ " +params; |
1039 | } else if (pCmd->command == "-var-list-children" ) { |
1040 | //hack for list variable children,to easy remember var expression |
1041 | params = " --all-values \"" + params+'\"'; |
1042 | } |
1043 | s+=" " +params; |
1044 | s+= "\n" ; |
1045 | if (mProcess->write(s)<0) { |
1046 | emit writeToDebugFailed(); |
1047 | } |
1048 | |
1049 | // if devDebugger.ShowCommandLog or pCmd^.ShowInConsole then begin |
1050 | if (pSettings->debugger().enableDebugConsole() ) { |
1051 | //update debug console |
1052 | if (pSettings->debugger().showDetailLog() |
1053 | && pCmd->source != DebugCommandSource::Console) { |
1054 | emit changeDebugConsoleLastLine(pCmd->command + ' ' + params); |
1055 | } |
1056 | } |
1057 | } |
1058 | |
1059 | QStringList DebugReader::tokenize(const QString &s) |
1060 | { |
1061 | QStringList result; |
1062 | int tStart,tEnd; |
1063 | int i=0; |
1064 | while (i<s.length()) { |
1065 | QChar ch = s[i]; |
1066 | if (ch == ' ' || ch == '\t' |
1067 | || ch == '\r' |
1068 | || ch == '\n') { |
1069 | // if (!current.isEmpty()) { |
1070 | // result.append(current); |
1071 | // current = ""; |
1072 | // } |
1073 | i++; |
1074 | continue; |
1075 | } else if (ch == '\'') { |
1076 | tStart = i; |
1077 | i++; //skip \' |
1078 | while (i<s.length()) { |
1079 | if (s[i]=='\'') { |
1080 | i++; |
1081 | break; |
1082 | } else if (s[i] == '\\') { |
1083 | i+=2; |
1084 | continue; |
1085 | } |
1086 | i++; |
1087 | } |
1088 | tEnd = std::min(i,s.length()); |
1089 | result.append(s.mid(tStart,tEnd-tStart)); |
1090 | } else if (ch == '\"') { |
1091 | tStart = i; |
1092 | i++; //skip \' |
1093 | while (i<s.length()) { |
1094 | if (s[i]=='\"') { |
1095 | i++; |
1096 | break; |
1097 | } else if (s[i] == '\\') { |
1098 | i+=2; |
1099 | continue; |
1100 | } |
1101 | i++; |
1102 | } |
1103 | tEnd = std::min(i,s.length()); |
1104 | result.append(s.mid(tStart,tEnd-tStart)); |
1105 | } else if (ch == '<') { |
1106 | tStart = i; |
1107 | i++; |
1108 | while (i<s.length()) { |
1109 | if (s[i]=='>') { |
1110 | i++; |
1111 | break; |
1112 | } |
1113 | i++; |
1114 | } |
1115 | tEnd = std::min(i,s.length()); |
1116 | result.append(s.mid(tStart,tEnd-tStart)); |
1117 | } else if (ch == '(') { |
1118 | tStart = i; |
1119 | i++; |
1120 | while (i<s.length()) { |
1121 | if (s[i]==')') { |
1122 | i++; |
1123 | break; |
1124 | } |
1125 | i++; |
1126 | } |
1127 | tEnd = std::min(i,s.length()); |
1128 | result.append(s.mid(tStart,tEnd-tStart)); |
1129 | } else if (ch == '_' || |
1130 | ch == '.' || |
1131 | ch == '+' || |
1132 | ch == '-' || |
1133 | ch.isLetterOrNumber() ) { |
1134 | tStart = i; |
1135 | while (i<s.length()) { |
1136 | ch = s[i]; |
1137 | if (!(ch == '_' || |
1138 | ch == '.' || |
1139 | ch == '+' || |
1140 | ch == '-' || |
1141 | ch.isLetterOrNumber() )) |
1142 | break; |
1143 | i++; |
1144 | } |
1145 | tEnd = std::min(i,s.length()); |
1146 | result.append(s.mid(tStart,tEnd-tStart)); |
1147 | } else { |
1148 | result.append(s[i]); |
1149 | i++; |
1150 | } |
1151 | } |
1152 | return result; |
1153 | } |
1154 | |
1155 | bool DebugReader::outputTerminated(const QByteArray &text) |
1156 | { |
1157 | QStringList lines = textToLines(QString::fromUtf8(text)); |
1158 | foreach (const QString& line,lines) { |
1159 | if (line.trimmed() == "(gdb)" ) |
1160 | return true; |
1161 | } |
1162 | return false; |
1163 | } |
1164 | |
1165 | void DebugReader::handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint) |
1166 | { |
1167 | QString filename; |
1168 | // gdb use system encoding for file path |
1169 | if (mDebugger->forceUTF8()) |
1170 | filename = breakpoint["fullname" ].utf8PathValue(); |
1171 | else |
1172 | filename = breakpoint["fullname" ].pathValue(); |
1173 | int line = breakpoint["line" ].intValue(); |
1174 | int number = breakpoint["number" ].intValue(); |
1175 | emit breakpointInfoGetted(filename, line , number); |
1176 | } |
1177 | |
1178 | void DebugReader::handleStack(const QList<GDBMIResultParser::ParseValue> & stack) |
1179 | { |
1180 | mDebugger->backtraceModel()->clear(); |
1181 | foreach (const GDBMIResultParser::ParseValue& frameValue, stack) { |
1182 | GDBMIResultParser::ParseObject frameObject = frameValue.object(); |
1183 | PTrace trace = std::make_shared<Trace>(); |
1184 | trace->funcname = frameObject["func" ].value(); |
1185 | if (mDebugger->forceUTF8()) |
1186 | trace->filename = frameObject["fullname" ].utf8PathValue(); |
1187 | else |
1188 | trace->filename = frameObject["fullname" ].pathValue(); |
1189 | trace->line = frameObject["line" ].intValue(); |
1190 | trace->level = frameObject["level" ].intValue(0); |
1191 | trace->address = frameObject["addr" ].value(); |
1192 | mDebugger->backtraceModel()->addTrace(trace); |
1193 | } |
1194 | } |
1195 | |
1196 | void DebugReader::handleLocalVariables(const QList<GDBMIResultParser::ParseValue> &variables) |
1197 | { |
1198 | QStringList locals; |
1199 | foreach (const GDBMIResultParser::ParseValue& varValue, variables) { |
1200 | GDBMIResultParser::ParseObject varObject = varValue.object(); |
1201 | locals.append( |
1202 | QString("%1 = %2" ) |
1203 | .arg( |
1204 | QString(varObject["name" ].value()), |
1205 | QString(varObject["value" ].value()) |
1206 | )); |
1207 | } |
1208 | emit localsUpdated(locals); |
1209 | } |
1210 | |
1211 | void DebugReader::handleEvaluation(const QString &value) |
1212 | { |
1213 | emit evalUpdated(value); |
1214 | } |
1215 | |
1216 | void DebugReader::handleMemory(const QList<GDBMIResultParser::ParseValue> &rows) |
1217 | { |
1218 | QStringList memory; |
1219 | foreach (const GDBMIResultParser::ParseValue& row, rows) { |
1220 | GDBMIResultParser::ParseObject rowObject = row.object(); |
1221 | QList<GDBMIResultParser::ParseValue> data = rowObject["data" ].array(); |
1222 | QStringList values; |
1223 | foreach (const GDBMIResultParser::ParseValue& val, data) { |
1224 | values.append(val.value()); |
1225 | } |
1226 | memory.append(QString("%1 %2" ) |
1227 | .arg(rowObject["addr" ].value(),values.join(" " ))); |
1228 | } |
1229 | emit memoryUpdated(memory); |
1230 | } |
1231 | |
1232 | void DebugReader::handleRegisterNames(const QList<GDBMIResultParser::ParseValue> &names) |
1233 | { |
1234 | QStringList nameList; |
1235 | foreach (const GDBMIResultParser::ParseValue& nameValue, names) { |
1236 | nameList.append(nameValue.value()); |
1237 | } |
1238 | emit registerNamesUpdated(nameList); |
1239 | } |
1240 | |
1241 | void DebugReader::handleRegisterValue(const QList<GDBMIResultParser::ParseValue> &values) |
1242 | { |
1243 | QHash<int,QString> result; |
1244 | foreach (const GDBMIResultParser::ParseValue& val, values) { |
1245 | GDBMIResultParser::ParseObject obj = val.object(); |
1246 | int number = obj["number" ].intValue(); |
1247 | QString value = obj["value" ].value(); |
1248 | bool ok; |
1249 | long long intVal; |
1250 | intVal = value.toLongLong(&ok,10); |
1251 | if (ok) { |
1252 | value = QString("0x%1" ).arg(intVal,0,16); |
1253 | } |
1254 | result.insert(number,value); |
1255 | } |
1256 | emit registerValuesUpdated(result); |
1257 | } |
1258 | |
1259 | void DebugReader::handleCreateVar(const GDBMIResultParser::ParseObject &multiVars) |
1260 | { |
1261 | if (!mCurrentCmd) |
1262 | return; |
1263 | QString expression = mCurrentCmd->params; |
1264 | QString name = multiVars["name" ].value(); |
1265 | int numChild = multiVars["numchild" ].intValue(0); |
1266 | QString value = multiVars["value" ].value(); |
1267 | QString type = multiVars["type" ].value(); |
1268 | bool hasMore = multiVars["has_more" ].value() != "0" ; |
1269 | emit varCreated(expression,name,numChild,value,type,hasMore); |
1270 | } |
1271 | |
1272 | void DebugReader::handleListVarChildren(const GDBMIResultParser::ParseObject &multiVars) |
1273 | { |
1274 | if (!mCurrentCmd) |
1275 | return; |
1276 | QString parentName = mCurrentCmd->params; |
1277 | int parentNumChild = multiVars["numchild" ].intValue(0); |
1278 | QList<GDBMIResultParser::ParseValue> children = multiVars["children" ].array(); |
1279 | bool hasMore = multiVars["has_more" ].value()!="0" ; |
1280 | emit prepareVarChildren(parentName,parentNumChild,hasMore); |
1281 | foreach(const GDBMIResultParser::ParseValue& child, children) { |
1282 | GDBMIResultParser::ParseObject childObj = child.object(); |
1283 | QString name = childObj["name" ].value(); |
1284 | QString exp = childObj["exp" ].value(); |
1285 | int numChild = childObj["numchild" ].intValue(0); |
1286 | QString value = childObj["value" ].value(); |
1287 | QString type = childObj["type" ].value(); |
1288 | bool hasMore = childObj["has_more" ].value() != "0" ; |
1289 | emit addVarChild(parentName, |
1290 | name, |
1291 | exp, |
1292 | numChild, |
1293 | value, |
1294 | type, |
1295 | hasMore); |
1296 | } |
1297 | } |
1298 | |
1299 | void DebugReader::handleUpdateVarValue(const QList<GDBMIResultParser::ParseValue> &changes) |
1300 | { |
1301 | foreach (const GDBMIResultParser::ParseValue& value, changes) { |
1302 | GDBMIResultParser::ParseObject obj = value.object(); |
1303 | QString name = obj["name" ].value(); |
1304 | QString val = obj["value" ].value(); |
1305 | QString inScope = obj["in_scope" ].value(); |
1306 | bool typeChanged = (obj["type_changed" ].value()=="true" ); |
1307 | QString newType = obj["new_type" ].value(); |
1308 | int newNumChildren = obj["new_num_children" ].intValue(-1); |
1309 | bool hasMore = (obj["has_more" ].value() == "1" ); |
1310 | emit varValueUpdated(name,val,inScope,typeChanged,newType,newNumChildren, |
1311 | hasMore); |
1312 | } |
1313 | //todo: -var-list-children will freeze if the var is not correctly initialized |
1314 | //emit varsValueUpdated(); |
1315 | } |
1316 | |
1317 | QByteArray DebugReader::removeToken(const QByteArray &line) |
1318 | { |
1319 | int p=0; |
1320 | while (p<line.length()) { |
1321 | QChar ch=line[p]; |
1322 | if (ch<'0' || ch>'9') { |
1323 | break; |
1324 | } |
1325 | p++; |
1326 | } |
1327 | if (p<line.length()) |
1328 | return line.mid(p); |
1329 | return line; |
1330 | } |
1331 | |
1332 | void DebugReader::asyncUpdate() |
1333 | { |
1334 | QMutexLocker locker(&mCmdQueueMutex); |
1335 | if (mCmdQueue.isEmpty()) |
1336 | postCommand("-var-update" ," --all-values *" ,DebugCommandSource::HeartBeat); |
1337 | mAsyncUpdated = false; |
1338 | } |
1339 | |
1340 | const QStringList &DebugReader::binDirs() const |
1341 | { |
1342 | return mBinDirs; |
1343 | } |
1344 | |
1345 | void DebugReader::addBinDirs(const QStringList &binDirs) |
1346 | { |
1347 | mBinDirs.append(binDirs); |
1348 | } |
1349 | |
1350 | void DebugReader::addBinDir(const QString &binDir) |
1351 | { |
1352 | mBinDirs.append(binDir); |
1353 | } |
1354 | |
1355 | const QString &DebugReader::signalMeaning() const |
1356 | { |
1357 | return mSignalMeaning; |
1358 | } |
1359 | |
1360 | const QString &DebugReader::signalName() const |
1361 | { |
1362 | return mSignalName; |
1363 | } |
1364 | |
1365 | bool DebugReader::inferiorRunning() const |
1366 | { |
1367 | return mInferiorRunning; |
1368 | } |
1369 | |
1370 | const QStringList &DebugReader::fullOutput() const |
1371 | { |
1372 | return mFullOutput; |
1373 | } |
1374 | |
1375 | bool DebugReader::receivedSFWarning() const |
1376 | { |
1377 | return mReceivedSFWarning; |
1378 | } |
1379 | |
1380 | bool DebugReader::updateCPUInfo() const |
1381 | { |
1382 | return mUpdateCPUInfo; |
1383 | } |
1384 | |
1385 | const PDebugCommand &DebugReader::currentCmd() const |
1386 | { |
1387 | return mCurrentCmd; |
1388 | } |
1389 | |
1390 | const QStringList &DebugReader::consoleOutput() const |
1391 | { |
1392 | return mConsoleOutput; |
1393 | } |
1394 | |
1395 | bool DebugReader::signalReceived() const |
1396 | { |
1397 | return mSignalReceived; |
1398 | } |
1399 | |
1400 | bool DebugReader::processExited() const |
1401 | { |
1402 | return mProcessExited; |
1403 | } |
1404 | |
1405 | QString DebugReader::debuggerPath() const |
1406 | { |
1407 | return mDebuggerPath; |
1408 | } |
1409 | |
1410 | void DebugReader::setDebuggerPath(const QString &debuggerPath) |
1411 | { |
1412 | mDebuggerPath = debuggerPath; |
1413 | } |
1414 | |
1415 | void DebugReader::stopDebug() |
1416 | { |
1417 | mStop = true; |
1418 | } |
1419 | |
1420 | bool DebugReader::commandRunning() |
1421 | { |
1422 | return !mCmdQueue.isEmpty(); |
1423 | } |
1424 | |
1425 | void DebugReader::waitStart() |
1426 | { |
1427 | mStartSemaphore.acquire(1); |
1428 | } |
1429 | |
1430 | void DebugReader::run() |
1431 | { |
1432 | mStop = false; |
1433 | mInferiorRunning = false; |
1434 | mProcessExited = false; |
1435 | mErrorOccured = false; |
1436 | QString cmd = mDebuggerPath; |
1437 | // QString arguments = "--annotate=2"; |
1438 | QString arguments = "--interpret=mi --silent" ; |
1439 | QString workingDir = QFileInfo(mDebuggerPath).path(); |
1440 | |
1441 | mProcess = std::make_shared<QProcess>(); |
1442 | auto action = finally([&]{ |
1443 | mProcess.reset(); |
1444 | }); |
1445 | mProcess->setProgram(cmd); |
1446 | mProcess->setArguments(splitProcessCommand(arguments)); |
1447 | mProcess->setProcessChannelMode(QProcess::MergedChannels); |
1448 | |
1449 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); |
1450 | QString path = env.value("PATH" ); |
1451 | QStringList pathAdded = mBinDirs; |
1452 | if (!path.isEmpty()) { |
1453 | path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path; |
1454 | } else { |
1455 | path = pathAdded.join(PATH_SEPARATOR); |
1456 | } |
1457 | QString cmdDir = extractFileDir(cmd); |
1458 | if (!cmdDir.isEmpty()) { |
1459 | path = cmdDir + PATH_SEPARATOR + path; |
1460 | } |
1461 | env.insert("PATH" ,path); |
1462 | mProcess->setProcessEnvironment(env); |
1463 | |
1464 | mProcess->setWorkingDirectory(workingDir); |
1465 | |
1466 | connect(mProcess.get(), &QProcess::errorOccurred, |
1467 | [&](){ |
1468 | mErrorOccured= true; |
1469 | }); |
1470 | QByteArray buffer; |
1471 | QByteArray readed; |
1472 | |
1473 | mProcess->start(); |
1474 | mProcess->waitForStarted(5000); |
1475 | mStartSemaphore.release(1); |
1476 | while (true) { |
1477 | mProcess->waitForFinished(1); |
1478 | if (mProcess->state()!=QProcess::Running) { |
1479 | break; |
1480 | } |
1481 | if (mStop) { |
1482 | mProcess->terminate(); |
1483 | mProcess->kill(); |
1484 | break; |
1485 | } |
1486 | if (mErrorOccured) |
1487 | break; |
1488 | readed = mProcess->readAll(); |
1489 | buffer += readed; |
1490 | |
1491 | if (readed.endsWith("\n" )&& outputTerminated(buffer)) { |
1492 | processDebugOutput(buffer); |
1493 | buffer.clear(); |
1494 | mCmdRunning = false; |
1495 | runNextCmd(); |
1496 | } else if (!mCmdRunning && readed.isEmpty()){ |
1497 | runNextCmd(); |
1498 | } else if (readed.isEmpty()){ |
1499 | msleep(1); |
1500 | } |
1501 | } |
1502 | if (mErrorOccured) { |
1503 | emit processError(mProcess->error()); |
1504 | } |
1505 | } |
1506 | |
1507 | BreakpointModel::BreakpointModel(QObject *parent):QAbstractTableModel(parent) |
1508 | { |
1509 | |
1510 | } |
1511 | |
1512 | int BreakpointModel::rowCount(const QModelIndex &) const |
1513 | { |
1514 | return mList.size(); |
1515 | } |
1516 | |
1517 | int BreakpointModel::columnCount(const QModelIndex &) const |
1518 | { |
1519 | return 3; |
1520 | } |
1521 | |
1522 | QVariant BreakpointModel::data(const QModelIndex &index, int role) const |
1523 | { |
1524 | if (!index.isValid()) |
1525 | return QVariant(); |
1526 | if (index.row()<0 || index.row() >= static_cast<int>(mList.size())) |
1527 | return QVariant(); |
1528 | PBreakpoint breakpoint = mList[index.row()]; |
1529 | if (!breakpoint) |
1530 | return QVariant(); |
1531 | switch (role) { |
1532 | case Qt::DisplayRole: |
1533 | switch (index.column()) { |
1534 | case 0: { |
1535 | return extractFileName(breakpoint->filename); |
1536 | } |
1537 | case 1: |
1538 | if (breakpoint->line>0) |
1539 | return breakpoint->line; |
1540 | else |
1541 | return "" ; |
1542 | case 2: |
1543 | return breakpoint->condition; |
1544 | default: |
1545 | return QVariant(); |
1546 | } |
1547 | case Qt::ToolTipRole: |
1548 | switch (index.column()) { |
1549 | case 0: |
1550 | return breakpoint->filename; |
1551 | case 1: |
1552 | if (breakpoint->line>0) |
1553 | return breakpoint->line; |
1554 | else |
1555 | return "" ; |
1556 | case 2: |
1557 | return breakpoint->condition; |
1558 | default: |
1559 | return QVariant(); |
1560 | } |
1561 | default: |
1562 | return QVariant(); |
1563 | } |
1564 | } |
1565 | |
1566 | QVariant BreakpointModel::(int section, Qt::Orientation orientation, int role) const |
1567 | { |
1568 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { |
1569 | switch(section) { |
1570 | case 0: |
1571 | return tr("Filename" ); |
1572 | case 1: |
1573 | return tr("Line" ); |
1574 | case 2: |
1575 | return tr("Condition" ); |
1576 | } |
1577 | } |
1578 | return QVariant(); |
1579 | } |
1580 | |
1581 | void BreakpointModel::addBreakpoint(PBreakpoint p) |
1582 | { |
1583 | beginInsertRows(QModelIndex(),mList.size(),mList.size()); |
1584 | mList.push_back(p); |
1585 | endInsertRows(); |
1586 | } |
1587 | |
1588 | void BreakpointModel::clear() |
1589 | { |
1590 | beginResetModel(); |
1591 | mList.clear(); |
1592 | endResetModel(); |
1593 | } |
1594 | |
1595 | void BreakpointModel::removeBreakpoint(int row) |
1596 | { |
1597 | beginRemoveRows(QModelIndex(),row,row); |
1598 | mList.removeAt(row); |
1599 | endRemoveRows(); |
1600 | } |
1601 | |
1602 | void BreakpointModel::invalidateAllBreakpointNumbers() |
1603 | { |
1604 | foreach (PBreakpoint bp,mList) { |
1605 | bp->number = -1; |
1606 | } |
1607 | //emit dateChanged(createIndex(0,0),) |
1608 | } |
1609 | |
1610 | PBreakpoint BreakpointModel::setBreakPointCondition(int index, const QString &condition) |
1611 | { |
1612 | PBreakpoint breakpoint = mList[index]; |
1613 | breakpoint->condition = condition; |
1614 | emit dataChanged(createIndex(index,0),createIndex(index,2)); |
1615 | return breakpoint; |
1616 | } |
1617 | |
1618 | const QList<PBreakpoint> &BreakpointModel::breakpoints() const |
1619 | { |
1620 | return mList; |
1621 | } |
1622 | |
1623 | PBreakpoint BreakpointModel::breakpoint(int index) const |
1624 | { |
1625 | if (index<0 && index>=mList.count()) |
1626 | return PBreakpoint(); |
1627 | return mList[index]; |
1628 | } |
1629 | |
1630 | void BreakpointModel::save(const QString &filename) |
1631 | { |
1632 | QFile file(filename); |
1633 | if (file.open(QFile::WriteOnly | QFile::Truncate)) { |
1634 | QJsonArray array; |
1635 | foreach (const PBreakpoint& breakpoint, mList) { |
1636 | QJsonObject obj; |
1637 | obj["filename" ]=breakpoint->filename; |
1638 | obj["line" ]=breakpoint->line; |
1639 | obj["condition" ]=breakpoint->condition; |
1640 | obj["enabled" ]=breakpoint->enabled; |
1641 | obj["breakpoint_type" ] = static_cast<int>(breakpoint->breakpointType); |
1642 | array.append(obj); |
1643 | } |
1644 | QJsonDocument doc; |
1645 | doc.setArray(array); |
1646 | if (file.write(doc.toJson())<0) { |
1647 | throw FileError(tr("Save file '%1' failed." ) |
1648 | .arg(filename)); |
1649 | } |
1650 | } else { |
1651 | throw FileError(tr("Can't open file '%1' for write." ) |
1652 | .arg(filename)); |
1653 | } |
1654 | } |
1655 | |
1656 | void BreakpointModel::load(const QString &filename) |
1657 | { |
1658 | clear(); |
1659 | QFile file(filename); |
1660 | if (!file.exists()) |
1661 | return; |
1662 | if (file.open(QFile::ReadOnly)) { |
1663 | QByteArray content = file.readAll(); |
1664 | QJsonParseError error; |
1665 | QJsonDocument doc(QJsonDocument::fromJson(content,&error)); |
1666 | if (error.error != QJsonParseError::NoError) { |
1667 | throw FileError(tr("Error in json file '%1':%2 : %3" ) |
1668 | .arg(filename) |
1669 | .arg(error.offset) |
1670 | .arg(error.errorString())); |
1671 | } |
1672 | QJsonArray array = doc.array(); |
1673 | for (int i=0;i<array.count();i++) { |
1674 | QJsonValue value = array[i]; |
1675 | QJsonObject obj=value.toObject(); |
1676 | PBreakpoint breakpoint = std::make_shared<Breakpoint>(); |
1677 | breakpoint->filename = QFileInfo(obj["filename" ].toString()).absoluteFilePath(); |
1678 | breakpoint->line = obj["line" ].toInt(); |
1679 | breakpoint->condition = obj["condition" ].toString(); |
1680 | breakpoint->enabled = obj["enabled" ].toBool(); |
1681 | breakpoint->breakpointType = static_cast<BreakpointType>(obj["breakpoint_type" ].toInt()); |
1682 | |
1683 | addBreakpoint(breakpoint); |
1684 | } |
1685 | } else { |
1686 | throw FileError(tr("Can't open file '%1' for read." ) |
1687 | .arg(filename)); |
1688 | } |
1689 | } |
1690 | |
1691 | void BreakpointModel::updateBreakpointNumber(const QString& filename, int line, int number) |
1692 | { |
1693 | foreach (PBreakpoint bp, mList) { |
1694 | if (bp->filename == filename && bp->line == line) { |
1695 | bp->number = number; |
1696 | return; |
1697 | } |
1698 | } |
1699 | } |
1700 | |
1701 | void BreakpointModel::onFileDeleteLines(const QString& filename, int startLine, int count) |
1702 | { |
1703 | for (int i = mList.count()-1;i>=0;i--){ |
1704 | PBreakpoint breakpoint = mList[i]; |
1705 | if (breakpoint->filename == filename |
1706 | && breakpoint->line>=startLine) { |
1707 | if (breakpoint->line >= startLine+count) { |
1708 | breakpoint->line -= count; |
1709 | emit dataChanged(createIndex(i,0),createIndex(i,2)); |
1710 | } else { |
1711 | removeBreakpoint(i); |
1712 | } |
1713 | } |
1714 | } |
1715 | } |
1716 | |
1717 | void BreakpointModel::onFileInsertLines(const QString& filename, int startLine, int count) |
1718 | { |
1719 | for (int i = mList.count()-1;i>=0;i--){ |
1720 | PBreakpoint breakpoint = mList[i]; |
1721 | if (breakpoint->filename == filename |
1722 | && breakpoint->line>=startLine) { |
1723 | breakpoint->line+=count; |
1724 | emit dataChanged(createIndex(i,0),createIndex(i,2)); |
1725 | } |
1726 | } |
1727 | } |
1728 | |
1729 | |
1730 | BacktraceModel::BacktraceModel(QObject *parent):QAbstractTableModel(parent) |
1731 | { |
1732 | |
1733 | } |
1734 | |
1735 | int BacktraceModel::rowCount(const QModelIndex &) const |
1736 | { |
1737 | return mList.size(); |
1738 | } |
1739 | |
1740 | int BacktraceModel::columnCount(const QModelIndex &) const |
1741 | { |
1742 | return 3; |
1743 | } |
1744 | |
1745 | QVariant BacktraceModel::data(const QModelIndex &index, int role) const |
1746 | { |
1747 | if (!index.isValid()) |
1748 | return QVariant(); |
1749 | if (index.row()<0 || index.row() >= static_cast<int>(mList.size())) |
1750 | return QVariant(); |
1751 | PTrace trace = mList[index.row()]; |
1752 | if (!trace) |
1753 | return QVariant(); |
1754 | switch (role) { |
1755 | case Qt::DisplayRole: |
1756 | switch (index.column()) { |
1757 | case 0: |
1758 | return trace->funcname; |
1759 | case 1: |
1760 | return trace->filename; |
1761 | case 2: |
1762 | if (trace->line>0) |
1763 | return trace->line; |
1764 | else |
1765 | return "" ; |
1766 | default: |
1767 | return QVariant(); |
1768 | } |
1769 | default: |
1770 | return QVariant(); |
1771 | } |
1772 | } |
1773 | |
1774 | QVariant BacktraceModel::(int section, Qt::Orientation orientation, int role) const |
1775 | { |
1776 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { |
1777 | switch(section) { |
1778 | case 0: |
1779 | return tr("Function" ); |
1780 | case 1: |
1781 | return tr("Filename" ); |
1782 | case 2: |
1783 | return tr("Line" ); |
1784 | } |
1785 | } |
1786 | return QVariant(); |
1787 | } |
1788 | |
1789 | void BacktraceModel::addTrace(PTrace p) |
1790 | { |
1791 | beginInsertRows(QModelIndex(),mList.size(),mList.size()); |
1792 | mList.push_back(p); |
1793 | endInsertRows(); |
1794 | } |
1795 | |
1796 | void BacktraceModel::clear() |
1797 | { |
1798 | beginResetModel(); |
1799 | mList.clear(); |
1800 | endResetModel(); |
1801 | } |
1802 | |
1803 | void BacktraceModel::removeTrace(int row) |
1804 | { |
1805 | beginRemoveRows(QModelIndex(),row,row); |
1806 | mList.removeAt(row); |
1807 | endRemoveRows(); |
1808 | } |
1809 | |
1810 | const QList<PTrace> &BacktraceModel::backtraces() const |
1811 | { |
1812 | return mList; |
1813 | } |
1814 | |
1815 | PTrace BacktraceModel::backtrace(int index) const |
1816 | { |
1817 | if (index>=0 && index < mList.count()){ |
1818 | return mList[index]; |
1819 | } |
1820 | return PTrace(); |
1821 | } |
1822 | |
1823 | WatchModel::WatchModel(QObject *parent):QAbstractItemModel(parent) |
1824 | { |
1825 | mUpdateCount = 0; |
1826 | } |
1827 | |
1828 | QVariant WatchModel::data(const QModelIndex &index, int role) const |
1829 | { |
1830 | if (!index.isValid()) { |
1831 | return QVariant(); |
1832 | } |
1833 | WatchVar* item = static_cast<WatchVar*>(index.internalPointer()); |
1834 | switch (role) { |
1835 | case Qt::DisplayRole: |
1836 | switch(index.column()) { |
1837 | case 0: |
1838 | return item->expression; |
1839 | case 1: |
1840 | return item->type; |
1841 | case 2: |
1842 | return item->value; |
1843 | } |
1844 | } |
1845 | return QVariant(); |
1846 | } |
1847 | |
1848 | QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const |
1849 | { |
1850 | if (!hasIndex(row,column,parent)) |
1851 | return QModelIndex(); |
1852 | WatchVar* parentItem; |
1853 | PWatchVar pChild; |
1854 | if (!parent.isValid()) { |
1855 | parentItem = nullptr; |
1856 | pChild = mWatchVars[row]; |
1857 | } else { |
1858 | parentItem = static_cast<WatchVar*>(parent.internalPointer()); |
1859 | pChild = parentItem->children[row]; |
1860 | } |
1861 | if (pChild) { |
1862 | return createIndex(row,column,pChild.get()); |
1863 | } |
1864 | return QModelIndex(); |
1865 | } |
1866 | |
1867 | static int getWatchIndex(WatchVar* var, const QList<PWatchVar> list) { |
1868 | for (int i=0;i<list.size();i++) { |
1869 | PWatchVar v = list[i]; |
1870 | if (v.get() == var) { |
1871 | return i; |
1872 | } |
1873 | } |
1874 | return -1; |
1875 | } |
1876 | |
1877 | QModelIndex WatchModel::parent(const QModelIndex &index) const |
1878 | { |
1879 | if (!index.isValid()) { |
1880 | return QModelIndex(); |
1881 | } |
1882 | WatchVar* childItem = static_cast<WatchVar*>(index.internalPointer()); |
1883 | WatchVar* parentItem = childItem->parent; |
1884 | |
1885 | //parent is root |
1886 | if (parentItem == nullptr) { |
1887 | return QModelIndex(); |
1888 | } |
1889 | int row; |
1890 | WatchVar* grandItem = parentItem->parent; |
1891 | if (grandItem == nullptr) { |
1892 | row = getWatchIndex(parentItem,mWatchVars); |
1893 | } else { |
1894 | row = getWatchIndex(parentItem,grandItem->children); |
1895 | } |
1896 | return createIndex(row,0,parentItem); |
1897 | } |
1898 | |
1899 | int WatchModel::rowCount(const QModelIndex &parent) const |
1900 | { |
1901 | if (!parent.isValid()) { |
1902 | return mWatchVars.count(); |
1903 | } else { |
1904 | WatchVar* parentItem = static_cast<WatchVar*>(parent.internalPointer()); |
1905 | return parentItem->children.count(); |
1906 | } |
1907 | } |
1908 | |
1909 | int WatchModel::columnCount(const QModelIndex&) const |
1910 | { |
1911 | return 3; |
1912 | } |
1913 | |
1914 | void WatchModel::addWatchVar(PWatchVar watchVar) |
1915 | { |
1916 | for (PWatchVar var:mWatchVars) { |
1917 | if (watchVar->expression == var->expression) { |
1918 | return; |
1919 | } |
1920 | } |
1921 | beginInsertRows(QModelIndex(),mWatchVars.count(),mWatchVars.count()); |
1922 | mWatchVars.append(watchVar); |
1923 | endInsertRows(); |
1924 | } |
1925 | |
1926 | void WatchModel::removeWatchVar(const QString &express) |
1927 | { |
1928 | for (int i=mWatchVars.size()-1;i>=0;i--) { |
1929 | PWatchVar var = mWatchVars[i]; |
1930 | if (express == var->expression) { |
1931 | this->beginResetModel(); |
1932 | //this->beginRemoveRows(QModelIndex(),i,i); |
1933 | if (mVarIndex.contains(var->name)) |
1934 | mVarIndex.remove(var->name); |
1935 | mWatchVars.removeAt(i); |
1936 | //this->endRemoveRows(); |
1937 | this->endResetModel(); |
1938 | } |
1939 | } |
1940 | } |
1941 | |
1942 | void WatchModel::removeWatchVar(const QModelIndex &index) |
1943 | { |
1944 | int r=index.row(); |
1945 | this->beginRemoveRows(QModelIndex(),r,r); |
1946 | PWatchVar var = mWatchVars[r]; |
1947 | if (mVarIndex.contains(var->name)) |
1948 | mVarIndex.remove(var->name); |
1949 | mWatchVars.removeAt(r); |
1950 | this->endRemoveRows(); |
1951 | } |
1952 | |
1953 | void WatchModel::clear() |
1954 | { |
1955 | this->beginResetModel(); |
1956 | mWatchVars.clear(); |
1957 | this->endResetModel(); |
1958 | } |
1959 | |
1960 | const QList<PWatchVar> &WatchModel::watchVars() |
1961 | { |
1962 | return mWatchVars; |
1963 | } |
1964 | |
1965 | PWatchVar WatchModel::findWatchVar(const QModelIndex &index) |
1966 | { |
1967 | if (!index.isValid()) |
1968 | return PWatchVar(); |
1969 | int r=index.row(); |
1970 | return mWatchVars[r]; |
1971 | } |
1972 | |
1973 | PWatchVar WatchModel::findWatchVar(const QString &expr) |
1974 | { |
1975 | for (PWatchVar var:mWatchVars) { |
1976 | if (expr == var->expression) { |
1977 | return var; |
1978 | } |
1979 | } |
1980 | return PWatchVar(); |
1981 | } |
1982 | |
1983 | void WatchModel::resetAllVarInfos() |
1984 | { |
1985 | beginResetModel(); |
1986 | foreach (PWatchVar var, mWatchVars) { |
1987 | var->name.clear(); |
1988 | var->value = tr("Not Valid" ); |
1989 | var->numChild = 0; |
1990 | var->hasMore = false; |
1991 | var->type.clear(); |
1992 | var->children.clear(); |
1993 | } |
1994 | mVarIndex.clear(); |
1995 | endResetModel(); |
1996 | } |
1997 | |
1998 | void WatchModel::updateVarInfo(const QString &expression, const QString &name, int numChild, const QString &value, const QString &type, bool hasMore) |
1999 | { |
2000 | PWatchVar var = findWatchVar(expression); |
2001 | if (!var) |
2002 | return; |
2003 | var->name = name; |
2004 | var->value = value; |
2005 | var->numChild = numChild; |
2006 | var->hasMore = hasMore; |
2007 | var->type = type; |
2008 | mVarIndex.insert(name,var); |
2009 | QModelIndex idx = index(var); |
2010 | if (!idx.isValid()) |
2011 | return; |
2012 | emit dataChanged(idx,createIndex(idx.row(),2,var.get())); |
2013 | } |
2014 | |
2015 | void WatchModel::prepareVarChildren(const QString &parentName, int numChild, bool hasMore) |
2016 | { |
2017 | PWatchVar var = mVarIndex.value(parentName,PWatchVar()); |
2018 | if (var) { |
2019 | var->numChild = numChild; |
2020 | var->hasMore = hasMore; |
2021 | if (var->children.count()>0) { |
2022 | beginRemoveRows(index(var),0,var->children.count()-1); |
2023 | var->children.clear(); |
2024 | endRemoveRows(); |
2025 | } |
2026 | } |
2027 | } |
2028 | |
2029 | void WatchModel::addVarChild(const QString &parentName, const QString &name, |
2030 | const QString &exp, int numChild, const QString &value, |
2031 | const QString &type, bool hasMore) |
2032 | { |
2033 | PWatchVar var = mVarIndex.value(parentName,PWatchVar()); |
2034 | if (!var) |
2035 | return; |
2036 | beginInsertRows(index(var),var->children.count(),var->children.count()); |
2037 | PWatchVar child = std::make_shared<WatchVar>(); |
2038 | child->name = name; |
2039 | child->expression = exp; |
2040 | child->numChild = numChild; |
2041 | child->value = value; |
2042 | child->type = type; |
2043 | child->hasMore = hasMore; |
2044 | child->parent = var.get(); |
2045 | var->children.append(child); |
2046 | endInsertRows(); |
2047 | mVarIndex.insert(name,child); |
2048 | } |
2049 | |
2050 | void WatchModel::updateVarValue(const QString &name, const QString &val, const QString &inScope, bool typeChanged, const QString &newType, int newNumChildren, bool hasMore) |
2051 | { |
2052 | PWatchVar var = mVarIndex.value(name,PWatchVar()); |
2053 | if (!var) |
2054 | return; |
2055 | if (inScope == "true" ) { |
2056 | var->value = val; |
2057 | } else{ |
2058 | var->value = tr("Not Valid" ); |
2059 | } |
2060 | if (typeChanged) { |
2061 | var->type = newType; |
2062 | } |
2063 | QModelIndex idx = index(var); |
2064 | bool oldHasMore = var->hasMore; |
2065 | var->hasMore = hasMore; |
2066 | if (newNumChildren>=0 |
2067 | && var->numChild!=newNumChildren) { |
2068 | var->numChild = newNumChildren; |
2069 | fetchMore(idx); |
2070 | } else if (!oldHasMore && hasMore) { |
2071 | fetchMore(idx); |
2072 | } |
2073 | emit dataChanged(idx,createIndex(idx.row(),2,var.get())); |
2074 | } |
2075 | |
2076 | void WatchModel::updateAllHasMoreVars() |
2077 | { |
2078 | foreach (const PWatchVar& var, mVarIndex.values()) { |
2079 | if (var->hasMore) { |
2080 | QModelIndex idx = index(var); |
2081 | fetchMore(idx); |
2082 | } |
2083 | } |
2084 | } |
2085 | |
2086 | void WatchModel::clearAllVarInfos() |
2087 | { |
2088 | beginResetModel(); |
2089 | foreach (PWatchVar var, mWatchVars) { |
2090 | var->name.clear(); |
2091 | var->value = tr("Execute to evaluate" ); |
2092 | var->numChild = 0; |
2093 | var->hasMore = false; |
2094 | var->type.clear(); |
2095 | var->children.clear(); |
2096 | } |
2097 | mVarIndex.clear(); |
2098 | endResetModel(); |
2099 | } |
2100 | |
2101 | void WatchModel::beginUpdate() |
2102 | { |
2103 | if (mUpdateCount == 0) { |
2104 | beginResetModel(); |
2105 | } |
2106 | mUpdateCount++; |
2107 | } |
2108 | |
2109 | void WatchModel::endUpdate() |
2110 | { |
2111 | mUpdateCount--; |
2112 | if (mUpdateCount == 0) { |
2113 | endResetModel(); |
2114 | } |
2115 | } |
2116 | |
2117 | void WatchModel::notifyUpdated(PWatchVar var) |
2118 | { |
2119 | if (!var) |
2120 | return; |
2121 | int row; |
2122 | if (var->parent==nullptr) { |
2123 | row = mWatchVars.indexOf(var); |
2124 | } else { |
2125 | row = var->parent->children.indexOf(var); |
2126 | } |
2127 | if (row<0) |
2128 | return; |
2129 | //qDebug()<<"dataChanged"<<row<<":"<<var->text; |
2130 | emit dataChanged(createIndex(row,0,var.get()),createIndex(row,0,var.get())); |
2131 | } |
2132 | |
2133 | void WatchModel::save(const QString &filename) |
2134 | { |
2135 | QFile file(filename); |
2136 | if (file.open(QFile::WriteOnly | QFile::Truncate)) { |
2137 | QJsonArray array; |
2138 | foreach (const PWatchVar& watchVar, mWatchVars) { |
2139 | QJsonObject obj; |
2140 | obj["expression" ]=watchVar->expression; |
2141 | array.append(obj); |
2142 | } |
2143 | QJsonDocument doc; |
2144 | doc.setArray(array); |
2145 | if (file.write(doc.toJson())<0) { |
2146 | throw FileError(tr("Save file '%1' failed." ) |
2147 | .arg(filename)); |
2148 | } |
2149 | } else { |
2150 | throw FileError(tr("Can't open file '%1' for write." ) |
2151 | .arg(filename)); |
2152 | } |
2153 | } |
2154 | |
2155 | void WatchModel::load(const QString &filename) |
2156 | { |
2157 | clear(); |
2158 | QFile file(filename); |
2159 | if (!file.exists()) |
2160 | return; |
2161 | if (file.open(QFile::ReadOnly)) { |
2162 | QByteArray content = file.readAll(); |
2163 | QJsonParseError error; |
2164 | QJsonDocument doc(QJsonDocument::fromJson(content,&error)); |
2165 | if (error.error != QJsonParseError::NoError) { |
2166 | throw FileError(tr("Error in json file '%1':%2 : %3" ) |
2167 | .arg(filename) |
2168 | .arg(error.offset) |
2169 | .arg(error.errorString())); |
2170 | } |
2171 | QJsonArray array = doc.array(); |
2172 | for (int i=0;i<array.count();i++) { |
2173 | QJsonValue value = array[i]; |
2174 | QJsonObject obj=value.toObject(); |
2175 | PWatchVar var = std::make_shared<WatchVar>(); |
2176 | var->parent= nullptr; |
2177 | var->expression = obj["expression" ].toString(); |
2178 | var->value = tr("Execute to evaluate" ); |
2179 | var->numChild = 0; |
2180 | var->hasMore=false; |
2181 | var->parent = nullptr; |
2182 | |
2183 | addWatchVar(var); |
2184 | } |
2185 | } else { |
2186 | throw FileError(tr("Can't open file '%1' for read." ) |
2187 | .arg(filename)); |
2188 | } |
2189 | } |
2190 | |
2191 | QModelIndex WatchModel::index(PWatchVar var) const |
2192 | { |
2193 | if (!var) |
2194 | return QModelIndex(); |
2195 | return index(var.get()); |
2196 | } |
2197 | |
2198 | QModelIndex WatchModel::index(WatchVar* pVar) const { |
2199 | if (pVar==nullptr) |
2200 | return QModelIndex(); |
2201 | if (pVar->parent) { |
2202 | int row=-1; |
2203 | for (int i=0;i<pVar->parent->children.count();i++) { |
2204 | if (pVar->parent->children[i].get() == pVar) { |
2205 | row = i; |
2206 | break; |
2207 | } |
2208 | } |
2209 | if (row<0) |
2210 | return QModelIndex(); |
2211 | return createIndex(row,0,pVar); |
2212 | } else { |
2213 | int row=-1; |
2214 | for (int i=0;i<mWatchVars.count();i++) { |
2215 | if (mWatchVars[i].get() == pVar) { |
2216 | row = i; |
2217 | break; |
2218 | } |
2219 | } |
2220 | if (row<0) |
2221 | return QModelIndex(); |
2222 | return createIndex(row,0,pVar); |
2223 | } |
2224 | } |
2225 | |
2226 | bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role) |
2227 | { |
2228 | if (!index.isValid()) { |
2229 | return false; |
2230 | } |
2231 | if (index.column()==2 && role == Qt::EditRole) { |
2232 | WatchVar* item = static_cast<WatchVar*>(index.internalPointer()); |
2233 | emit setWatchVarValue(item->name,value.toString()); |
2234 | } |
2235 | return false; |
2236 | |
2237 | } |
2238 | |
2239 | Qt::ItemFlags WatchModel::flags(const QModelIndex &index) const |
2240 | { |
2241 | Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; |
2242 | if (!index.isValid()) { |
2243 | return Qt::ItemIsEnabled; |
2244 | } |
2245 | if (index.column() == 2) { |
2246 | WatchVar* item = static_cast<WatchVar*>(index.internalPointer()); |
2247 | if (item->numChild==0 && !item->type.isEmpty()) |
2248 | flags |= Qt::ItemIsEditable; |
2249 | } |
2250 | return flags; |
2251 | } |
2252 | |
2253 | |
2254 | QVariant WatchModel::(int section, Qt::Orientation orientation, int role) const |
2255 | { |
2256 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { |
2257 | switch(section) { |
2258 | case 0: |
2259 | return tr("Expression" ); |
2260 | case 1: |
2261 | return tr("Type" ); |
2262 | case 2: |
2263 | return tr("Value" ); |
2264 | } |
2265 | } |
2266 | return QVariant(); |
2267 | } |
2268 | |
2269 | void WatchModel::fetchMore(const QModelIndex &parent) |
2270 | { |
2271 | if (!parent.isValid()) { |
2272 | return; |
2273 | } |
2274 | WatchVar* item = static_cast<WatchVar*>(parent.internalPointer()); |
2275 | item->hasMore = false; |
2276 | item->numChild = item->children.count(); |
2277 | emit fetchChildren(item->name); |
2278 | } |
2279 | |
2280 | bool WatchModel::canFetchMore(const QModelIndex &parent) const |
2281 | { |
2282 | if (!parent.isValid()) { |
2283 | return false; |
2284 | } |
2285 | WatchVar* item = static_cast<WatchVar*>(parent.internalPointer()); |
2286 | return item->numChild>item->children.count() || item->hasMore; |
2287 | } |
2288 | |
2289 | bool WatchModel::hasChildren(const QModelIndex &parent) const |
2290 | { |
2291 | if (!parent.isValid()) { |
2292 | return true; |
2293 | } |
2294 | WatchVar* item = static_cast<WatchVar*>(parent.internalPointer()); |
2295 | return item->numChild>0 || item->hasMore; |
2296 | } |
2297 | |
2298 | RegisterModel::RegisterModel(QObject *parent):QAbstractTableModel(parent) |
2299 | { |
2300 | |
2301 | } |
2302 | |
2303 | int RegisterModel::rowCount(const QModelIndex &) const |
2304 | { |
2305 | return mRegisterNames.count(); |
2306 | } |
2307 | |
2308 | int RegisterModel::columnCount(const QModelIndex &) const |
2309 | { |
2310 | return 2; |
2311 | } |
2312 | |
2313 | QVariant RegisterModel::data(const QModelIndex &index, int role) const |
2314 | { |
2315 | if (!index.isValid()) |
2316 | return QVariant(); |
2317 | if (index.row()<0 || index.row() >= static_cast<int>(mRegisterNames.size())) |
2318 | return QVariant(); |
2319 | switch (role) { |
2320 | case Qt::DisplayRole: |
2321 | switch (index.column()) { |
2322 | case 0: |
2323 | return mRegisterNames[index.row()]; |
2324 | case 1: |
2325 | return mRegisterValues.value(index.row(),"" ); |
2326 | default: |
2327 | return QVariant(); |
2328 | } |
2329 | default: |
2330 | return QVariant(); |
2331 | } |
2332 | } |
2333 | |
2334 | QVariant RegisterModel::(int section, Qt::Orientation orientation, int role) const |
2335 | { |
2336 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { |
2337 | switch(section) { |
2338 | case 0: |
2339 | return tr("Register" ); |
2340 | case 1: |
2341 | return tr("Value" ); |
2342 | } |
2343 | } |
2344 | return QVariant(); |
2345 | } |
2346 | |
2347 | void RegisterModel::updateNames(const QStringList ®Names) |
2348 | { |
2349 | beginResetModel(); |
2350 | mRegisterNames = regNames; |
2351 | endResetModel(); |
2352 | } |
2353 | |
2354 | void RegisterModel::updateValues(const QHash<int, QString> registerValues) |
2355 | { |
2356 | mRegisterValues= registerValues; |
2357 | emit dataChanged(createIndex(0,1), |
2358 | createIndex(mRegisterNames.count()-1,1)); |
2359 | } |
2360 | |
2361 | |
2362 | void RegisterModel::clear() |
2363 | { |
2364 | beginResetModel(); |
2365 | mRegisterNames.clear(); |
2366 | mRegisterValues.clear(); |
2367 | endResetModel(); |
2368 | } |
2369 | |
2370 | DebugTarget::DebugTarget( |
2371 | const QString &inferior, |
2372 | const QString &GDBServer, |
2373 | int port, |
2374 | QObject *parent): |
2375 | QThread(parent), |
2376 | mInferior(inferior), |
2377 | mGDBServer(GDBServer), |
2378 | mPort(port), |
2379 | mStop(false), |
2380 | mStartSemaphore(0), |
2381 | mErrorOccured(false) |
2382 | { |
2383 | mProcess = nullptr; |
2384 | } |
2385 | |
2386 | void DebugTarget::setInputFile(const QString &inputFile) |
2387 | { |
2388 | mInputFile = inputFile; |
2389 | } |
2390 | |
2391 | void DebugTarget::stopDebug() |
2392 | { |
2393 | mStop = true; |
2394 | } |
2395 | |
2396 | void DebugTarget::waitStart() |
2397 | { |
2398 | mStartSemaphore.acquire(1); |
2399 | } |
2400 | |
2401 | const QStringList &DebugTarget::binDirs() const |
2402 | { |
2403 | return mBinDirs; |
2404 | } |
2405 | |
2406 | void DebugTarget::addBinDirs(const QStringList &binDirs) |
2407 | { |
2408 | mBinDirs.append(binDirs); |
2409 | } |
2410 | |
2411 | void DebugTarget::addBinDir(const QString &binDir) |
2412 | { |
2413 | mBinDirs.append(binDir); |
2414 | } |
2415 | |
2416 | void DebugTarget::run() |
2417 | { |
2418 | mStop = false; |
2419 | mErrorOccured = false; |
2420 | |
2421 | //find first available port |
2422 | QString cmd; |
2423 | QString arguments; |
2424 | #ifdef Q_OS_WIN |
2425 | cmd= mGDBServer; |
2426 | arguments = QString(" localhost:%1 \"%2\"" ).arg(mPort).arg(mInferior); |
2427 | #else |
2428 | cmd= pSettings->environment().terminalPath(); |
2429 | arguments = QString(" -e \"%1\" localhost:%2 \"%3\"" ).arg(mGDBServer).arg(mPort).arg(mInferior); |
2430 | #endif |
2431 | QString workingDir = QFileInfo(mInferior).path(); |
2432 | |
2433 | mProcess = std::make_shared<QProcess>(); |
2434 | auto action = finally([&]{ |
2435 | mProcess.reset(); |
2436 | }); |
2437 | mProcess->setProgram(cmd); |
2438 | mProcess->setArguments(splitProcessCommand(arguments)); |
2439 | mProcess->setProcessChannelMode(QProcess::MergedChannels); |
2440 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); |
2441 | QString path = env.value("PATH" ); |
2442 | QStringList pathAdded = mBinDirs; |
2443 | if (!path.isEmpty()) { |
2444 | path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path; |
2445 | } else { |
2446 | path = pathAdded.join(PATH_SEPARATOR); |
2447 | } |
2448 | QString cmdDir = extractFileDir(cmd); |
2449 | if (!cmdDir.isEmpty()) { |
2450 | path = cmdDir + PATH_SEPARATOR + path; |
2451 | } |
2452 | env.insert("PATH" ,path); |
2453 | mProcess->setProcessEnvironment(env); |
2454 | mProcess->setWorkingDirectory(workingDir); |
2455 | |
2456 | #ifdef Q_OS_WIN |
2457 | mProcess->setCreateProcessArgumentsModifier([this](QProcess::CreateProcessArguments * args){ |
2458 | if (programHasConsole(mInferior)) { |
2459 | args->flags |= CREATE_NEW_CONSOLE; |
2460 | args->flags &= ~CREATE_NO_WINDOW; |
2461 | } |
2462 | if (mInputFile.isEmpty()) { |
2463 | args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES; |
2464 | } else { |
2465 | args->startupInfo->hStdOutput = NULL; |
2466 | args->startupInfo->hStdError = NULL; |
2467 | } |
2468 | |
2469 | }); |
2470 | #endif |
2471 | |
2472 | connect(mProcess.get(), &QProcess::errorOccurred, |
2473 | [&](){ |
2474 | mErrorOccured= true; |
2475 | }); |
2476 | mProcess->start(); |
2477 | mProcess->waitForStarted(5000); |
2478 | mStartSemaphore.release(1); |
2479 | if (mProcess->state()==QProcess::Running && !mInputFile.isEmpty()) { |
2480 | mProcess->write(readFileToByteArray(mInputFile)); |
2481 | mProcess->waitForFinished(0); |
2482 | } |
2483 | bool writeChannelClosed = false; |
2484 | while (true) { |
2485 | if (mProcess->bytesToWrite()==0 && !writeChannelClosed) { |
2486 | writeChannelClosed = true; |
2487 | mProcess->closeWriteChannel(); |
2488 | } |
2489 | mProcess->waitForFinished(1); |
2490 | if (mProcess->state()!=QProcess::Running) { |
2491 | break; |
2492 | } |
2493 | if (mStop) { |
2494 | mProcess->terminate(); |
2495 | mProcess->kill(); |
2496 | break; |
2497 | } |
2498 | if (mErrorOccured) |
2499 | break; |
2500 | msleep(1); |
2501 | } |
2502 | if (mErrorOccured) { |
2503 | emit processError(mProcess->error()); |
2504 | } |
2505 | } |
2506 | |
2507 | MemoryModel::MemoryModel(int dataPerLine, QObject *parent): |
2508 | QAbstractTableModel(parent), |
2509 | mDataPerLine(dataPerLine), |
2510 | mStartAddress(0) |
2511 | { |
2512 | } |
2513 | |
2514 | void MemoryModel::updateMemory(const QStringList &value) |
2515 | { |
2516 | int maxDataPerLine=-1; |
2517 | QRegExp delimiter("(\\s+)" ); |
2518 | QList<PMemoryLine> newModel; |
2519 | for (int i=0;i<value.length();i++) { |
2520 | QString line = value[i].trimmed(); |
2521 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) |
2522 | QStringList dataLst = line.split(delimiter,Qt::SkipEmptyParts); |
2523 | #else |
2524 | QStringList dataLst = line.split(delimiter,QString::SkipEmptyParts); |
2525 | #endif |
2526 | PMemoryLine memoryLine = std::make_shared<MemoryLine>(); |
2527 | memoryLine->startAddress = -1; |
2528 | if (dataLst.length()>0) { |
2529 | bool isOk; |
2530 | memoryLine->startAddress = stringToHex(dataLst[0],isOk); |
2531 | if (isOk) { |
2532 | if (dataLst.length()-1>maxDataPerLine) |
2533 | maxDataPerLine = dataLst.length()-1; |
2534 | for (int j=1;j<dataLst.length();j++) { |
2535 | qulonglong data = stringToHex(dataLst[j],isOk); |
2536 | if (isOk) |
2537 | memoryLine->datas.append((unsigned char)data); |
2538 | else |
2539 | memoryLine->datas.append(0); |
2540 | } |
2541 | } else { |
2542 | memoryLine->startAddress=0; |
2543 | } |
2544 | |
2545 | } |
2546 | newModel.append(memoryLine); |
2547 | } |
2548 | if (newModel.count()>0 && newModel.count()== mLines.count() && |
2549 | newModel[0]->startAddress == mLines[0]->startAddress && |
2550 | maxDataPerLine==mDataPerLine) { |
2551 | for (int i=0;i<newModel.count();i++) { |
2552 | PMemoryLine newLine = newModel[i]; |
2553 | PMemoryLine oldLine = mLines[i]; |
2554 | for (int j=0;j<newLine->datas.count();j++) { |
2555 | if (j>=oldLine->datas.count()) |
2556 | break; |
2557 | if (newLine->datas[j]!=oldLine->datas[j]) |
2558 | newLine->changedDatas.insert(j); |
2559 | } |
2560 | } |
2561 | mLines = newModel; |
2562 | emit dataChanged(createIndex(0,0), |
2563 | createIndex(mLines.count()-1,mDataPerLine-1)); |
2564 | } else { |
2565 | beginResetModel(); |
2566 | if (maxDataPerLine>0) |
2567 | mDataPerLine=maxDataPerLine; |
2568 | mLines = newModel; |
2569 | endResetModel(); |
2570 | } |
2571 | if (mLines.count()>0) { |
2572 | mStartAddress = mLines[0]->startAddress; |
2573 | } else { |
2574 | mStartAddress = 0; |
2575 | } |
2576 | } |
2577 | |
2578 | int MemoryModel::rowCount(const QModelIndex &/*parent*/) const |
2579 | { |
2580 | return mLines.count(); |
2581 | } |
2582 | |
2583 | int MemoryModel::columnCount(const QModelIndex &/*parent*/) const |
2584 | { |
2585 | return mDataPerLine; |
2586 | } |
2587 | |
2588 | QVariant MemoryModel::data(const QModelIndex &index, int role) const |
2589 | { |
2590 | if (!index.isValid()) |
2591 | return QVariant(); |
2592 | if (index.row()<0 || index.row()>=mLines.count()) |
2593 | return QVariant(); |
2594 | PMemoryLine line = mLines[index.row()]; |
2595 | int col = index.column(); |
2596 | if (col<0 || col>=line->datas.count()) |
2597 | return QVariant(); |
2598 | if (role == Qt::DisplayRole) |
2599 | return QString("%1" ).arg(line->datas[col],2,16,QChar('0')); |
2600 | return QVariant(); |
2601 | } |
2602 | |
2603 | QVariant MemoryModel::(int section, Qt::Orientation orientation, int role) const |
2604 | { |
2605 | if (orientation == Qt::Vertical && role == Qt::DisplayRole) { |
2606 | if (section<0 || section>=mLines.count()) |
2607 | return QVariant(); |
2608 | PMemoryLine line = mLines[section]; |
2609 | return QString("0x%1" ).arg(line->startAddress,0,16,QChar('0')); |
2610 | } |
2611 | return QVariant(); |
2612 | } |
2613 | |
2614 | bool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role) |
2615 | { |
2616 | if (!index.isValid()) |
2617 | return false; |
2618 | if (index.row()<0 || index.row()>=mLines.count()) |
2619 | return false; |
2620 | PMemoryLine line = mLines[index.row()]; |
2621 | int col = index.column(); |
2622 | if (col<0 || col>=line->datas.count()) |
2623 | return false; |
2624 | if (role == Qt::EditRole && mStartAddress>0) { |
2625 | bool ok; |
2626 | unsigned char val = ("0x" +value.toString()).toUInt(&ok,16); |
2627 | if (!ok) |
2628 | return false; |
2629 | emit setMemoryData(mStartAddress+mDataPerLine*index.row()+col,val); |
2630 | return true; |
2631 | } |
2632 | return false; |
2633 | } |
2634 | |
2635 | Qt::ItemFlags MemoryModel::flags(const QModelIndex &/*index*/) const |
2636 | { |
2637 | Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; |
2638 | if (mStartAddress!=0) |
2639 | flags |= Qt::ItemIsEditable; |
2640 | return flags; |
2641 | } |
2642 | |
2643 | qulonglong MemoryModel::startAddress() const |
2644 | { |
2645 | return mStartAddress; |
2646 | } |
2647 | |
2648 | void MemoryModel::reset() |
2649 | { |
2650 | mStartAddress=0; |
2651 | mLines.clear(); |
2652 | } |
2653 | |