1 /***************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "breakhandler.h"
27 
28 #include "debuggeractions.h"
29 #include "debuggercore.h"
30 #include "debuggerengine.h"
31 #include "debuggericons.h"
32 #include "debuggerinternalconstants.h"
33 #include "disassembleragent.h"
34 #include "enginemanager.h"
35 #include "simplifytype.h"
36 
37 #include <coreplugin/coreconstants.h>
38 #include <coreplugin/coreplugin.h>
39 #include <coreplugin/editormanager/editormanager.h>
40 #include <coreplugin/icore.h>
41 #include <coreplugin/idocument.h>
42 
43 #include <projectexplorer/session.h>
44 
45 #include <texteditor/textmark.h>
46 #include <texteditor/texteditor.h>
47 
48 #include <utils/basetreeview.h>
49 #include <utils/checkablemessagebox.h>
50 #include <utils/fileutils.h>
51 #include <utils/hostosinfo.h>
52 #include <utils/pathchooser.h>
53 #include <utils/qtcassert.h>
54 #include <utils/theme/theme.h>
55 
56 #if USE_BREAK_MODEL_TEST
57 #include <modeltest.h>
58 #endif
59 
60 #include <QApplication>
61 #include <QCheckBox>
62 #include <QComboBox>
63 #include <QDebug>
64 #include <QDir>
65 #include <QFormLayout>
66 #include <QGroupBox>
67 #include <QMenu>
68 #include <QSpinBox>
69 #include <QStyledItemDelegate>
70 #include <QTimer>
71 #include <QTimerEvent>
72 
73 using namespace Core;
74 using namespace ProjectExplorer;
75 using namespace Utils;
76 
77 namespace Debugger {
78 namespace Internal {
79 
80 static BreakpointManager *theBreakpointManager = nullptr;
81 
82 //
83 // BreakpointMarker
84 //
85 
86 // The red blob on the left side in the cpp editor.
87 class BreakpointMarker : public TextEditor::TextMark
88 {
89 public:
BreakpointMarker(const Breakpoint & bp,const FilePath & fileName,int lineNumber)90     BreakpointMarker(const Breakpoint &bp, const FilePath &fileName, int lineNumber)
91         : TextMark(fileName, lineNumber, Constants::TEXT_MARK_CATEGORY_BREAKPOINT), m_bp(bp)
92     {
93         setColor(Theme::Debugger_Breakpoint_TextMarkColor);
94         setDefaultToolTip(QApplication::translate("BreakHandler", "Breakpoint"));
95         setPriority(TextEditor::TextMark::NormalPriority);
96         setIconProvider([bp] { return bp->icon(); });
97         setToolTipProvider([bp] { return bp->toolTip(); });
98     }
99 
updateLineNumber(int lineNumber)100     void updateLineNumber(int lineNumber) final
101     {
102         TextMark::updateLineNumber(lineNumber);
103         QTC_ASSERT(m_bp, return);
104         m_bp->setLineNumber(lineNumber);
105         if (GlobalBreakpoint gbp = m_bp->globalBreakpoint())
106             gbp->m_params.lineNumber = lineNumber;
107     }
108 
updateFileName(const FilePath & fileName)109     void updateFileName(const FilePath &fileName) final
110     {
111         TextMark::updateFileName(fileName);
112         QTC_ASSERT(m_bp, return);
113         m_bp->setFileName(fileName);
114         if (GlobalBreakpoint gbp = m_bp->globalBreakpoint())
115             gbp->m_params.fileName = fileName;
116     }
117 
isDraggable() const118     bool isDraggable() const final { return true; }
119 
dragToLine(int line)120     void dragToLine(int line) final
121     {
122         QTC_ASSERT(m_bp, return);
123         GlobalBreakpoint gbp = m_bp->globalBreakpoint();
124         if (!gbp)
125             return;
126         BreakpointParameters params = gbp->m_params;
127         params.lineNumber = line;
128         gbp->deleteBreakpoint();
129         BreakpointManager::createBreakpoint(params);
130     }
131 
isClickable() const132     bool isClickable() const final { return true; }
133 
clicked()134     void clicked() final
135     {
136         QTC_ASSERT(m_bp, return);
137         m_bp->deleteGlobalOrThisBreakpoint();
138     }
139 
140 public:
141     Breakpoint m_bp;
142 };
143 
144 // The red blob on the left side in the cpp editor.
145 class GlobalBreakpointMarker : public TextEditor::TextMark
146 {
147 public:
GlobalBreakpointMarker(GlobalBreakpoint gbp,const FilePath & fileName,int lineNumber)148     GlobalBreakpointMarker(GlobalBreakpoint gbp, const FilePath &fileName, int lineNumber)
149         : TextMark(fileName, lineNumber, Constants::TEXT_MARK_CATEGORY_BREAKPOINT), m_gbp(gbp)
150     {
151         setDefaultToolTip(QApplication::translate("BreakHandler", "Breakpoint"));
152         setPriority(TextEditor::TextMark::NormalPriority);
153         setIconProvider([this] { return m_gbp->icon(); });
154         setToolTipProvider([this] { return m_gbp->toolTip(); });
155     }
156 
removedFromEditor()157     void removedFromEditor() final
158     {
159         QTC_ASSERT(m_gbp, return);
160         m_gbp->removeBreakpointFromModel();
161     }
162 
updateLineNumber(int lineNumber)163     void updateLineNumber(int lineNumber) final
164     {
165         TextMark::updateLineNumber(lineNumber);
166         QTC_ASSERT(m_gbp, return);
167 
168         // Ignore updates to the "real" line number while the debugger is
169         // running, as this can be triggered by moving the breakpoint to
170         // the next line that generated code.
171 
172         m_gbp->updateLineNumber(lineNumber);
173     }
174 
updateFileName(const FilePath & fileName)175     void updateFileName(const FilePath &fileName) final
176     {
177         TextMark::updateFileName(fileName);
178         QTC_ASSERT(m_gbp, return);
179         m_gbp->updateFileName(fileName);
180     }
181 
isDraggable() const182     bool isDraggable() const final { return true; }
183 
dragToLine(int line)184     void dragToLine(int line) final
185     {
186         TextMark::move(line);
187         QTC_ASSERT(m_gbp, return);
188         QTC_ASSERT(BreakpointManager::globalBreakpoints().contains(m_gbp), return);
189         m_gbp->updateLineNumber(line);
190     }
191 
isClickable() const192     bool isClickable() const final { return true; }
193 
clicked()194     void clicked() final
195     {
196         QTC_ASSERT(m_gbp, return);
197         m_gbp->removeBreakpointFromModel();
198     }
199 
200 public:
201     GlobalBreakpoint m_gbp;
202 };
203 
stateToString(BreakpointState state)204 static QString stateToString(BreakpointState state)
205 {
206     switch (state) {
207         case BreakpointNew:
208             return BreakHandler::tr("New");
209         case BreakpointInsertionRequested:
210             return BreakHandler::tr("Insertion requested");
211         case BreakpointInsertionProceeding:
212             return BreakHandler::tr("Insertion proceeding");
213         case BreakpointUpdateRequested:
214             return BreakHandler::tr("Change requested");
215         case BreakpointUpdateProceeding:
216             return BreakHandler::tr("Change proceeding");
217         case BreakpointInserted:
218             return BreakHandler::tr("Breakpoint inserted");
219         case BreakpointRemoveRequested:
220             return BreakHandler::tr("Removal requested");
221         case BreakpointRemoveProceeding:
222             return BreakHandler::tr("Removal proceeding");
223         case BreakpointDead:
224             return BreakHandler::tr("Dead");
225         default:
226             break;
227     }
228     //: Invalid breakpoint state.
229     return BreakHandler::tr("<invalid state>");
230 }
231 
msgBreakpointAtSpecialFunc(const QString & func)232 static QString msgBreakpointAtSpecialFunc(const QString &func)
233 {
234     return BreakHandler::tr("Breakpoint at \"%1\"").arg(func);
235 }
236 
typeToString(BreakpointType type)237 static QString typeToString(BreakpointType type)
238 {
239     switch (type) {
240         case BreakpointByFileAndLine:
241             return BreakHandler::tr("Breakpoint by File and Line");
242         case BreakpointByFunction:
243             return BreakHandler::tr("Breakpoint by Function");
244         case BreakpointByAddress:
245             return BreakHandler::tr("Breakpoint by Address");
246         case BreakpointAtThrow:
247             return msgBreakpointAtSpecialFunc("throw");
248         case BreakpointAtCatch:
249             return msgBreakpointAtSpecialFunc("catch");
250         case BreakpointAtFork:
251             return msgBreakpointAtSpecialFunc("fork");
252         case BreakpointAtExec:
253             return msgBreakpointAtSpecialFunc("exec");
254         //case BreakpointAtVFork:
255         //    return msgBreakpointAtSpecialFunc("vfork");
256         case BreakpointAtSysCall:
257             return msgBreakpointAtSpecialFunc("syscall");
258         case BreakpointAtMain:
259             return BreakHandler::tr("Breakpoint at Function \"main()\"");
260         case WatchpointAtAddress:
261             return BreakHandler::tr("Watchpoint at Address");
262         case WatchpointAtExpression:
263             return BreakHandler::tr("Watchpoint at Expression");
264         case BreakpointOnQmlSignalEmit:
265             return BreakHandler::tr("Breakpoint on QML Signal Emit");
266         case BreakpointAtJavaScriptThrow:
267             return BreakHandler::tr("Breakpoint at JavaScript throw");
268         case UnknownBreakpointType:
269         case LastBreakpointType:
270             break;
271     }
272     return BreakHandler::tr("Unknown Breakpoint Type");
273 }
274 
275 class LeftElideDelegate : public QStyledItemDelegate
276 {
277 public:
278     LeftElideDelegate() = default;
279 
paint(QPainter * pain,const QStyleOptionViewItem & option,const QModelIndex & index) const280     void paint(QPainter *pain, const QStyleOptionViewItem &option, const QModelIndex &index) const override
281     {
282         QStyleOptionViewItem opt = option;
283         opt.textElideMode = Qt::ElideLeft;
284         QStyledItemDelegate::paint(pain, opt, index);
285     }
286 };
287 
288 class SmallTextEdit : public QTextEdit
289 {
290 public:
SmallTextEdit(QWidget * parent)291     explicit SmallTextEdit(QWidget *parent) : QTextEdit(parent) {}
sizeHint() const292     QSize sizeHint() const override { return QSize(QTextEdit::sizeHint().width(), 100); }
minimumSizeHint() const293     QSize minimumSizeHint() const override { return sizeHint(); }
294 };
295 
296 ///////////////////////////////////////////////////////////////////////
297 //
298 // BreakpointDialog: Show a dialog for editing breakpoints. Shows controls
299 // for the file-and-line, function and address parameters depending on the
300 // breakpoint type. The controls not applicable to the current type
301 // (say function name for file-and-line) are disabled and cleared out.
302 // However,the values are saved and restored once the respective mode
303 // is again chosen, which is done using m_savedParameters and
304 // setters/getters taking the parts mask enumeration parameter.
305 //
306 ///////////////////////////////////////////////////////////////////////
307 
308 class BreakpointDialog : public QDialog
309 {
310     Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
311 
312 public:
313     explicit BreakpointDialog(unsigned int enabledParts, QWidget *parent = nullptr);
314     bool showDialog(BreakpointParameters *data, BreakpointParts *parts);
315 
316     void setParameters(const BreakpointParameters &data);
317     BreakpointParameters parameters() const;
318 
319     void typeChanged(int index);
320 
321 private:
322     void setPartsEnabled(unsigned partsMask);
323     void clearOtherParts(unsigned partsMask);
324     void getParts(unsigned partsMask, BreakpointParameters *data) const;
325     void setParts(unsigned partsMask, const BreakpointParameters &data);
326 
327     void setType(BreakpointType type);
328     BreakpointType type() const;
329 
330     unsigned m_enabledParts;
331     BreakpointParameters m_savedParameters;
332     BreakpointType m_previousType;
333     bool m_firstTypeChange;
334 
335     QLabel *m_labelType;
336     QComboBox *m_comboBoxType;
337     QLabel *m_labelFileName;
338     Utils::PathChooser *m_pathChooserFileName;
339     QLabel *m_labelLineNumber;
340     QLineEdit *m_lineEditLineNumber;
341     QCheckBox *m_checkBoxPropagate;
342     QLabel *m_labelEnabled;
343     QCheckBox *m_checkBoxEnabled;
344     QLabel *m_labelAddress;
345     QLineEdit *m_lineEditAddress;
346     QLabel *m_labelExpression;
347     QLineEdit *m_lineEditExpression;
348     QLabel *m_labelFunction;
349     QLineEdit *m_lineEditFunction;
350     QLabel *m_labelTracepoint;
351     QCheckBox *m_checkBoxTracepoint;
352     QLabel *m_labelOneShot;
353     QCheckBox *m_checkBoxOneShot;
354     QLabel *m_labelUseFullPath;
355     QLabel *m_labelModule;
356     QLineEdit *m_lineEditModule;
357     QLabel *m_labelCommands;
358     QTextEdit *m_textEditCommands;
359     QComboBox *m_comboBoxPathUsage;
360     QLabel *m_labelMessage;
361     QLineEdit *m_lineEditMessage;
362     QLabel *m_labelCondition;
363     QLineEdit *m_lineEditCondition;
364     QLabel *m_labelIgnoreCount;
365     QSpinBox *m_spinBoxIgnoreCount;
366     QLabel *m_labelThreadSpec;
367     QLineEdit *m_lineEditThreadSpec;
368     QDialogButtonBox *m_buttonBox;
369 };
370 
BreakpointDialog(unsigned int enabledParts,QWidget * parent)371 BreakpointDialog::BreakpointDialog(unsigned int enabledParts, QWidget *parent)
372     : QDialog(parent), m_enabledParts(enabledParts), m_previousType(UnknownBreakpointType),
373       m_firstTypeChange(true)
374 {
375     setWindowTitle(tr("Edit Breakpoint Properties"));
376 
377     auto groupBoxBasic = new QGroupBox(tr("Basic"), this);
378 
379     // Match BreakpointType (omitting unknown type).
380     const QStringList types = {
381         tr("File Name and Line Number"),
382         tr("Function Name"),
383         tr("Break on Memory Address"),
384         tr("Break When C++ Exception Is Thrown"),
385         tr("Break When C++ Exception Is Caught"),
386         tr("Break When Function \"main\" Starts"),
387         tr("Break When a New Process Is Forked"),
388         tr("Break When a New Process Is Executed"),
389         tr("Break When a System Call Is Executed"),
390         tr("Break on Data Access at Fixed Address"),
391         tr("Break on Data Access at Address Given by Expression"),
392         tr("Break on QML Signal Emit"),
393         tr("Break When JavaScript Exception Is Thrown")
394     };
395     // We don't list UnknownBreakpointType, so 1 less:
396     QTC_CHECK(types.size() + 1 == LastBreakpointType);
397     m_comboBoxType = new QComboBox(groupBoxBasic);
398     m_comboBoxType->setMaxVisibleItems(20);
399     m_comboBoxType->addItems(types);
400     m_labelType = new QLabel(tr("Breakpoint &type:"), groupBoxBasic);
401     m_labelType->setBuddy(m_comboBoxType);
402 
403     m_pathChooserFileName = new PathChooser(groupBoxBasic);
404     m_pathChooserFileName->setHistoryCompleter("Debugger.Breakpoint.File.History");
405     m_pathChooserFileName->setExpectedKind(PathChooser::File);
406     m_labelFileName = new QLabel(tr("&File name:"), groupBoxBasic);
407     m_labelFileName->setBuddy(m_pathChooserFileName);
408 
409     m_lineEditLineNumber = new QLineEdit(groupBoxBasic);
410     m_labelLineNumber = new QLabel(tr("&Line number:"), groupBoxBasic);
411     m_labelLineNumber->setBuddy(m_lineEditLineNumber);
412 
413     m_checkBoxEnabled = new QCheckBox(groupBoxBasic);
414     m_labelEnabled = new QLabel(tr("&Enabled:"), groupBoxBasic);
415     m_labelEnabled->setBuddy(m_checkBoxEnabled);
416 
417     m_lineEditAddress = new QLineEdit(groupBoxBasic);
418     m_labelAddress = new QLabel(tr("&Address:"), groupBoxBasic);
419     m_labelAddress->setBuddy(m_lineEditAddress);
420 
421     m_lineEditExpression = new QLineEdit(groupBoxBasic);
422     m_labelExpression = new QLabel(tr("&Expression:"), groupBoxBasic);
423     m_labelExpression->setBuddy(m_lineEditExpression);
424 
425     m_lineEditFunction = new QLineEdit(groupBoxBasic);
426     m_labelFunction = new QLabel(tr("Fun&ction:"), groupBoxBasic);
427     m_labelFunction->setBuddy(m_lineEditFunction);
428 
429     auto groupBoxAdvanced = new QGroupBox(tr("Advanced"), this);
430 
431     m_checkBoxTracepoint = new QCheckBox(groupBoxAdvanced);
432     m_labelTracepoint = new QLabel(tr("T&racepoint only:"), groupBoxAdvanced);
433     m_labelTracepoint->setBuddy(m_checkBoxTracepoint);
434 
435     m_checkBoxOneShot = new QCheckBox(groupBoxAdvanced);
436     m_labelOneShot = new QLabel(tr("&One shot only:"), groupBoxAdvanced);
437     m_labelOneShot->setBuddy(m_checkBoxOneShot);
438 
439     const QString pathToolTip =
440         tr("<p>Determines how the path is specified "
441                 "when setting breakpoints:</p><ul>"
442            "<li><i>Use Engine Default</i>: Preferred setting of the "
443                 "debugger engine.</li>"
444            "<li><i>Use Full Path</i>: Pass full path, avoiding ambiguities "
445                 "should files of the same name exist in several modules. "
446                 "This is the engine default for CDB and LLDB.</li>"
447            "<li><i>Use File Name</i>: Pass the file name only. This is "
448                 "useful when using a source tree whose location does "
449                 "not match the one used when building the modules. "
450                 "It is the engine default for GDB as using full paths can "
451                 "be slow with this engine.</li></ul>");
452     m_comboBoxPathUsage = new QComboBox(groupBoxAdvanced);
453     m_comboBoxPathUsage->addItem(tr("Use Engine Default"));
454     m_comboBoxPathUsage->addItem(tr("Use Full Path"));
455     m_comboBoxPathUsage->addItem(tr("Use File Name"));
456     m_comboBoxPathUsage->setToolTip(pathToolTip);
457     m_labelUseFullPath = new QLabel(tr("Pat&h:"), groupBoxAdvanced);
458     m_labelUseFullPath->setBuddy(m_comboBoxPathUsage);
459     m_labelUseFullPath->setToolTip(pathToolTip);
460 
461     const QString moduleToolTip =
462             "<p>" + tr("Specifying the module (base name of the library or executable) "
463                        "for function or file type breakpoints can significantly speed up "
464                        "debugger startup times (CDB, LLDB).") + "</p>";
465     m_lineEditModule = new QLineEdit(groupBoxAdvanced);
466     m_lineEditModule->setToolTip(moduleToolTip);
467     m_labelModule = new QLabel(tr("&Module:"), groupBoxAdvanced);
468     m_labelModule->setBuddy(m_lineEditModule);
469     m_labelModule->setToolTip(moduleToolTip);
470 
471     const QString commandsToolTip =
472             "<p>" + tr("Debugger commands to be executed when the breakpoint is hit. "
473                        "This feature is only available for GDB.") + "</p>";
474     m_textEditCommands = new SmallTextEdit(groupBoxAdvanced);
475     m_textEditCommands->setToolTip(commandsToolTip);
476     m_labelCommands = new QLabel(tr("&Commands:"), groupBoxAdvanced);
477     m_labelCommands->setBuddy(m_textEditCommands);
478     m_labelCommands->setToolTip(commandsToolTip);
479 
480     m_lineEditMessage = new QLineEdit(groupBoxAdvanced);
481     m_labelMessage = new QLabel(tr("&Message:"), groupBoxAdvanced);
482     m_labelMessage->setBuddy(m_lineEditMessage);
483 
484     m_lineEditCondition = new QLineEdit(groupBoxAdvanced);
485     m_labelCondition = new QLabel(tr("C&ondition:"), groupBoxAdvanced);
486     m_labelCondition->setBuddy(m_lineEditCondition);
487 
488     m_spinBoxIgnoreCount = new QSpinBox(groupBoxAdvanced);
489     m_spinBoxIgnoreCount->setMinimum(0);
490     m_spinBoxIgnoreCount->setMaximum(2147483647);
491     m_labelIgnoreCount = new QLabel(tr("&Ignore count:"), groupBoxAdvanced);
492     m_labelIgnoreCount->setBuddy(m_spinBoxIgnoreCount);
493 
494     m_lineEditThreadSpec = new QLineEdit(groupBoxAdvanced);
495     m_labelThreadSpec = new QLabel(tr("&Thread specification:"), groupBoxAdvanced);
496     m_labelThreadSpec->setBuddy(m_lineEditThreadSpec);
497 
498     m_checkBoxPropagate = new QCheckBox(tr("Propagate Change to Preset Breakpoint"), this);
499     m_checkBoxPropagate->setCheckable(true);
500     m_checkBoxPropagate->setChecked(true);
501     m_checkBoxPropagate->setVisible(false); // FIXME: Make it work.
502 
503     m_buttonBox = new QDialogButtonBox(this);
504     m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
505 
506     auto basicLayout = new QFormLayout(groupBoxBasic);
507     basicLayout->addRow(m_labelType, m_comboBoxType);
508     basicLayout->addRow(m_labelFileName, m_pathChooserFileName);
509     basicLayout->addRow(m_labelLineNumber, m_lineEditLineNumber);
510     basicLayout->addRow(m_labelEnabled, m_checkBoxEnabled);
511     basicLayout->addRow(m_labelAddress, m_lineEditAddress);
512     basicLayout->addRow(m_labelExpression, m_lineEditExpression);
513     basicLayout->addRow(m_labelFunction, m_lineEditFunction);
514     basicLayout->addRow(m_labelOneShot, m_checkBoxOneShot);
515 
516     auto advancedLeftLayout = new QFormLayout();
517     advancedLeftLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
518     advancedLeftLayout->addRow(m_labelCondition, m_lineEditCondition);
519     advancedLeftLayout->addRow(m_labelIgnoreCount, m_spinBoxIgnoreCount);
520     advancedLeftLayout->addRow(m_labelThreadSpec, m_lineEditThreadSpec);
521     advancedLeftLayout->addRow(m_labelUseFullPath, m_comboBoxPathUsage);
522     advancedLeftLayout->addRow(m_labelModule, m_lineEditModule);
523 
524     auto advancedRightLayout = new QFormLayout();
525     advancedRightLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
526     advancedRightLayout->addRow(m_labelCommands, m_textEditCommands);
527     advancedRightLayout->addRow(m_labelTracepoint, m_checkBoxTracepoint);
528     advancedRightLayout->addRow(m_labelMessage, m_lineEditMessage);
529 
530     auto horizontalLayout = new QHBoxLayout(groupBoxAdvanced);
531     horizontalLayout->addLayout(advancedLeftLayout);
532     horizontalLayout->addSpacing(15);
533     horizontalLayout->addLayout(advancedRightLayout);
534 
535     auto verticalLayout = new QVBoxLayout(this);
536     verticalLayout->addWidget(groupBoxBasic);
537     verticalLayout->addSpacing(10);
538     verticalLayout->addWidget(groupBoxAdvanced);
539     verticalLayout->addSpacing(10);
540     verticalLayout->addWidget(m_checkBoxPropagate);
541     verticalLayout->addSpacing(10);
542     verticalLayout->addWidget(m_buttonBox);
543     verticalLayout->setStretchFactor(groupBoxAdvanced, 10);
544 
545     connect(m_comboBoxType, QOverload<int>::of(&QComboBox::activated),
546             this, &BreakpointDialog::typeChanged);
547     connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
548     connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
549 }
550 
setType(BreakpointType type)551 void BreakpointDialog::setType(BreakpointType type)
552 {
553     const int comboIndex = type - 1; // Skip UnknownType.
554     if (comboIndex != m_comboBoxType->currentIndex() || m_firstTypeChange) {
555         m_comboBoxType->setCurrentIndex(comboIndex);
556         typeChanged(comboIndex);
557         m_firstTypeChange = false;
558     }
559 }
560 
type() const561 BreakpointType BreakpointDialog::type() const
562 {
563     const int type = m_comboBoxType->currentIndex() + 1; // Skip unknown type.
564     return static_cast<BreakpointType>(type);
565 }
566 
setParameters(const BreakpointParameters & data)567 void BreakpointDialog::setParameters(const BreakpointParameters &data)
568 {
569     m_savedParameters = data;
570     setType(data.type);
571     setParts(AllParts, data);
572 }
573 
parameters() const574 BreakpointParameters BreakpointDialog::parameters() const
575 {
576     BreakpointParameters data(type());
577     getParts(AllParts, &data);
578     return data;
579 }
580 
setPartsEnabled(unsigned partsMask)581 void BreakpointDialog::setPartsEnabled(unsigned partsMask)
582 {
583     partsMask &= m_enabledParts;
584     m_labelFileName->setEnabled(partsMask & FileAndLinePart);
585     m_pathChooserFileName->setEnabled(partsMask & FileAndLinePart);
586     m_labelLineNumber->setEnabled(partsMask & FileAndLinePart);
587     m_lineEditLineNumber->setEnabled(partsMask & FileAndLinePart);
588     m_labelUseFullPath->setEnabled(partsMask & FileAndLinePart);
589     m_comboBoxPathUsage->setEnabled(partsMask & FileAndLinePart);
590 
591     m_labelFunction->setEnabled(partsMask & FunctionPart);
592     m_lineEditFunction->setEnabled(partsMask & FunctionPart);
593 
594     m_labelOneShot->setEnabled(partsMask & OneShotPart);
595     m_checkBoxOneShot->setEnabled(partsMask & OneShotPart);
596 
597     m_labelAddress->setEnabled(partsMask & AddressPart);
598     m_lineEditAddress->setEnabled(partsMask & AddressPart);
599     m_labelExpression->setEnabled(partsMask & ExpressionPart);
600     m_lineEditExpression->setEnabled(partsMask & ExpressionPart);
601 
602     m_labelCondition->setEnabled(partsMask & ConditionPart);
603     m_lineEditCondition->setEnabled(partsMask & ConditionPart);
604     m_labelIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
605     m_spinBoxIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
606     m_labelThreadSpec->setEnabled(partsMask & ThreadSpecPart);
607     m_lineEditThreadSpec->setEnabled(partsMask & ThreadSpecPart);
608 
609     m_labelModule->setEnabled(partsMask & ModulePart);
610     m_lineEditModule->setEnabled(partsMask & ModulePart);
611 
612     m_labelTracepoint->setEnabled(partsMask & TracePointPart);
613     m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart);
614 
615     m_labelCommands->setEnabled(partsMask & CommandPart);
616     m_textEditCommands->setEnabled(partsMask & CommandPart);
617 
618     m_labelMessage->setEnabled(partsMask & TracePointPart);
619     m_lineEditMessage->setEnabled(partsMask & TracePointPart);
620 }
621 
clearOtherParts(unsigned partsMask)622 void BreakpointDialog::clearOtherParts(unsigned partsMask)
623 {
624     const unsigned invertedPartsMask = ~partsMask;
625     if (invertedPartsMask & FileAndLinePart) {
626         m_pathChooserFileName->setPath(QString());
627         m_lineEditLineNumber->clear();
628         m_comboBoxPathUsage->setCurrentIndex(BreakpointPathUsageEngineDefault);
629     }
630 
631     if (invertedPartsMask & FunctionPart)
632         m_lineEditFunction->clear();
633 
634     if (invertedPartsMask & AddressPart)
635         m_lineEditAddress->clear();
636     if (invertedPartsMask & ExpressionPart)
637         m_lineEditExpression->clear();
638 
639     if (invertedPartsMask & ConditionPart)
640         m_lineEditCondition->clear();
641     if (invertedPartsMask & IgnoreCountPart)
642         m_spinBoxIgnoreCount->clear();
643     if (invertedPartsMask & ThreadSpecPart)
644         m_lineEditThreadSpec->clear();
645     if (invertedPartsMask & ModulePart)
646         m_lineEditModule->clear();
647 
648     if (partsMask & OneShotPart)
649         m_checkBoxOneShot->setChecked(false);
650     if (invertedPartsMask & CommandPart)
651         m_textEditCommands->clear();
652     if (invertedPartsMask & TracePointPart) {
653         m_checkBoxTracepoint->setChecked(false);
654         m_lineEditMessage->clear();
655     }
656 }
657 
getParts(unsigned partsMask,BreakpointParameters * data) const658 void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) const
659 {
660     data->enabled = m_checkBoxEnabled->isChecked();
661 
662     if (partsMask & FileAndLinePart) {
663         data->lineNumber = m_lineEditLineNumber->text().toInt();
664         data->pathUsage = static_cast<BreakpointPathUsage>(m_comboBoxPathUsage->currentIndex());
665         data->fileName = m_pathChooserFileName->filePath();
666     }
667     if (partsMask & FunctionPart)
668         data->functionName = m_lineEditFunction->text();
669 
670     if (partsMask & AddressPart)
671         data->address = m_lineEditAddress->text().toULongLong(nullptr, 0);
672     if (partsMask & ExpressionPart)
673         data->expression = m_lineEditExpression->text();
674 
675     if (partsMask & ConditionPart)
676         data->condition = m_lineEditCondition->text();
677     if (partsMask & IgnoreCountPart)
678         data->ignoreCount = m_spinBoxIgnoreCount->text().toInt();
679     if (partsMask & ThreadSpecPart)
680         data->threadSpec =
681             BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text());
682     if (partsMask & ModulePart)
683         data->module = m_lineEditModule->text();
684 
685     if (partsMask & OneShotPart)
686         data->oneShot = m_checkBoxOneShot->isChecked();
687     if (partsMask & CommandPart)
688         data->command = m_textEditCommands->toPlainText().trimmed();
689     if (partsMask & TracePointPart) {
690         data->tracepoint = m_checkBoxTracepoint->isChecked();
691         data->message = m_lineEditMessage->text();
692     }
693 }
694 
setParts(unsigned mask,const BreakpointParameters & data)695 void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data)
696 {
697     m_checkBoxEnabled->setChecked(data.enabled);
698     m_comboBoxPathUsage->setCurrentIndex(data.pathUsage);
699     m_lineEditMessage->setText(data.message);
700 
701     if (mask & FileAndLinePart) {
702         m_pathChooserFileName->setFilePath(data.fileName);
703         m_lineEditLineNumber->setText(QString::number(data.lineNumber));
704     }
705 
706     if (mask & FunctionPart)
707         m_lineEditFunction->setText(data.functionName);
708 
709     if (mask & AddressPart) {
710         if (data.address) {
711             m_lineEditAddress->setText(QString("0x%1").arg(data.address, 0, 16));
712         } else {
713             m_lineEditAddress->clear();
714         }
715     }
716 
717     if (mask & ExpressionPart) {
718         if (!data.expression.isEmpty())
719             m_lineEditExpression->setText(data.expression);
720         else
721             m_lineEditExpression->clear();
722     }
723 
724     if (mask & ConditionPart)
725         m_lineEditCondition->setText(data.condition);
726     if (mask & IgnoreCountPart)
727         m_spinBoxIgnoreCount->setValue(data.ignoreCount);
728     if (mask & ThreadSpecPart)
729         m_lineEditThreadSpec->
730             setText(BreakHandler::displayFromThreadSpec(data.threadSpec));
731     if (mask & ModulePart)
732         m_lineEditModule->setText(data.module);
733 
734     if (mask & OneShotPart)
735         m_checkBoxOneShot->setChecked(data.oneShot);
736     if (mask & TracePointPart)
737         m_checkBoxTracepoint->setChecked(data.tracepoint);
738     if (mask & CommandPart)
739         m_textEditCommands->setPlainText(data.command);
740 }
741 
typeChanged(int)742 void BreakpointDialog::typeChanged(int)
743 {
744     BreakpointType previousType = m_previousType;
745     const BreakpointType newType = type();
746     m_previousType = newType;
747     // Save current state.
748     switch (previousType) {
749     case UnknownBreakpointType:
750     case LastBreakpointType:
751         break;
752     case BreakpointByFileAndLine:
753         getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
754         break;
755     case BreakpointByFunction:
756         getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
757         break;
758     case BreakpointAtThrow:
759     case BreakpointAtCatch:
760     case BreakpointAtMain:
761     case BreakpointAtFork:
762     case BreakpointAtExec:
763     //case BreakpointAtVFork:
764     case BreakpointAtSysCall:
765     case BreakpointAtJavaScriptThrow:
766         break;
767     case BreakpointByAddress:
768     case WatchpointAtAddress:
769         getParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
770         break;
771     case WatchpointAtExpression:
772         getParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
773         break;
774     case BreakpointOnQmlSignalEmit:
775         getParts(FunctionPart, &m_savedParameters);
776     }
777 
778     // Enable and set up new state from saved values.
779     switch (newType) {
780     case UnknownBreakpointType:
781     case LastBreakpointType:
782         break;
783     case BreakpointByFileAndLine:
784         setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
785         setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
786         clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
787         break;
788     case BreakpointByFunction:
789         setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
790         setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
791         clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
792         break;
793     case BreakpointAtThrow:
794     case BreakpointAtCatch:
795     case BreakpointAtFork:
796     case BreakpointAtExec:
797     //case BreakpointAtVFork:
798     case BreakpointAtSysCall:
799         clearOtherParts(AllConditionParts|ModulePart|TracePointPart|CommandPart);
800         setPartsEnabled(AllConditionParts|TracePointPart|CommandPart);
801         break;
802     case BreakpointAtJavaScriptThrow:
803         clearOtherParts(AllParts);
804         setPartsEnabled(0);
805         break;
806     case BreakpointAtMain:
807         m_lineEditFunction->setText("main"); // Just for display
808         clearOtherParts(0);
809         setPartsEnabled(0);
810         break;
811     case BreakpointByAddress:
812     case WatchpointAtAddress:
813         setParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
814         setPartsEnabled(AddressPart|AllConditionParts|TracePointPart|CommandPart);
815         clearOtherParts(AddressPart|AllConditionParts|TracePointPart|CommandPart);
816         break;
817     case WatchpointAtExpression:
818         setParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
819         setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
820         clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
821         break;
822     case BreakpointOnQmlSignalEmit:
823         setParts(FunctionPart, m_savedParameters);
824         setPartsEnabled(FunctionPart);
825         clearOtherParts(FunctionPart);
826     }
827 }
828 
showDialog(BreakpointParameters * data,BreakpointParts * parts)829 bool BreakpointDialog::showDialog(BreakpointParameters *data,
830     BreakpointParts *parts)
831 {
832     setParameters(*data);
833     if (exec() != QDialog::Accepted)
834         return false;
835 
836     // Check if changed.
837     const BreakpointParameters newParameters = parameters();
838     *parts = data->differencesTo(newParameters);
839     if (!*parts)
840         return false;
841 
842     *data = newParameters;
843     return true;
844 }
845 
846 // Dialog allowing changing properties of multiple breakpoints at a time.
847 class MultiBreakPointsDialog : public QDialog
848 {
849     Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
850 
851 public:
852     MultiBreakPointsDialog(unsigned int enabledParts, QWidget *parent);
853 
condition() const854     QString condition() const { return m_lineEditCondition->text(); }
ignoreCount() const855     int ignoreCount() const { return m_spinBoxIgnoreCount->value(); }
threadSpec() const856     int threadSpec() const
857        { return BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text()); }
858 
setCondition(const QString & c)859     void setCondition(const QString &c) { m_lineEditCondition->setText(c); }
setIgnoreCount(int i)860     void setIgnoreCount(int i) { m_spinBoxIgnoreCount->setValue(i); }
setThreadSpec(int t)861     void setThreadSpec(int t)
862         { return m_lineEditThreadSpec->setText(BreakHandler::displayFromThreadSpec(t)); }
863 
864 private:
865     QLineEdit *m_lineEditCondition;
866     QSpinBox *m_spinBoxIgnoreCount;
867     QLineEdit *m_lineEditThreadSpec;
868     QDialogButtonBox *m_buttonBox;
869 };
870 
MultiBreakPointsDialog(unsigned int enabledParts,QWidget * parent)871 MultiBreakPointsDialog::MultiBreakPointsDialog(unsigned int enabledParts, QWidget *parent) :
872     QDialog(parent)
873 {
874     setWindowTitle(tr("Edit Breakpoint Properties"));
875 
876     m_lineEditCondition = new QLineEdit(this);
877     m_spinBoxIgnoreCount = new QSpinBox(this);
878     m_spinBoxIgnoreCount->setMinimum(0);
879     m_spinBoxIgnoreCount->setMaximum(2147483647);
880     m_lineEditThreadSpec = new QLineEdit(this);
881 
882     m_buttonBox = new QDialogButtonBox(this);
883     m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
884 
885     auto formLayout = new QFormLayout;
886     if (enabledParts & ConditionPart)
887         formLayout->addRow(tr("&Condition:"), m_lineEditCondition);
888     formLayout->addRow(tr("&Ignore count:"), m_spinBoxIgnoreCount);
889     formLayout->addRow(tr("&Thread specification:"), m_lineEditThreadSpec);
890 
891     auto verticalLayout = new QVBoxLayout(this);
892     verticalLayout->addLayout(formLayout);
893     verticalLayout->addWidget(m_buttonBox);
894 
895     connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
896     connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
897 }
898 
BreakHandler(DebuggerEngine * engine)899 BreakHandler::BreakHandler(DebuggerEngine *engine)
900   : m_engine(engine)
901 {
902 #if USE_BREAK_MODEL_TEST
903     new ModelTest(this, 0);
904 #endif
905     setHeader({tr("Number"), tr("Function"), tr("File"), tr("Line"), tr("Address"),
906                tr("Condition"), tr("Ignore"), tr("Threads")});
907 }
908 
isLocatedAt(const FilePath & file,int line,const FilePath & markerFile) const909 bool BreakpointParameters::isLocatedAt(const FilePath &file, int line, const FilePath &markerFile) const
910 {
911     return lineNumber == line && (fileName == file || fileName == markerFile);
912 }
913 
isSimilarTo(const BreakpointParameters & params,const BreakpointParameters & needle)914 static bool isSimilarTo(const BreakpointParameters &params, const BreakpointParameters &needle)
915 {
916     // Clear miss.
917     if (needle.type != UnknownBreakpointType && params.type != UnknownBreakpointType
918             && params.type != needle.type)
919         return false;
920 
921     // Clear hit.
922     if (params.address && params.address == needle.address)
923         return true;
924 
925     // Clear hit.
926     if (params == needle)
927         return true;
928 
929     // At least at a position we were looking for.
930     // FIXME: breaks multiple breakpoints at the same location
931     if (!params.fileName.isEmpty()
932             && params.fileName == needle.fileName
933             && params.lineNumber == needle.lineNumber)
934         return true;
935 
936     return false;
937 }
938 
findBreakpointByResponseId(const QString & id) const939 Breakpoint BreakHandler::findBreakpointByResponseId(const QString &id) const
940 {
941     return findItemAtLevel<1>([id](const Breakpoint bp) {
942         return bp && bp->responseId() == id;
943     });
944 }
945 
findSubBreakpointByResponseId(const QString & id) const946 SubBreakpoint BreakHandler::findSubBreakpointByResponseId(const QString &id) const
947 {
948     return findItemAtLevel<2>([id](const SubBreakpoint sub) {
949         return sub && sub->responseId == id;
950     });
951 }
952 
data(const QModelIndex & idx,int role) const953 QVariant BreakHandler::data(const QModelIndex &idx, int role) const
954 {
955     if (role == BaseTreeView::ItemDelegateRole)
956         return QVariant::fromValue(new LeftElideDelegate);
957 
958     return BreakHandlerModel::data(idx, role);
959 }
960 
findWatchpoint(const BreakpointParameters & params) const961 Breakpoint BreakHandler::findWatchpoint(const BreakpointParameters &params) const
962 {
963     return findItemAtLevel<1>([params](const Breakpoint &bp) {
964         return bp->m_parameters.isWatchpoint()
965                 && bp->m_parameters.address == params.address
966                 && bp->m_parameters.size == params.size
967                 && bp->m_parameters.expression == params.expression
968                 && bp->m_parameters.bitpos == params.bitpos;
969     });
970 }
971 
findBreakpointByIndex(const QModelIndex & index) const972 Breakpoint BreakHandler::findBreakpointByIndex(const QModelIndex &index) const
973 {
974     return itemForIndexAtLevel<1>(index);
975 }
976 
findSubBreakpointByIndex(const QModelIndex & index) const977 SubBreakpoint BreakHandler::findSubBreakpointByIndex(const QModelIndex &index) const
978 {
979     return itemForIndexAtLevel<2>(index);
980 }
981 
findBreakpointByModelId(int modelId) const982 Breakpoint BreakHandler::findBreakpointByModelId(int modelId) const
983 {
984     return findItemAtLevel<1>([modelId](const Breakpoint &bp) {
985         QTC_ASSERT(bp, return false);
986         return bp->modelId() == modelId;
987     });
988 }
989 
findBreakpointsByIndex(const QList<QModelIndex> & list) const990 Breakpoints BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
991 {
992     QSet<Breakpoint> items;
993     for (const QModelIndex &index : list) {
994         if (Breakpoint bp = findBreakpointByIndex(index))
995             items.insert(bp);
996     }
997     return Utils::toList(items);
998 }
999 
findSubBreakpointsByIndex(const QList<QModelIndex> & list) const1000 SubBreakpoints BreakHandler::findSubBreakpointsByIndex(const QList<QModelIndex> &list) const
1001 {
1002     QSet<SubBreakpoint> items;
1003     for (const QModelIndex &index : list) {
1004         if (SubBreakpoint sbp = findSubBreakpointByIndex(index))
1005             items.insert(sbp);
1006     }
1007     return Utils::toList(items);
1008 
1009 }
1010 
displayFromThreadSpec(int spec)1011 QString BreakHandler::displayFromThreadSpec(int spec)
1012 {
1013     return spec == -1 ? BreakHandler::tr("(all)") : QString::number(spec);
1014 }
1015 
threadSpecFromDisplay(const QString & str)1016 int BreakHandler::threadSpecFromDisplay(const QString &str)
1017 {
1018     bool ok = false;
1019     int result = str.toInt(&ok);
1020     return ok ? result : -1;
1021 }
1022 
1023 const QString empty("-");
1024 
data(int column,int role) const1025 QVariant BreakpointItem::data(int column, int role) const
1026 {
1027     if (role == Qt::ForegroundRole) {
1028         static const QVariant gray(QColor(140, 140, 140));
1029         switch (m_state) {
1030             case BreakpointInsertionRequested:
1031             case BreakpointInsertionProceeding:
1032             case BreakpointUpdateRequested:
1033             case BreakpointUpdateProceeding:
1034             case BreakpointRemoveRequested:
1035             case BreakpointRemoveProceeding:
1036                 return gray;
1037             case BreakpointInserted:
1038             case BreakpointNew:
1039             case BreakpointDead:
1040                 break;
1041         };
1042     }
1043 
1044     switch (column) {
1045         case BreakpointNumberColumn:
1046             if (role == Qt::DisplayRole)
1047                 return m_displayName.isEmpty() ? m_responseId : m_displayName;
1048             if (role == Qt::DecorationRole)
1049                 return icon(m_needsLocationMarker);
1050             break;
1051         case BreakpointFunctionColumn:
1052             if (role == Qt::DisplayRole) {
1053                 if (!m_parameters.functionName.isEmpty())
1054                     return simplifyType(m_parameters.functionName);
1055 
1056                 if (m_parameters.type == BreakpointAtMain
1057                         || m_parameters.type == BreakpointAtThrow
1058                         || m_parameters.type == BreakpointAtCatch
1059                         || m_parameters.type == BreakpointAtFork
1060                         || m_parameters.type == BreakpointAtExec
1061                         //|| m_response.type == BreakpointAtVFork
1062                         || m_parameters.type == BreakpointAtSysCall)
1063                     return typeToString(m_parameters.type);
1064 
1065                 if (m_parameters.type == WatchpointAtAddress)
1066                     return BreakHandler::tr("Data at 0x%1").arg(m_parameters.address, 0, 16);
1067 
1068                 if (m_parameters.type == WatchpointAtExpression)
1069                     return BreakHandler::tr("Data at %1").arg(m_parameters.expression);
1070 
1071                 return empty;
1072             }
1073             break;
1074         case BreakpointFileColumn:
1075             if (role == Qt::DisplayRole)
1076                 return markerFileName().toUserOutput();
1077             break;
1078         case BreakpointLineColumn:
1079             if (role == Qt::DisplayRole) {
1080                 const int line = markerLineNumber();
1081                 if (line > 0)
1082                     return line;
1083                 return empty;
1084             }
1085             if (role == Qt::UserRole + 1)
1086                 return m_parameters.lineNumber;
1087             break;
1088         case BreakpointAddressColumn:
1089             if (role == Qt::DisplayRole) {
1090                 if (m_parameters.address)
1091                     return QString("0x%1").arg(m_parameters.address, 0, 16);
1092                 return QVariant();
1093             }
1094             break;
1095         case BreakpointConditionColumn:
1096             if (role == Qt::DisplayRole)
1097                 return m_parameters.condition;
1098             if (role == Qt::ToolTipRole)
1099                 return BreakHandler::tr("Breakpoint will only be hit if this condition is met.");
1100             if (role == Qt::UserRole + 1)
1101                 return m_parameters.condition;
1102             break;
1103         case BreakpointIgnoreColumn:
1104             if (role == Qt::DisplayRole) {
1105                 const int ignoreCount = m_parameters.ignoreCount;
1106                 return ignoreCount ? QVariant(ignoreCount) : QVariant(QString());
1107             }
1108             if (role == Qt::ToolTipRole)
1109                 return BreakHandler::tr("Breakpoint will only be hit after being ignored so many times.");
1110             if (role == Qt::UserRole + 1)
1111                 return m_parameters.ignoreCount;
1112             break;
1113         case BreakpointThreadsColumn:
1114             if (role == Qt::DisplayRole)
1115                 return BreakHandler::displayFromThreadSpec(m_parameters.threadSpec);
1116             if (role == Qt::ToolTipRole)
1117                 return BreakHandler::tr("Breakpoint will only be hit in the specified thread(s).");
1118             if (role == Qt::UserRole + 1)
1119                 return BreakHandler::displayFromThreadSpec(m_parameters.threadSpec);
1120             break;
1121     }
1122 
1123     if (role == Qt::ToolTipRole && debuggerSettings()->useToolTipsInBreakpointsView.value())
1124         return toolTip();
1125 
1126     return QVariant();
1127 }
1128 
addToCommand(DebuggerCommand * cmd) const1129 void BreakpointItem::addToCommand(DebuggerCommand *cmd) const
1130 {
1131     QTC_ASSERT(m_globalBreakpoint, return);
1132     const BreakpointParameters &requested = requestedParameters();
1133     cmd->arg("modelid", modelId());
1134     cmd->arg("id", m_responseId);
1135     cmd->arg("type", requested.type);
1136     cmd->arg("ignorecount", requested.ignoreCount);
1137     cmd->arg("condition", toHex(requested.condition));
1138     cmd->arg("command", toHex(requested.command));
1139     cmd->arg("function", requested.functionName);
1140     cmd->arg("oneshot", requested.oneShot);
1141     cmd->arg("enabled", requested.enabled);
1142     cmd->arg("file", requested.fileName);
1143     cmd->arg("line", requested.lineNumber);
1144     cmd->arg("address", requested.address);
1145     cmd->arg("expression", requested.expression);
1146 }
1147 
updateFromGdbOutput(const GdbMi & bkpt)1148 void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt)
1149 {
1150     m_parameters.updateFromGdbOutput(bkpt);
1151     adjustMarker();
1152 }
1153 
modelId() const1154 int BreakpointItem::modelId() const
1155 {
1156     return m_globalBreakpoint ? m_globalBreakpoint->modelId() : 0;
1157 }
1158 
setPending(bool pending)1159 void BreakpointItem::setPending(bool pending)
1160 {
1161     m_parameters.pending = pending;
1162     adjustMarker();
1163 }
1164 
removeAlienBreakpoint(const QString & rid)1165 void BreakHandler::removeAlienBreakpoint(const QString &rid)
1166 {
1167     Breakpoint bp = findBreakpointByResponseId(rid);
1168     destroyItem(bp);
1169 }
1170 
requestBreakpointInsertion(const Breakpoint & bp)1171 void BreakHandler::requestBreakpointInsertion(const Breakpoint &bp)
1172 {
1173     bp->gotoState(BreakpointInsertionRequested, BreakpointNew);
1174     m_engine->insertBreakpoint(bp);
1175 }
1176 
requestBreakpointUpdate(const Breakpoint & bp)1177 void BreakHandler::requestBreakpointUpdate(const Breakpoint &bp)
1178 {
1179     bp->gotoState(BreakpointUpdateRequested, BreakpointInserted);
1180     m_engine->updateBreakpoint(bp);
1181 }
1182 
requestBreakpointRemoval(const Breakpoint & bp)1183 void BreakHandler::requestBreakpointRemoval(const Breakpoint &bp)
1184 {
1185     bp->gotoState(BreakpointRemoveRequested, BreakpointInserted);
1186     m_engine->removeBreakpoint(bp);
1187 }
1188 
requestBreakpointEnabling(const Breakpoint & bp,bool enabled)1189 void BreakHandler::requestBreakpointEnabling(const Breakpoint &bp, bool enabled)
1190 {
1191     if (bp->m_parameters.enabled != enabled) {
1192         bp->update();
1193         requestBreakpointUpdate(bp);
1194     }
1195 }
1196 
requestSubBreakpointEnabling(const SubBreakpoint & sbp,bool enabled)1197 void BreakHandler::requestSubBreakpointEnabling(const SubBreakpoint &sbp, bool enabled)
1198 {
1199     if (sbp->params.enabled != enabled) {
1200         sbp->params.enabled = enabled;
1201         sbp->breakpoint()->update();
1202         QTimer::singleShot(0, m_engine, [this, sbp, enabled] {
1203             m_engine->enableSubBreakpoint(sbp, enabled);
1204         });
1205     }
1206 }
1207 
setMarkerFileAndLine(const FilePath & fileName,int lineNumber)1208 void BreakpointItem::setMarkerFileAndLine(const FilePath &fileName, int lineNumber)
1209 {
1210     if (m_parameters.fileName == fileName && m_parameters.lineNumber == lineNumber)
1211         return;
1212     m_parameters.fileName = fileName;
1213     m_parameters.lineNumber = lineNumber;
1214     destroyMarker();
1215     updateMarker();
1216     update();
1217 }
1218 
isAllowedTransition(BreakpointState from,BreakpointState to)1219 static bool isAllowedTransition(BreakpointState from, BreakpointState to)
1220 {
1221     switch (from) {
1222     case BreakpointNew:
1223         return to == BreakpointInsertionRequested
1224             || to == BreakpointDead;
1225     case BreakpointInsertionRequested:
1226         return to == BreakpointInsertionProceeding;
1227     case BreakpointInsertionProceeding:
1228         return to == BreakpointInserted
1229             || to == BreakpointDead
1230             || to == BreakpointUpdateRequested
1231             || to == BreakpointRemoveRequested;
1232     case BreakpointUpdateRequested:
1233         return to == BreakpointUpdateProceeding;
1234     case BreakpointUpdateProceeding:
1235         return to == BreakpointInserted
1236             || to == BreakpointDead;
1237     case BreakpointInserted:
1238         return to == BreakpointUpdateRequested
1239             || to == BreakpointRemoveRequested;
1240     case BreakpointRemoveRequested:
1241         return to == BreakpointRemoveProceeding;
1242     case BreakpointRemoveProceeding:
1243         return to == BreakpointDead;
1244     case BreakpointDead:
1245         return false;
1246     }
1247     qDebug() << "UNKNOWN BREAKPOINT STATE:" << from;
1248     return false;
1249 }
1250 
gotoState(BreakpointState target,BreakpointState assumedCurrent)1251 void BreakpointItem::gotoState(BreakpointState target, BreakpointState assumedCurrent)
1252 {
1253     QTC_ASSERT(m_state == assumedCurrent, qDebug() << m_state);
1254     setState(target);
1255 }
1256 
setNeedsLocationMarker(bool needsLocationMarker)1257 void BreakpointItem::setNeedsLocationMarker(bool needsLocationMarker)
1258 {
1259     if (m_needsLocationMarker == needsLocationMarker)
1260         return;
1261     m_needsLocationMarker = needsLocationMarker;
1262     update();
1263 }
1264 
updateDisassemblerMarker(const Breakpoint & bp)1265 void BreakHandler::updateDisassemblerMarker(const Breakpoint &bp)
1266 {
1267     return m_engine->disassemblerAgent()->updateBreakpointMarker(bp);
1268 }
1269 
removeDisassemblerMarker(const Breakpoint & bp)1270 void BreakHandler::removeDisassemblerMarker(const Breakpoint &bp)
1271 {
1272     m_engine->disassemblerAgent()->removeBreakpointMarker(bp);
1273     bp->destroyMarker();
1274     if (GlobalBreakpoint gbp = bp->globalBreakpoint())
1275         gbp->updateMarker();
1276 }
1277 
matches(const Location & loc,const BreakpointParameters & bp)1278 static bool matches(const Location &loc, const BreakpointParameters &bp)
1279 {
1280     if (loc.fileName() == bp.fileName && loc.lineNumber() == bp.lineNumber && bp.lineNumber > 0)
1281         return true;
1282     if (loc.address() == bp.address && bp.address > 0)
1283         return true;
1284     return false;
1285 }
1286 
setLocation(const Location & loc)1287 void BreakHandler::setLocation(const Location &loc)
1288 {
1289     forItemsAtLevel<1>([loc](Breakpoint bp) {
1290         bool needsMarker = matches(loc, bp->parameters());
1291         if (GlobalBreakpoint gpb = bp->globalBreakpoint())
1292             needsMarker = needsMarker || matches(loc, gpb->requestedParameters());
1293         bp->setNeedsLocationMarker(needsMarker);
1294     });
1295 }
1296 
resetLocation()1297 void BreakHandler::resetLocation()
1298 {
1299     forItemsAtLevel<1>([](Breakpoint bp) { bp->setNeedsLocationMarker(false); });
1300 }
1301 
setState(BreakpointState state)1302 void BreakpointItem::setState(BreakpointState state)
1303 {
1304     //qDebug() << "BREAKPOINT STATE TRANSITION, ID: " << m_id
1305     //    << " FROM: " << state << " TO: " << state;
1306     if (!isAllowedTransition(m_state, state)) {
1307         qDebug() << "UNEXPECTED BREAKPOINT STATE TRANSITION" << m_state << state;
1308         QTC_CHECK(false);
1309     }
1310 
1311     if (m_state == state) {
1312         qDebug() << "STATE UNCHANGED: " << responseId() << m_state;
1313         return;
1314     }
1315 
1316     m_state = state;
1317 
1318     // FIXME: updateMarker() should recognize the need for icon changes.
1319     if (state == BreakpointInserted || state == BreakpointInsertionRequested) {
1320         destroyMarker();
1321         updateMarker();
1322     }
1323     update();
1324 }
1325 
globalBreakpoint() const1326 const GlobalBreakpoint BreakpointItem::globalBreakpoint() const
1327 {
1328     return m_globalBreakpoint;
1329 }
1330 
setParameters(const BreakpointParameters & value)1331 void BreakpointItem::setParameters(const BreakpointParameters &value)
1332 {
1333     m_parameters = value;
1334     adjustMarker();
1335 }
1336 
setEnabled(bool on)1337 void BreakpointItem::setEnabled(bool on)
1338 {
1339     m_parameters.enabled = on;
1340     adjustMarker();
1341 }
1342 
notifyBreakpointInsertProceeding(const Breakpoint & bp)1343 void DebuggerEngine::notifyBreakpointInsertProceeding(const Breakpoint &bp)
1344 {
1345     QTC_ASSERT(bp, return);
1346     bp->gotoState(BreakpointInsertionProceeding, BreakpointInsertionRequested);
1347 }
1348 
notifyBreakpointInsertOk(const Breakpoint & bp)1349 void DebuggerEngine::notifyBreakpointInsertOk(const Breakpoint &bp)
1350 {
1351     QTC_ASSERT(bp, return);
1352     bp->adjustMarker();
1353     bp->gotoState(BreakpointInserted, BreakpointInsertionProceeding);
1354     breakHandler()->updateDisassemblerMarker(bp);
1355     bp->updateMarker();
1356 }
1357 
notifyBreakpointInsertFailed(const Breakpoint & bp)1358 void DebuggerEngine::notifyBreakpointInsertFailed(const Breakpoint &bp)
1359 {
1360     QTC_ASSERT(bp, return);
1361     GlobalBreakpoint gbp = bp->globalBreakpoint();
1362     bp->gotoState(BreakpointDead, BreakpointInsertionProceeding);
1363     breakHandler()->removeDisassemblerMarker(bp);
1364     breakHandler()->destroyItem(bp);
1365     QTC_ASSERT(gbp, return);
1366     gbp->updateMarker();
1367 }
1368 
notifyBreakpointRemoveProceeding(const Breakpoint & bp)1369 void DebuggerEngine::notifyBreakpointRemoveProceeding(const Breakpoint &bp)
1370 {
1371     QTC_ASSERT(bp, return);
1372     bp->gotoState(BreakpointRemoveProceeding, BreakpointRemoveRequested);
1373 }
1374 
notifyBreakpointRemoveOk(const Breakpoint & bp)1375 void DebuggerEngine::notifyBreakpointRemoveOk(const Breakpoint &bp)
1376 {
1377     QTC_ASSERT(bp, return);
1378     QTC_ASSERT(bp->state() == BreakpointRemoveProceeding, qDebug() << bp->state());
1379     breakHandler()->removeDisassemblerMarker(bp);
1380     breakHandler()->destroyItem(bp);
1381 }
1382 
notifyBreakpointRemoveFailed(const Breakpoint & bp)1383 void DebuggerEngine::notifyBreakpointRemoveFailed(const Breakpoint &bp)
1384 {
1385     QTC_ASSERT(bp, return);
1386     QTC_ASSERT(bp->m_state == BreakpointRemoveProceeding, qDebug() << bp->m_state);
1387     breakHandler()->removeDisassemblerMarker(bp);
1388     breakHandler()->destroyItem(bp);
1389 }
1390 
notifyBreakpointChangeProceeding(const Breakpoint & bp)1391 void DebuggerEngine::notifyBreakpointChangeProceeding(const Breakpoint &bp)
1392 {
1393     bp->gotoState(BreakpointUpdateProceeding, BreakpointUpdateRequested);
1394 }
1395 
notifyBreakpointChangeOk(const Breakpoint & bp)1396 void DebuggerEngine::notifyBreakpointChangeOk(const Breakpoint &bp)
1397 {
1398     bp->gotoState(BreakpointInserted, BreakpointUpdateProceeding);
1399 }
1400 
notifyBreakpointChangeFailed(const Breakpoint & bp)1401 void DebuggerEngine::notifyBreakpointChangeFailed(const Breakpoint &bp)
1402 {
1403     bp->gotoState(BreakpointDead, BreakpointUpdateProceeding);
1404 }
1405 
notifyBreakpointNeedsReinsertion(const Breakpoint & bp)1406 void DebuggerEngine::notifyBreakpointNeedsReinsertion(const Breakpoint &bp)
1407 {
1408     QTC_ASSERT(bp, return);
1409     QTC_ASSERT(bp->m_state == BreakpointUpdateProceeding, qDebug() << bp->m_state);
1410     bp->m_state = BreakpointInsertionRequested;
1411 }
1412 
handleAlienBreakpoint(const QString & responseId,const BreakpointParameters & params)1413 void BreakHandler::handleAlienBreakpoint(const QString &responseId, const BreakpointParameters &params)
1414 {
1415     // Search a breakpoint we might refer to.
1416     Breakpoint bp = findItemAtLevel<1>([params, responseId](const Breakpoint &bp) {
1417         if (bp && !bp->responseId().isEmpty() && bp->responseId() == responseId)
1418             return true;
1419         return bp && isSimilarTo(bp->m_parameters, params);
1420     });
1421 
1422     if (bp) {
1423         // FIXME: x.y looks rather gdb specific.
1424         if (bp->responseId().contains('.')) {
1425             SubBreakpoint loc = bp->findOrCreateSubBreakpoint(bp->responseId());
1426             QTC_ASSERT(loc, return);
1427             loc->setParameters(params);
1428         } else {
1429             bp->setParameters(params);
1430         }
1431     } else {
1432         bp = new BreakpointItem(nullptr);
1433         bp->m_responseId = responseId;
1434         bp->m_parameters = params;
1435         bp->m_state = BreakpointInserted;
1436         bp->updateMarker();
1437         rootItem()->appendChild(bp);
1438         // This has no global breakpoint, so there's nothing to update here.
1439     }
1440 }
1441 
findOrCreateSubBreakpoint(const QString & responseId)1442 SubBreakpoint BreakpointItem::findOrCreateSubBreakpoint(const QString &responseId)
1443 {
1444     SubBreakpoint loc = findFirstLevelChild([&](const SubBreakpoint &l) {
1445         return l->responseId == responseId;
1446     });
1447     if (loc) {
1448         // This modifies an existing sub-breakpoint.
1449         loc->update();
1450     } else {
1451         // This is a new sub-breakpoint.
1452         loc = new SubBreakpointItem;
1453         loc->responseId = responseId;
1454         appendChild(loc);
1455         expand();
1456     }
1457     return loc;
1458 }
1459 
tryClaimBreakpoint(const GlobalBreakpoint & gbp)1460 bool BreakHandler::tryClaimBreakpoint(const GlobalBreakpoint &gbp)
1461 {
1462     const Breakpoints bps = breakpoints();
1463     if (Utils::anyOf(bps, [gbp](const Breakpoint &bp) { return bp->globalBreakpoint() == gbp; }))
1464         return false;
1465 
1466     if (!m_engine->acceptsBreakpoint(gbp->requestedParameters())) {
1467         m_engine->showMessage(QString("BREAKPOINT %1 IS NOT ACCEPTED BY ENGINE %2")
1468                     .arg(gbp->displayName()).arg(objectName()));
1469         return false;
1470     }
1471 
1472     m_engine->showMessage(QString("TAKING OWNERSHIP OF BREAKPOINT %1").arg(gbp->displayName()));
1473 
1474     Breakpoint bp(new BreakpointItem(gbp));
1475     rootItem()->appendChild(bp);
1476 
1477     gbp->updateMarker();
1478     requestBreakpointInsertion(bp);
1479 
1480     return true;
1481 }
1482 
gotoLocation(const Breakpoint & bp) const1483 void BreakHandler::gotoLocation(const Breakpoint &bp) const
1484 {
1485     QTC_ASSERT(bp, return);
1486     QTC_ASSERT(m_engine, return);
1487     if (bp->m_parameters.type == BreakpointByAddress) {
1488         m_engine->gotoLocation(bp->m_parameters.address);
1489     } else {
1490         // Don't use gotoLocation unconditionally as this ends up in
1491         // disassembly if OperateByInstruction is on. But fallback
1492         // to disassembly if we can't open the file.
1493         if (IEditor *editor = EditorManager::openEditor(bp->markerFileName()))
1494             editor->gotoLine(bp->markerLineNumber(), 0);
1495         else
1496             m_engine->openDisassemblerView(Location(bp->m_parameters.address));
1497     }
1498 }
1499 
breakpoints() const1500 const Breakpoints BreakHandler::breakpoints() const
1501 {
1502     QList<Breakpoint> items;
1503     forItemsAtLevel<1>([&items](Breakpoint bp) { if (bp) items.append(bp); });
1504     return items;
1505 }
1506 
adjustMarker()1507 void BreakpointItem::adjustMarker()
1508 {
1509     destroyMarker();
1510     updateMarker();
1511 }
1512 
deleteBreakpoint()1513 void BreakpointItem::deleteBreakpoint()
1514 {
1515     QTC_ASSERT(!globalBreakpoint(), return); // Use deleteBreakpoint(GlobalBreakpoint gbp) instead.
1516 
1517     for (QPointer<DebuggerEngine> engine : EngineManager::engines())
1518         engine->breakHandler()->requestBreakpointRemoval(this);
1519 }
1520 
deleteGlobalOrThisBreakpoint()1521 void BreakpointItem::deleteGlobalOrThisBreakpoint()
1522 {
1523     if (GlobalBreakpoint gbp = globalBreakpoint()) {
1524         gbp->deleteBreakpoint();
1525     } else {
1526         deleteBreakpoint();
1527     }
1528 }
1529 
setData(const QModelIndex & idx,const QVariant & value,int role)1530 bool BreakHandler::setData(const QModelIndex &idx, const QVariant &value, int role)
1531 {
1532     if (role == BaseTreeView::ItemActivatedRole) {
1533         if (Breakpoint bp = findBreakpointByIndex(idx))
1534             gotoLocation(bp);
1535         return true;
1536     }
1537 
1538     if (role == BaseTreeView::ItemViewEventRole) {
1539         ItemViewEvent ev = value.value<ItemViewEvent>();
1540 
1541         if (ev.as<QContextMenuEvent>())
1542             return contextMenuEvent(ev);
1543 
1544         if (auto kev = ev.as<QKeyEvent>(QEvent::KeyPress)) {
1545             if (kev->key() == Qt::Key_Delete || kev->key() == Qt::Key_Backspace) {
1546                 QModelIndexList si = ev.currentOrSelectedRows();
1547                 const Breakpoints bps = findBreakpointsByIndex(si);
1548                 for (Breakpoint bp : bps) {
1549                     if (GlobalBreakpoint gbp = bp->globalBreakpoint())
1550                         gbp->deleteBreakpoint();
1551                     else
1552                         bp->deleteBreakpoint();
1553                 }
1554 //                int row = qMin(rowCount() - ids.size() - 1, idx.row());
1555 //                setCurrentIndex(index(row, 0));   FIXME
1556                 return true;
1557             }
1558             if (kev->key() == Qt::Key_Space) {
1559                 const QModelIndexList selectedIds = ev.selectedRows();
1560                 if (!selectedIds.isEmpty()) {
1561                     const Breakpoints bps = findBreakpointsByIndex(selectedIds);
1562                     const SubBreakpoints sbps = findSubBreakpointsByIndex(selectedIds);
1563                     const bool isEnabled = (bps.isEmpty() && sbps.isEmpty())
1564                             || (!bps.isEmpty() && bps.at(0)->isEnabled())
1565                             || (!sbps.isEmpty() && sbps.at(0)->params.enabled);
1566                     for (Breakpoint bp : bps) {
1567                         if (GlobalBreakpoint gbp = bp->globalBreakpoint())
1568                             gbp->setEnabled(!isEnabled, false);
1569                         requestBreakpointEnabling(bp, !isEnabled);
1570                     }
1571                     for (SubBreakpoint sbp : sbps)
1572                         requestSubBreakpointEnabling(sbp, !isEnabled);
1573                     return true;
1574                 }
1575             }
1576         }
1577 
1578         if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
1579             if (Breakpoint bp = findBreakpointByIndex(idx)) {
1580                 if (idx.column() >= BreakpointAddressColumn)
1581                     editBreakpoints({bp}, ev.view());
1582                 else
1583                     gotoLocation(bp);
1584             } else if (SubBreakpoint loc = itemForIndexAtLevel<2>(idx)) {
1585                 gotoLocation(loc->breakpoint());
1586             } else {
1587                 BreakpointManager::executeAddBreakpointDialog();
1588             }
1589             return true;
1590         }
1591     }
1592 
1593     return false;
1594 }
1595 
contextMenuEvent(const ItemViewEvent & ev)1596 bool BreakHandler::contextMenuEvent(const ItemViewEvent &ev)
1597 {
1598     const QModelIndexList selectedIndices = ev.selectedRows();
1599 
1600     const Breakpoints selectedBreakpoints = findBreakpointsByIndex(selectedIndices);
1601     const bool breakpointsEnabled = selectedBreakpoints.isEmpty() || selectedBreakpoints.at(0)->isEnabled();
1602 
1603     QList<SubBreakpointItem *> selectedLocations;
1604     bool handlesIndividualLocations = false;
1605     for (const QModelIndex &index : selectedIndices) {
1606         if (SubBreakpointItem *location = itemForIndexAtLevel<2>(index)) {
1607             if (selectedLocations.contains(location))
1608                 continue;
1609             selectedLocations.append(location);
1610             if (m_engine->hasCapability(BreakIndividualLocationsCapability))
1611                 handlesIndividualLocations = true;
1612         }
1613     }
1614     const bool locationsEnabled = selectedLocations.isEmpty() || selectedLocations.at(0)->params.enabled;
1615 
1616     auto menu = new QMenu;
1617 
1618     addAction(menu, tr("Add Breakpoint..."), true, &BreakpointManager::executeAddBreakpointDialog);
1619 
1620     addAction(menu, tr("Delete Selected Breakpoints"),
1621               !selectedBreakpoints.isEmpty(),
1622               [selectedBreakpoints] {
1623                 for (Breakpoint bp : selectedBreakpoints) {
1624                     if (GlobalBreakpoint gbp = bp->globalBreakpoint()) {
1625                         gbp->deleteBreakpoint();
1626                     } else {
1627                         bp->deleteBreakpoint();
1628                     }
1629                 }
1630              });
1631 
1632     addAction(menu, tr("Edit Selected Breakpoints..."),
1633               !selectedBreakpoints.isEmpty(),
1634               [this, selectedBreakpoints, ev] { editBreakpoints(selectedBreakpoints, ev.view()); });
1635 
1636 
1637     // FIXME BP: m_engine->threadsHandler()->currentThreadId();
1638     // int threadId = 0;
1639     // addAction(menu,
1640     //           threadId == -1 ? tr("Associate Breakpoint with All Threads")
1641     //                          : tr("Associate Breakpoint with Thread %1").arg(threadId),
1642     //           !selectedItems.isEmpty(),
1643     //           [this, selectedItems, threadId] {
1644     //                 for (Breakpoint bp : selectedItems)
1645     //                     bp.setThreadSpec(threadId);
1646     //           });
1647 
1648     addAction(menu,
1649               selectedBreakpoints.size() > 1
1650                   ? breakpointsEnabled ? tr("Disable Selected Breakpoints") : tr("Enable Selected Breakpoints")
1651                   : breakpointsEnabled ? tr("Disable Breakpoint") : tr("Enable Breakpoint"),
1652               !selectedBreakpoints.isEmpty(),
1653               [this, selectedBreakpoints, breakpointsEnabled] {
1654                     for (Breakpoint bp : selectedBreakpoints) {
1655                         if (GlobalBreakpoint gbp = bp->globalBreakpoint())
1656                             gbp->setEnabled(!breakpointsEnabled, false);
1657                         requestBreakpointEnabling(bp, !breakpointsEnabled);
1658                     }
1659               }
1660     );
1661 
1662     addAction(menu,
1663               selectedLocations.size() > 1
1664                   ? locationsEnabled ? tr("Disable Selected Locations") : tr("Enable Selected Locations")
1665                   : locationsEnabled ? tr("Disable Location") : tr("Enable Location"),
1666               !selectedLocations.isEmpty() && handlesIndividualLocations,
1667               [this, selectedLocations, locationsEnabled] {
1668                    for (SubBreakpointItem * const sbp : selectedLocations)
1669                        requestSubBreakpointEnabling(QPointer(sbp), !locationsEnabled);
1670               }
1671     );
1672 
1673     menu->addSeparator();
1674 
1675     addAction(menu, tr("Delete All Breakpoints"),
1676               rowCount() > 0,
1677               &BreakpointManager::executeDeleteAllBreakpointsDialog);
1678 
1679     // Delete by file: Find indices of breakpoints of the same file.
1680     QList<Breakpoint> breakpointsInFile;
1681     QString file;
1682     if (Breakpoint bp = itemForIndexAtLevel<1>(ev.sourceModelIndex())) {
1683         const QModelIndex index = ev.sourceModelIndex().sibling(ev.sourceModelIndex().row(), BreakpointFileColumn);
1684         if (!file.isEmpty()) {
1685             for (int i = 0; i != rowCount(); ++i)
1686                 if (index.data().toString() == file)
1687                     breakpointsInFile.append(findBreakpointByIndex(index));
1688         }
1689     }
1690     addAction(menu, tr("Delete Breakpoints of \"%1\"").arg(file),
1691               tr("Delete Breakpoints of File"),
1692               breakpointsInFile.size() > 1,
1693               [breakpointsInFile] {
1694                 for (Breakpoint bp : breakpointsInFile)
1695                     bp->deleteGlobalOrThisBreakpoint();
1696               });
1697 
1698     menu->addSeparator();
1699 
1700     menu->addAction(debuggerSettings()->useToolTipsInBreakpointsView.action());
1701     menu->addAction(debuggerSettings()->settingsDialog.action());
1702 
1703     menu->popup(ev.globalPos());
1704 
1705     return true;
1706 }
1707 
removeBreakpoint(const Breakpoint & bp)1708 void BreakHandler::removeBreakpoint(const Breakpoint &bp)
1709 {
1710     QTC_ASSERT(bp, return);
1711     switch (bp->m_state) {
1712     case BreakpointRemoveRequested:
1713         break;
1714     case BreakpointInserted:
1715     case BreakpointInsertionProceeding:
1716         requestBreakpointRemoval(bp);
1717         break;
1718     case BreakpointNew:
1719         bp->setState(BreakpointDead);
1720         bp->destroyMarker();
1721         destroyItem(bp);
1722         break;
1723     default:
1724         qWarning("Warning: Cannot remove breakpoint %s in state '%s'.",
1725                qPrintable(bp->responseId()), qPrintable(stateToString(bp->m_state)));
1726         bp->m_state = BreakpointRemoveRequested;
1727         break;
1728     }
1729 }
1730 
engineBreakpointCapabilities(DebuggerEngine * engine)1731 static unsigned int engineBreakpointCapabilities(DebuggerEngine *engine)
1732 {
1733     unsigned int enabledParts = ~0;
1734     if (!engine->hasCapability(BreakConditionCapability))
1735         enabledParts &= ~ConditionPart;
1736     if (!engine->hasCapability(BreakModuleCapability))
1737         enabledParts &= ~ModulePart;
1738     if (!engine->hasCapability(TracePointCapability))
1739         enabledParts &= ~TracePointPart;
1740     return enabledParts;
1741 }
1742 
editBreakpoint(const Breakpoint & bp,QWidget * parent)1743 void BreakHandler::editBreakpoint(const Breakpoint &bp, QWidget *parent)
1744 {
1745     QTC_ASSERT(bp, return);
1746     BreakpointParameters params = bp->requestedParameters();
1747     BreakpointParts parts = NoParts;
1748 
1749     BreakpointDialog dialog(engineBreakpointCapabilities(m_engine), parent);
1750     if (!dialog.showDialog(&params, &parts))
1751         return;
1752 
1753     if (params != bp->requestedParameters()) {
1754         if (GlobalBreakpoint gbp = bp->globalBreakpoint()) {
1755             gbp->setParameters(params);
1756         } else {
1757             bp->setParameters(params);
1758         }
1759         updateDisassemblerMarker(bp);
1760         bp->updateMarker();
1761         bp->update();
1762         if (bp->needsChange() && bp->m_state != BreakpointNew)
1763             requestBreakpointUpdate(bp);
1764     }
1765 }
1766 
editBreakpoints(const Breakpoints & bps,QWidget * parent)1767 void BreakHandler::editBreakpoints(const Breakpoints &bps, QWidget *parent)
1768 {
1769     QTC_ASSERT(!bps.isEmpty(), return);
1770 
1771     Breakpoint bp = bps.at(0);
1772 
1773     if (bps.size() == 1) {
1774         editBreakpoint(bp, parent);
1775         return;
1776     }
1777 
1778     // This allows to change properties of multiple breakpoints at a time.
1779     QTC_ASSERT(bp, return);
1780 
1781     MultiBreakPointsDialog dialog(engineBreakpointCapabilities(m_engine), parent);
1782     dialog.setCondition(bp->condition());
1783     dialog.setIgnoreCount(bp->ignoreCount());
1784     dialog.setThreadSpec(bp->threadSpec());
1785 
1786     if (dialog.exec() == QDialog::Rejected)
1787         return;
1788 
1789     const QString newCondition = dialog.condition();
1790     const int newIgnoreCount = dialog.ignoreCount();
1791     const int newThreadSpec = dialog.threadSpec();
1792 
1793     for (Breakpoint bp : bps) {
1794         if (bp) {
1795             if (GlobalBreakpoint gbp = bp->globalBreakpoint()) {
1796                 BreakpointParameters params = bp->requestedParameters();
1797                 params.condition = newCondition;
1798                 params.ignoreCount = newIgnoreCount;
1799                 params.threadSpec = newThreadSpec;
1800                 gbp->setParameters(params);
1801             } else {
1802                 bp->m_parameters.condition = newCondition;
1803                 bp->m_parameters.ignoreCount = newIgnoreCount;
1804                 bp->m_parameters.threadSpec = newThreadSpec;
1805             }
1806 
1807             if (bp->m_state != BreakpointNew)
1808                 requestBreakpointUpdate(bp);
1809         }
1810     }
1811 }
1812 
1813 //////////////////////////////////////////////////////////////////
1814 //
1815 // Storage
1816 //
1817 //////////////////////////////////////////////////////////////////
1818 
BreakpointItem(const GlobalBreakpoint & gbp)1819 BreakpointItem::BreakpointItem(const GlobalBreakpoint &gbp)
1820     : m_globalBreakpoint(gbp)
1821 {
1822 }
1823 
~BreakpointItem()1824 BreakpointItem::~BreakpointItem()
1825 {
1826     delete m_marker;
1827 }
1828 
destroyMarker()1829 void BreakpointItem::destroyMarker()
1830 {
1831     if (m_marker) {
1832         BreakpointMarker *m = m_marker;
1833         m_marker = nullptr;
1834         delete m;
1835     }
1836 }
1837 
markerFileName() const1838 FilePath BreakpointItem::markerFileName() const
1839 {
1840     // Some heuristics to find a "good" file name.
1841     if (m_parameters.fileName.exists())
1842         return m_parameters.fileName.absoluteFilePath();
1843 
1844     const FilePath origFileName = requestedParameters().fileName;
1845     if (m_parameters.fileName.endsWith(origFileName.fileName()))
1846         return m_parameters.fileName;
1847     if (origFileName.endsWith(m_parameters.fileName.fileName()))
1848         return origFileName;
1849 
1850     return m_parameters.fileName.toString().size() > origFileName.toString().size()
1851                ? m_parameters.fileName
1852                : origFileName;
1853 }
1854 
markerLineNumber() const1855 int BreakpointItem::markerLineNumber() const
1856 {
1857     if (m_parameters.lineNumber > 0)
1858         return m_parameters.lineNumber;
1859     return requestedParameters().lineNumber;
1860 }
1861 
requestedParameters() const1862 const BreakpointParameters &BreakpointItem::requestedParameters() const
1863 {
1864     return m_globalBreakpoint ? m_globalBreakpoint->requestedParameters() : m_alienParameters;
1865 }
1866 
formatAddress(QTextStream & str,quint64 address)1867 static void formatAddress(QTextStream &str, quint64 address)
1868 {
1869     if (address) {
1870         str << "0x";
1871         str.setIntegerBase(16);
1872         str << address;
1873         str.setIntegerBase(10);
1874     }
1875 }
1876 
needsChange() const1877 bool BreakpointItem::needsChange() const
1878 {
1879     const BreakpointParameters &oparams = requestedParameters();
1880     if (!oparams.conditionsMatch(m_parameters.condition))
1881         return true;
1882     if (oparams.ignoreCount != m_parameters.ignoreCount)
1883         return true;
1884     if (oparams.enabled != m_parameters.enabled)
1885         return true;
1886     if (oparams.threadSpec != m_parameters.threadSpec)
1887         return true;
1888     if (oparams.command != m_parameters.command)
1889         return true;
1890     if (oparams.type == BreakpointByFileAndLine && oparams.lineNumber != m_parameters.lineNumber)
1891         return true;
1892     // FIXME: Too strict, functions may have parameter lists, or not.
1893     // if (m_params.type == BreakpointByFunction && m_params.functionName != m_response.functionName)
1894     //     return true;
1895     // if (m_params.type == BreakpointByAddress && m_params.address != m_response.address)
1896     //     return true;
1897     return false;
1898 }
1899 
updateMarker()1900 void BreakpointItem::updateMarker()
1901 {
1902     const FilePath &file = markerFileName();
1903     int line = markerLineNumber();
1904     if (m_marker && (file != m_marker->fileName() || line != m_marker->lineNumber()))
1905         destroyMarker();
1906 
1907     if (!m_marker && !file.isEmpty() && line > 0)
1908         m_marker = new BreakpointMarker(this, file, line);
1909 }
1910 
icon(bool withLocationMarker) const1911 QIcon BreakpointItem::icon(bool withLocationMarker) const
1912 {
1913     // FIXME: This seems to be called on each cursor blink as soon as the
1914     // cursor is near a line with a breakpoint marker (+/- 2 lines or so).
1915     if (m_parameters.isTracepoint())
1916         return Icons::TRACEPOINT.icon();
1917     if (m_parameters.type == WatchpointAtAddress)
1918         return Icons::WATCHPOINT.icon();
1919     if (m_parameters.type == WatchpointAtExpression)
1920         return Icons::WATCHPOINT.icon();
1921     if (!m_parameters.enabled)
1922         return Icons::BREAKPOINT_DISABLED.icon();
1923     if (m_state == BreakpointInserted && !m_parameters.pending)
1924         return withLocationMarker ? Icons::BREAKPOINT_WITH_LOCATION.icon()
1925                                   : Icons::BREAKPOINT.icon();
1926     return Icons::BREAKPOINT_PENDING.icon();
1927 }
1928 
toolTip() const1929 QString BreakpointItem::toolTip() const
1930 {
1931     const BreakpointParameters &requested = requestedParameters();
1932     QString rc;
1933     QTextStream str(&rc);
1934     str << "<html><body><b>" << tr("Breakpoint") << "</b>"
1935         << "<table>"
1936         << "<tr><td>" << tr("Internal ID:")
1937         << "</td><td>" << m_responseId << "</td></tr>"
1938         << "<tr><td>" << tr("State:")
1939         << "</td><td>" << (requestedParameters().enabled ? tr("Enabled") : tr("Disabled"));
1940     if (m_parameters.pending)
1941         str << ", " << tr("pending");
1942     str << ", " << stateToString(m_state) << "</td></tr>";
1943     str << "<tr><td>" << tr("Breakpoint Type:")
1944         << "</td><td>" << typeToString(requested.type) << "</td></tr>"
1945         << "<tr><td>" << tr("Marker File:")
1946         << "</td><td>" << markerFileName().toUserOutput() << "</td></tr>"
1947         << "<tr><td>" << tr("Marker Line:")
1948         << "</td><td>" << markerLineNumber() << "</td></tr>"
1949         << "<tr><td>" << tr("Hit Count:")
1950         << "</td><td>" << m_parameters.hitCount << "</td></tr>"
1951         << "</table><br><table>"
1952         << "<tr><th>" << tr("Property")
1953         << "</th><th>" << tr("Requested")
1954         << "</th><th>" << tr("Obtained") << "</th></tr>";
1955     if (!m_displayName.isEmpty()) {
1956         str << "<tr><td>" << tr("Display Name:")
1957             << "</td><td>&mdash;</td><td>" << m_displayName << "</td></tr>";
1958     }
1959     if (m_parameters.type == BreakpointByFunction) {
1960         str << "<tr><td>" << tr("Function Name:")
1961         << "</td><td>" << requested.functionName
1962         << "</td><td>" << m_parameters.functionName
1963         << "</td></tr>";
1964     }
1965     if (m_parameters.type == BreakpointByFileAndLine) {
1966         str << "<tr><td>" << tr("File Name:")
1967             << "</td><td>" << requested.fileName.toUserOutput()
1968             << "</td><td>" << m_parameters.fileName.toUserOutput()
1969             << "</td></tr>"
1970             << "<tr><td>" << tr("Line Number:")
1971             << "</td><td>" << requested.lineNumber
1972             << "</td><td>" << m_parameters.lineNumber << "</td></tr>";
1973     }
1974     if (requested.type == BreakpointByFunction || m_parameters.type == BreakpointByFileAndLine) {
1975         str << "<tr><td>" << tr("Module:")
1976             << "</td><td>" << requested.module
1977             << "</td><td>" << m_parameters.module
1978             << "</td></tr>";
1979     }
1980     str << "<tr><td>" << tr("Breakpoint Address:")
1981         << "</td><td>";
1982     formatAddress(str, requested.address);
1983     str << "</td><td>";
1984     formatAddress(str, m_parameters.address);
1985     str << "</td></tr>";
1986     if (!requested.command.isEmpty() || !m_parameters.command.isEmpty()) {
1987         str << "<tr><td>" << tr("Command:")
1988             << "</td><td>" << requested.command
1989             << "</td><td>" << m_parameters.command
1990             << "</td></tr>";
1991     }
1992     if (!requested.message.isEmpty() || !m_parameters.message.isEmpty()) {
1993         str << "<tr><td>" << tr("Message:")
1994             << "</td><td>" << requested.message
1995             << "</td><td>" << m_parameters.message
1996             << "</td></tr>";
1997     }
1998     if (!requested.condition.isEmpty() || !m_parameters.condition.isEmpty()) {
1999         str << "<tr><td>" << tr("Condition:")
2000             << "</td><td>" << requested.condition
2001             << "</td><td>" << m_parameters.condition
2002             << "</td></tr>";
2003     }
2004     if (requested.ignoreCount || m_parameters.ignoreCount) {
2005         str << "<tr><td>" << tr("Ignore Count:") << "</td><td>";
2006         if (requested.ignoreCount)
2007             str << m_parameters.ignoreCount;
2008         str << "</td><td>";
2009         if (m_parameters.ignoreCount)
2010             str << m_parameters.ignoreCount;
2011         str << "</td></tr>";
2012     }
2013     if (requested.threadSpec >= 0 || m_parameters.threadSpec >= 0) {
2014         str << "<tr><td>" << tr("Thread Specification:")
2015             << "</td><td>";
2016         if (requested.threadSpec >= 0)
2017             str << requested.threadSpec;
2018         str << "</td><td>";
2019         if (m_parameters.threadSpec >= 0)
2020             str << m_parameters.threadSpec;
2021         str << "</td></tr>";
2022     }
2023     str  << "</table></body></html>";
2024     return rc;
2025 }
2026 
setWatchpointAtAddress(quint64 address,unsigned size)2027 void BreakHandler::setWatchpointAtAddress(quint64 address, unsigned size)
2028 {
2029     BreakpointParameters params(WatchpointAtAddress);
2030     params.address = address;
2031     params.size = size;
2032     if (findWatchpoint(params)) {
2033         qDebug() << "WATCHPOINT EXISTS";
2034         //   removeBreakpoint(index);
2035         return;
2036     }
2037     BreakpointManager::createBreakpointForEngine(params, m_engine);
2038 }
2039 
setWatchpointAtExpression(const QString & exp)2040 void BreakHandler::setWatchpointAtExpression(const QString &exp)
2041 {
2042     BreakpointParameters params(WatchpointAtExpression);
2043     params.expression = exp;
2044     if (findWatchpoint(params)) {
2045         qDebug() << "WATCHPOINT EXISTS";
2046         //   removeBreakpoint(index);
2047         return;
2048     }
2049     BreakpointManager::createBreakpointForEngine(params, m_engine);
2050 }
2051 
releaseAllBreakpoints()2052 void BreakHandler::releaseAllBreakpoints()
2053 {
2054     GlobalBreakpoints gbps;
2055     for (Breakpoint bp : breakpoints()) {
2056         bp->removeChildren();
2057         bp->destroyMarker();
2058         gbps.append(bp->globalBreakpoint());
2059     }
2060     clear();
2061     // Make now-unclaimed breakpoints globally visible again.
2062     for (GlobalBreakpoint gbp: qAsConst(gbps)) {
2063         if (gbp)
2064             gbp->updateMarker();
2065     }
2066 }
2067 
msgWatchpointByExpressionTriggered(const QString & expr) const2068 QString BreakpointItem::msgWatchpointByExpressionTriggered(const QString &expr) const
2069 {
2070     return tr("Internal data breakpoint %1 at %2 triggered.")
2071             .arg(responseId()).arg(expr);
2072 }
2073 
msgWatchpointByExpressionTriggered(const QString & expr,const QString & threadId) const2074 QString BreakpointItem::msgWatchpointByExpressionTriggered(const QString &expr,
2075                                                            const QString &threadId) const
2076 {
2077     return tr("Internal data breakpoint %1 at %2 in thread %3 triggered.")
2078             .arg(responseId()).arg(expr).arg(threadId);
2079 }
2080 
msgWatchpointByAddressTriggered(quint64 address) const2081 QString BreakpointItem::msgWatchpointByAddressTriggered(quint64 address) const
2082 {
2083     return tr("Internal data breakpoint %1 at 0x%2 triggered.")
2084             .arg(responseId()).arg(address, 0, 16);
2085 }
2086 
msgWatchpointByAddressTriggered(quint64 address,const QString & threadId) const2087 QString BreakpointItem::msgWatchpointByAddressTriggered(quint64 address,
2088                                                         const QString &threadId) const
2089 {
2090     return tr("Internal data breakpoint %1 at 0x%2 in thread %3 triggered.")
2091             .arg(responseId()).arg(address, 0, 16).arg(threadId);
2092 }
2093 
msgBreakpointTriggered(const QString & threadId) const2094 QString BreakpointItem::msgBreakpointTriggered(const QString &threadId) const
2095 {
2096     return tr("Stopped at breakpoint %1 in thread %2.")
2097             .arg(responseId()).arg(threadId);
2098 }
2099 
2100 
data(int column,int role) const2101 QVariant SubBreakpointItem::data(int column, int role) const
2102 {
2103     if (role == Qt::DecorationRole && column == 0) {
2104         if (params.tracepoint)
2105             return Icons::TRACEPOINT.icon();
2106         return params.enabled ? Icons::BREAKPOINT.icon()
2107                               : Icons::BREAKPOINT_DISABLED.icon();
2108     }
2109 
2110     if (role == Qt::DisplayRole) {
2111         switch (column) {
2112         case BreakpointNumberColumn:
2113             return displayName.isEmpty() ? responseId : displayName;
2114         case BreakpointFunctionColumn:
2115             return params.functionName;
2116         case BreakpointAddressColumn:
2117             if (params.address)
2118                 return QString::fromLatin1("0x%1").arg(params.address, 0, 16);
2119         }
2120     }
2121     return QVariant();
2122 }
2123 
2124 
2125 //
2126 // GlobalBreakpointItem
2127 //
2128 
2129 // Ok to be not thread-safe. The order does not matter and only the gui
2130 // produces authoritative ids.
2131 static int currentId = 0;
2132 
GlobalBreakpointItem()2133 GlobalBreakpointItem::GlobalBreakpointItem()
2134     : m_modelId(++currentId)
2135 {
2136 }
2137 
~GlobalBreakpointItem()2138 GlobalBreakpointItem::~GlobalBreakpointItem()
2139 {
2140     delete m_marker;
2141     m_marker = nullptr;
2142 }
2143 
data(int column,int role) const2144 QVariant GlobalBreakpointItem::data(int column, int role) const
2145 {
2146 
2147     switch (column) {
2148         case BreakpointNumberColumn:
2149             if (role == Qt::DisplayRole) {
2150                 if (auto engine = usingEngine())
2151                     return engine->runParameters().displayName;
2152 
2153 
2154                 return QString("-");
2155             }
2156             if (role == Qt::DecorationRole)
2157                 return icon();
2158             break;
2159         case BreakpointFunctionColumn:
2160             if (role == Qt::DisplayRole) {
2161                 if (!m_params.functionName.isEmpty())
2162                     return m_params.functionName;
2163                 if (m_params.type == BreakpointAtMain
2164                         || m_params.type == BreakpointAtThrow
2165                         || m_params.type == BreakpointAtCatch
2166                         || m_params.type == BreakpointAtFork
2167                         || m_params.type == BreakpointAtExec
2168                         //|| m_params.type == BreakpointAtVFork
2169                         || m_params.type == BreakpointAtSysCall)
2170                     return typeToString(m_params.type);
2171                 if (m_params.type == WatchpointAtAddress)
2172                     return BreakHandler::tr("Data at 0x%1").arg(m_params.address, 0, 16);
2173                 if (m_params.type == WatchpointAtExpression)
2174                     return BreakHandler::tr("Data at %1").arg(m_params.expression);
2175                 return empty;
2176             }
2177             break;
2178         case BreakpointFileColumn:
2179             if (role == Qt::DisplayRole)
2180                 return m_params.fileName.toUserOutput();
2181             break;
2182         case BreakpointLineColumn:
2183             if (role == Qt::DisplayRole) {
2184                 if (m_params.lineNumber > 0)
2185                     return m_params.lineNumber;
2186                 return empty;
2187             }
2188             if (role == Qt::UserRole + 1)
2189                 return m_params.lineNumber;
2190             break;
2191         case BreakpointAddressColumn:
2192             if (role == Qt::DisplayRole) {
2193                 const quint64 address = m_params.address;
2194                 if (address)
2195                     return QString("0x%1").arg(address, 0, 16);
2196                 return QVariant();
2197             }
2198             break;
2199         case BreakpointConditionColumn:
2200             if (role == Qt::DisplayRole)
2201                 return m_params.condition;
2202             if (role == Qt::ToolTipRole)
2203                 return BreakHandler::tr("Breakpoint will only be hit if this condition is met.");
2204             if (role == Qt::UserRole + 1)
2205                 return m_params.condition;
2206             break;
2207         case BreakpointIgnoreColumn:
2208             if (role == Qt::DisplayRole) {
2209                 const int ignoreCount = m_params.ignoreCount;
2210                 return ignoreCount ? QVariant(ignoreCount) : QVariant(QString());
2211             }
2212             if (role == Qt::ToolTipRole)
2213                 return BreakHandler::tr("Breakpoint will only be hit after being ignored so many times.");
2214             if (role == Qt::UserRole + 1)
2215                 return m_params.ignoreCount;
2216             break;
2217         case BreakpointThreadsColumn:
2218             if (role == Qt::DisplayRole)
2219                 return BreakHandler::displayFromThreadSpec(m_params.threadSpec);
2220             if (role == Qt::ToolTipRole)
2221                 return BreakHandler::tr("Breakpoint will only be hit in the specified thread(s).");
2222             if (role == Qt::UserRole + 1)
2223                 return BreakHandler::displayFromThreadSpec(m_params.threadSpec);
2224             break;
2225     }
2226 
2227     if (role == Qt::ToolTipRole && debuggerSettings()->useToolTipsInBreakpointsView.value())
2228         return toolTip();
2229 
2230     return QVariant();
2231 }
2232 
icon() const2233 QIcon GlobalBreakpointItem::icon() const
2234 {
2235     // FIXME: This seems to be called on each cursor blink as soon as the
2236     // cursor is near a line with a breakpoint marker (+/- 2 lines or so).
2237     if (m_params.isTracepoint())
2238         return Icons::TRACEPOINT.icon();
2239     if (m_params.type == WatchpointAtAddress)
2240         return Icons::WATCHPOINT.icon();
2241     if (m_params.type == WatchpointAtExpression)
2242         return Icons::WATCHPOINT.icon();
2243     if (!m_params.enabled)
2244         return Icons::BREAKPOINT_DISABLED.icon();
2245 
2246     return Icons::BREAKPOINT_PENDING.icon();
2247 }
2248 
usingEngine() const2249 QPointer<DebuggerEngine> GlobalBreakpointItem::usingEngine() const
2250 {
2251     for (QPointer<DebuggerEngine> engine : EngineManager::engines()) {
2252         for (Breakpoint bp : engine->breakHandler()->breakpoints()) {
2253             if (bp->globalBreakpoint() == this)
2254                 return engine;
2255         }
2256     }
2257     return nullptr;
2258 }
2259 
modelId() const2260 int GlobalBreakpointItem::modelId() const
2261 {
2262     return m_modelId;
2263 }
2264 
displayName() const2265 QString GlobalBreakpointItem::displayName() const
2266 {
2267     return QString::number(m_modelId);
2268 }
2269 
deleteBreakpoint()2270 void GlobalBreakpointItem::deleteBreakpoint()
2271 {
2272     for (QPointer<DebuggerEngine> engine : EngineManager::engines()) {
2273         BreakHandler *handler = engine->breakHandler();
2274         for (Breakpoint bp : handler->breakpoints()) {
2275             if (bp->globalBreakpoint() == this)
2276                 handler->removeBreakpoint(bp);
2277         }
2278     }
2279     removeBreakpointFromModel();
2280 }
2281 
removeBreakpointFromModel()2282 void GlobalBreakpointItem::removeBreakpointFromModel()
2283 {
2284     delete m_marker;
2285     m_marker = nullptr;
2286     theBreakpointManager->destroyItem(this);
2287 }
2288 
updateLineNumber(int lineNumber)2289 void GlobalBreakpointItem::updateLineNumber(int lineNumber)
2290 {
2291     if (m_params.lineNumber == lineNumber)
2292         return;
2293     m_params.lineNumber = lineNumber;
2294     update();
2295 }
2296 
updateFileName(const FilePath & fileName)2297 void GlobalBreakpointItem::updateFileName(const FilePath &fileName)
2298 {
2299     if (m_params.fileName == fileName)
2300         return;
2301     m_params.fileName = fileName;
2302     update();
2303 }
2304 
markerFileName() const2305 FilePath GlobalBreakpointItem::markerFileName() const
2306 {
2307     // Some heuristics to find a "good" file name.
2308     if (m_params.fileName.exists())
2309         return m_params.fileName.absoluteFilePath();
2310     return m_params.fileName;
2311 }
2312 
markerLineNumber() const2313 int GlobalBreakpointItem::markerLineNumber() const
2314 {
2315     return m_params.lineNumber;
2316 }
2317 
updateMarker()2318 void GlobalBreakpointItem::updateMarker()
2319 {
2320     if (usingEngine() != nullptr) {
2321         // Don't show markers that are claimed by engines.
2322         // FIXME: Apart, perhaps, when the engine's reported location does not match?
2323         destroyMarker();
2324         return;
2325     }
2326 
2327     const int line = m_params.lineNumber;
2328     if (m_marker) {
2329         if (m_params.fileName != m_marker->fileName())
2330             m_marker->updateFileName(m_params.fileName);
2331         if (line != m_marker->lineNumber())
2332             m_marker->move(line);
2333     } else if (!m_params.fileName.isEmpty() && line > 0) {
2334         m_marker = new GlobalBreakpointMarker(this, m_params.fileName, line);
2335     }
2336 }
2337 
setEnabled(bool enabled,bool descend)2338 void GlobalBreakpointItem::setEnabled(bool enabled, bool descend)
2339 {
2340     if (m_params.enabled != enabled) {
2341         m_params.enabled = enabled;
2342         if (m_marker)
2343             m_marker->updateMarker();
2344         update();
2345     }
2346 
2347     if (descend) {
2348         for (QPointer<DebuggerEngine> engine : EngineManager::engines()) {
2349             BreakHandler *handler = engine->breakHandler();
2350             for (Breakpoint bp : handler->breakpoints()) {
2351                 if (bp->globalBreakpoint() == this)
2352                     handler->requestBreakpointEnabling(bp, enabled);
2353             }
2354         }
2355     }
2356 }
2357 
setParameters(const BreakpointParameters & params)2358 void GlobalBreakpointItem::setParameters(const BreakpointParameters &params)
2359 {
2360     if (m_params != params) {
2361         m_params = params;
2362         if (m_marker)
2363             m_marker->updateMarker();
2364         update();
2365     }
2366 }
2367 
destroyMarker()2368 void GlobalBreakpointItem::destroyMarker()
2369 {
2370     delete m_marker;
2371     m_marker = nullptr;
2372 }
2373 
toolTip() const2374 QString GlobalBreakpointItem::toolTip() const
2375 {
2376     QString rc;
2377     QTextStream str(&rc);
2378     str << "<html><body><b>" << BreakpointItem::tr("Unclaimed Breakpoint") << "</b>"
2379         << "<table>"
2380         //<< "<tr><td>" << tr("ID:") << "</td><td>" << m_id << "</td></tr>"
2381         << "<tr><td>" << BreakpointItem::tr("State:")
2382         << "</td><td>" << (m_params.enabled ? BreakpointItem::tr("Enabled") : BreakpointItem::tr("Disabled"))
2383         << "<tr><td>" << BreakpointItem::tr("Breakpoint Type:")
2384         << "</td><td>" << typeToString(m_params.type) << "</td></tr>";
2385     if (m_params.type == BreakpointByFunction) {
2386         str << "<tr><td>" << BreakpointItem::tr("Function Name:")
2387         << "</td><td>" << m_params.functionName
2388         << "</td></tr>";
2389     }
2390     if (m_params.type == BreakpointByFileAndLine) {
2391         str << "<tr><td>" << BreakpointItem::tr("File Name:")
2392             << "</td><td>" << m_params.fileName.toUserOutput()
2393             << "</td></tr>"
2394             << "<tr><td>" << BreakpointItem::tr("Line Number:")
2395             << "</td><td>" << m_params.lineNumber;
2396     }
2397     if (m_params.type == BreakpointByFunction || m_params.type == BreakpointByFileAndLine) {
2398         str << "<tr><td>" << BreakpointItem::tr("Module:")
2399             << "</td><td>" << m_params.module
2400             << "</td></tr>";
2401     }
2402     str << "<tr><td>" << BreakpointItem::tr("Breakpoint Address:") << "</td><td>";
2403     formatAddress(str, m_params.address);
2404     str << "</td></tr>";
2405     if (!m_params.command.isEmpty())
2406         str << "<tr><td>" << BreakpointItem::tr("Command:") << "</td><td>" << m_params.command << "</td></tr>";
2407     if (!m_params.message.isEmpty())
2408         str << "<tr><td>" << BreakpointItem::tr("Message:") << "</td><td>" << m_params.message << "</td></tr>";
2409     if (!m_params.condition.isEmpty())
2410         str << "<tr><td>" << BreakpointItem::tr("Condition:") << "</td><td>" << m_params.condition << "</td></tr>";
2411     if (m_params.ignoreCount)
2412         str << "<tr><td>" << BreakpointItem::tr("Ignore Count:") << "</td><td>" << m_params.ignoreCount << "</td></tr>";
2413     if (m_params.threadSpec >= 0)
2414         str << "<tr><td>" << BreakpointItem::tr("Thread Specification:") << "</td><td>" << m_params.threadSpec << "</td></tr>";
2415 
2416     str  << "</table></body></html><hr>";
2417     return rc;
2418 }
2419 
2420 //
2421 // BreakpointManager
2422 //
2423 
BreakpointManager()2424 BreakpointManager::BreakpointManager()
2425 {
2426     theBreakpointManager = this;
2427     setHeader({tr("Debuggee"), tr("Function"), tr("File"), tr("Line"), tr("Address"),
2428                tr("Condition"), tr("Ignore"), tr("Threads")});
2429     connect(SessionManager::instance(), &SessionManager::sessionLoaded,
2430             this, &BreakpointManager::loadSessionData);
2431     connect(SessionManager::instance(), &SessionManager::aboutToSaveSession,
2432             this, &BreakpointManager::saveSessionData);
2433 }
2434 
model()2435 QAbstractItemModel *BreakpointManager::model()
2436 {
2437     return theBreakpointManager;
2438 }
2439 
globalBreakpoints()2440 const GlobalBreakpoints BreakpointManager::globalBreakpoints()
2441 {
2442     GlobalBreakpoints items;
2443     theBreakpointManager->forItemsAtLevel<1>([&items](GlobalBreakpointItem *b) { items.append(b); });
2444     return items;
2445 }
2446 
claimBreakpointsForEngine(DebuggerEngine * engine)2447 void BreakpointManager::claimBreakpointsForEngine(DebuggerEngine *engine)
2448 {
2449     theBreakpointManager->forItemsAtLevel<1>([&](GlobalBreakpoint gbp) {
2450         engine->breakHandler()->tryClaimBreakpoint(gbp);
2451         gbp->updateMarker();
2452     });
2453 }
2454 
createBreakpointHelper(const BreakpointParameters & params)2455 GlobalBreakpoint BreakpointManager::createBreakpointHelper(const BreakpointParameters &params)
2456 {
2457     GlobalBreakpoint gbp = new GlobalBreakpointItem;
2458     gbp->m_params = params;
2459     gbp->updateMarker();
2460     theBreakpointManager->rootItem()->appendChild(gbp);
2461     return gbp;
2462 }
2463 
findBreakpointByIndex(const QModelIndex & index)2464 GlobalBreakpoint BreakpointManager::findBreakpointByIndex(const QModelIndex &index)
2465 {
2466     return theBreakpointManager->itemForIndexAtLevel<1>(index);
2467 }
2468 
findBreakpointsByIndex(const QList<QModelIndex> & list)2469 GlobalBreakpoints BreakpointManager::findBreakpointsByIndex(const QList<QModelIndex> &list)
2470 {
2471     QSet<GlobalBreakpoint> items;
2472     for (const QModelIndex &index : list) {
2473         if (GlobalBreakpoint gbp = findBreakpointByIndex(index))
2474             items.insert(gbp);
2475     }
2476     return Utils::toList(items);
2477 }
2478 
createBreakpoint(const BreakpointParameters & params)2479 GlobalBreakpoint BreakpointManager::createBreakpoint(const BreakpointParameters &params)
2480 {
2481     GlobalBreakpoint gbp = createBreakpointHelper(params);
2482     for (QPointer<DebuggerEngine> engine : EngineManager::engines())
2483         engine->breakHandler()->tryClaimBreakpoint(gbp);
2484     return gbp;
2485 }
2486 
createBreakpointForEngine(const BreakpointParameters & params,DebuggerEngine * engine)2487 void BreakpointManager::createBreakpointForEngine(const BreakpointParameters &params, DebuggerEngine *engine)
2488 {
2489     GlobalBreakpoint gbp = createBreakpointHelper(params);
2490     engine->breakHandler()->tryClaimBreakpoint(gbp);
2491 }
2492 
toggleBreakpoint(const ContextData & location,const QString & tracePointMessage)2493 void BreakpointManager::toggleBreakpoint(const ContextData &location, const QString &tracePointMessage)
2494 {
2495     QTC_ASSERT(location.isValid(), return);
2496     GlobalBreakpoint gbp = findBreakpointFromContext(location);
2497 
2498     if (gbp) {
2499         gbp->deleteBreakpoint();
2500     } else {
2501         BreakpointParameters data;
2502         if (location.type == LocationByFile) {
2503             data.type = BreakpointByFileAndLine;
2504             if (debuggerSettings()->breakpointsFullPathByDefault.value())
2505                 data.pathUsage = BreakpointUseFullPath;
2506             data.tracepoint = !tracePointMessage.isEmpty();
2507             data.message = tracePointMessage;
2508             data.fileName = location.fileName;
2509             data.lineNumber = location.lineNumber;
2510         } else if (location.type == LocationByAddress) {
2511             data.type = BreakpointByAddress;
2512             data.tracepoint = !tracePointMessage.isEmpty();
2513             data.message = tracePointMessage;
2514             data.address = location.address;
2515         }
2516         BreakpointManager::createBreakpoint(data);
2517     }
2518 }
2519 
findBreakpointFromContext(const ContextData & location)2520 GlobalBreakpoint BreakpointManager::findBreakpointFromContext(const ContextData &location)
2521 {
2522     int matchLevel = 0;
2523     GlobalBreakpoint bestMatch;
2524     theBreakpointManager->forItemsAtLevel<1>([&](const GlobalBreakpoint &gbp) {
2525         if (location.type == LocationByFile) {
2526             if (gbp->m_params.isLocatedAt(location.fileName, location.lineNumber, FilePath())) {
2527                 matchLevel = 2;
2528                 bestMatch = gbp;
2529             } else if (matchLevel < 2) {
2530                 for (const QPointer<DebuggerEngine> &engine : EngineManager::engines()) {
2531                     BreakHandler *handler = engine->breakHandler();
2532                     for (Breakpoint bp : handler->breakpoints()) {
2533                         if (bp->globalBreakpoint() == gbp) {
2534                             if (bp->fileName() == location.fileName
2535                                     && bp->lineNumber() == location.lineNumber) {
2536                                 matchLevel = 1;
2537                                 bestMatch = gbp;
2538                             }
2539                         }
2540                     }
2541                 }
2542             }
2543         } else if (location.type == LocationByAddress) {
2544             if (gbp->m_params.address == location.address) {
2545                 matchLevel = 2;
2546                 bestMatch = gbp;
2547             }
2548         }
2549     });
2550 
2551     return bestMatch;
2552 }
2553 
executeAddBreakpointDialog()2554 void BreakpointManager::executeAddBreakpointDialog()
2555 {
2556     BreakpointParameters data(BreakpointByFileAndLine);
2557     BreakpointParts parts = NoParts;
2558     BreakpointDialog dialog(~0, ICore::dialogParent());
2559     dialog.setWindowTitle(tr("Add Breakpoint"));
2560     if (dialog.showDialog(&data, &parts))
2561         BreakpointManager::createBreakpoint(data);
2562 }
2563 
data(const QModelIndex & idx,int role) const2564 QVariant BreakpointManager::data(const QModelIndex &idx, int role) const
2565 {
2566     if (role == BaseTreeView::ItemDelegateRole)
2567         return QVariant::fromValue(new LeftElideDelegate);
2568 
2569     return BreakpointManagerModel::data(idx, role);
2570 }
2571 
setData(const QModelIndex & idx,const QVariant & value,int role)2572 bool BreakpointManager::setData(const QModelIndex &idx, const QVariant &value, int role)
2573 {
2574     if (role == BaseTreeView::ItemActivatedRole) {
2575         if (GlobalBreakpoint bp = findBreakpointByIndex(idx))
2576             gotoLocation(bp);
2577         return true;
2578     }
2579 
2580     if (role == BaseTreeView::ItemViewEventRole) {
2581         ItemViewEvent ev = value.value<ItemViewEvent>();
2582 
2583         if (ev.as<QContextMenuEvent>())
2584             return contextMenuEvent(ev);
2585 
2586         if (auto kev = ev.as<QKeyEvent>(QEvent::KeyPress)) {
2587             if (kev->key() == Qt::Key_Delete || kev->key() == Qt::Key_Backspace) {
2588                 QModelIndexList si = ev.currentOrSelectedRows();
2589                 const GlobalBreakpoints gbps = findBreakpointsByIndex(si);
2590                 for (GlobalBreakpoint gbp : gbps)
2591                     gbp->deleteBreakpoint();
2592 //                int row = qMin(rowCount() - ids.size() - 1, idx.row());
2593 //                setCurrentIndex(index(row, 0));   FIXME
2594                 return true;
2595             }
2596             if (kev->key() == Qt::Key_Space) {
2597                 const QModelIndexList selectedIds = ev.selectedRows();
2598                 if (!selectedIds.isEmpty()) {
2599                     const GlobalBreakpoints gbps = findBreakpointsByIndex(selectedIds);
2600                     const bool isEnabled = gbps.isEmpty() || gbps.at(0)->isEnabled();
2601                     for (GlobalBreakpoint gbp : gbps)
2602                         gbp->setEnabled(!isEnabled);
2603 //                    scheduleSynchronization();
2604                     return true;
2605                 }
2606             }
2607         }
2608 
2609         if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
2610             if (GlobalBreakpoint gbp = findBreakpointByIndex(idx)) {
2611                 if (idx.column() >= BreakpointAddressColumn)
2612                     editBreakpoints({gbp}, ev.view());
2613                 else
2614                     gotoLocation(gbp);
2615             } else {
2616                 BreakpointManager::executeAddBreakpointDialog();
2617             }
2618             return true;
2619         }
2620     }
2621 
2622     return false;
2623 }
2624 
contextMenuEvent(const ItemViewEvent & ev)2625 bool BreakpointManager::contextMenuEvent(const ItemViewEvent &ev)
2626 {
2627     const QModelIndexList selectedIndices = ev.selectedRows();
2628 
2629     const GlobalBreakpoints selectedBreakpoints = findBreakpointsByIndex(selectedIndices);
2630     const bool breakpointsEnabled = selectedBreakpoints.isEmpty() || selectedBreakpoints.at(0)->isEnabled();
2631 
2632     auto menu = new QMenu;
2633 
2634     addAction(menu, tr("Add Breakpoint..."), true, &BreakpointManager::executeAddBreakpointDialog);
2635 
2636     addAction(menu, tr("Delete Selected Breakpoints"),
2637               !selectedBreakpoints.isEmpty(),
2638               [selectedBreakpoints] {
2639                 for (GlobalBreakpoint gbp : selectedBreakpoints)
2640                     gbp->deleteBreakpoint();
2641              });
2642 
2643     addAction(menu, tr("Edit Selected Breakpoints..."),
2644               !selectedBreakpoints.isEmpty(),
2645               [this, selectedBreakpoints, ev] { editBreakpoints(selectedBreakpoints, ev.view()); });
2646 
2647     addAction(menu,
2648               selectedBreakpoints.size() > 1
2649                   ? breakpointsEnabled ? tr("Disable Selected Breakpoints") : tr("Enable Selected Breakpoints")
2650                   : breakpointsEnabled ? tr("Disable Breakpoint") : tr("Enable Breakpoint"),
2651               !selectedBreakpoints.isEmpty(),
2652               [selectedBreakpoints, breakpointsEnabled] {
2653                     for (GlobalBreakpoint gbp : selectedBreakpoints)
2654                         gbp->setEnabled(!breakpointsEnabled);
2655               }
2656     );
2657 
2658     menu->addSeparator();
2659 
2660     addAction(menu, tr("Delete All Breakpoints"),
2661               rowCount() > 0,
2662               &BreakpointManager::executeDeleteAllBreakpointsDialog);
2663 
2664     // Delete by file: Find breakpoints of the same file.
2665     GlobalBreakpoints breakpointsInFile;
2666     FilePath file;
2667     if (GlobalBreakpoint gbp = itemForIndexAtLevel<1>(ev.sourceModelIndex())) {
2668         file = gbp->markerFileName();
2669         if (!file.isEmpty()) {
2670             forItemsAtLevel<1>([file, &breakpointsInFile](const GlobalBreakpoint &gbp) {
2671                 if (gbp->markerFileName() == file)
2672                     breakpointsInFile.append(gbp);
2673             });
2674         }
2675     }
2676     addAction(menu, tr("Delete Breakpoints of \"%1\"").arg(file.toUserOutput()),
2677               tr("Delete Breakpoints of File"),
2678               breakpointsInFile.size() > 1,
2679               [breakpointsInFile] {
2680                 for (GlobalBreakpoint gbp : breakpointsInFile)
2681                     gbp->deleteBreakpoint();
2682               });
2683 
2684     menu->addSeparator();
2685 
2686     menu->addAction(debuggerSettings()->useToolTipsInBreakpointsView.action());
2687     menu->addAction(debuggerSettings()->settingsDialog.action());
2688 
2689     menu->popup(ev.globalPos());
2690 
2691     return true;
2692 }
2693 
gotoLocation(const GlobalBreakpoint & gbp) const2694 void BreakpointManager::gotoLocation(const GlobalBreakpoint &gbp) const
2695 {
2696     QTC_ASSERT(gbp, return);
2697     if (IEditor *editor = EditorManager::openEditor(gbp->markerFileName()))
2698         editor->gotoLine(gbp->markerLineNumber(), 0);
2699 }
2700 
executeDeleteAllBreakpointsDialog()2701 void BreakpointManager::executeDeleteAllBreakpointsDialog()
2702 {
2703     QDialogButtonBox::StandardButton pressed =
2704         CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
2705            tr("Remove All Breakpoints"),
2706            tr("Are you sure you want to remove all breakpoints "
2707               "from all files in the current session?"),
2708            ICore::settings(),
2709            "RemoveAllBreakpoints");
2710     if (pressed != QDialogButtonBox::Yes)
2711         return;
2712 
2713     for (GlobalBreakpoint gbp : globalBreakpoints())
2714         gbp->deleteBreakpoint();
2715 }
2716 
editBreakpoint(const GlobalBreakpoint & gbp,QWidget * parent)2717 void BreakpointManager::editBreakpoint(const GlobalBreakpoint &gbp, QWidget *parent)
2718 {
2719     QTC_ASSERT(gbp, return);
2720     BreakpointParts parts = NoParts;
2721 
2722     BreakpointParameters params = gbp->requestedParameters();
2723     BreakpointDialog dialog(~0, parent);
2724     if (!dialog.showDialog(&params, &parts))
2725         return;
2726 
2727     gbp->destroyMarker();
2728     gbp->deleteBreakpoint();
2729     BreakpointManager::createBreakpoint(params);
2730 }
2731 
editBreakpoints(const GlobalBreakpoints & gbps,QWidget * parent)2732 void BreakpointManager::editBreakpoints(const GlobalBreakpoints &gbps, QWidget *parent)
2733 {
2734     QTC_ASSERT(!gbps.isEmpty(), return);
2735 
2736     GlobalBreakpoint gbp = gbps.at(0);
2737 
2738     if (gbps.size() == 1) {
2739         editBreakpoint(gbp, parent);
2740         return;
2741     }
2742 
2743     // This allows to change properties of multiple breakpoints at a time.
2744     QTC_ASSERT(gbp, return);
2745     BreakpointParameters params = gbp->requestedParameters();
2746 
2747     MultiBreakPointsDialog dialog(~0, parent);
2748     dialog.setCondition(params.condition);
2749     dialog.setIgnoreCount(params.ignoreCount);
2750     dialog.setThreadSpec(params.threadSpec);
2751 
2752     if (dialog.exec() == QDialog::Rejected)
2753         return;
2754 
2755     const QString newCondition = dialog.condition();
2756     const int newIgnoreCount = dialog.ignoreCount();
2757     const int newThreadSpec = dialog.threadSpec();
2758 
2759     for (GlobalBreakpoint gbp : gbps) {
2760         QTC_ASSERT(gbp, continue);
2761         BreakpointParameters newParams = gbp->requestedParameters();
2762         newParams.condition = newCondition;
2763         newParams.ignoreCount = newIgnoreCount;
2764         newParams.threadSpec = newThreadSpec;
2765         gbp->destroyMarker();
2766         gbp->deleteBreakpoint();
2767         BreakpointManager::createBreakpoint(newParams);
2768     }
2769 }
2770 
saveSessionData()2771 void BreakpointManager::saveSessionData()
2772 {
2773     QList<QVariant> list;
2774     theBreakpointManager->forItemsAtLevel<1>([&list](const GlobalBreakpoint &bp) {
2775         const BreakpointParameters &params = bp->m_params;
2776         QMap<QString, QVariant> map;
2777         if (params.type != BreakpointByFileAndLine)
2778             map.insert("type", params.type);
2779         if (!params.fileName.isEmpty())
2780             map.insert("filename", params.fileName.toVariant());
2781         if (params.lineNumber)
2782             map.insert("linenumber", params.lineNumber);
2783         if (!params.functionName.isEmpty())
2784             map.insert("funcname", params.functionName);
2785         if (params.address)
2786             map.insert("address", params.address);
2787         if (!params.condition.isEmpty())
2788             map.insert("condition", params.condition);
2789         if (params.ignoreCount)
2790             map.insert("ignorecount", params.ignoreCount);
2791         if (params.threadSpec >= 0)
2792             map.insert("threadspec", params.threadSpec);
2793         if (!params.enabled)
2794             map.insert("disabled", "1");
2795         if (params.oneShot)
2796             map.insert("oneshot", "1");
2797         if (params.pathUsage != BreakpointPathUsageEngineDefault)
2798             map.insert("usefullpath", QString::number(params.pathUsage));
2799         if (params.tracepoint)
2800             map.insert("tracepoint", "1");
2801         if (!params.module.isEmpty())
2802             map.insert("module", params.module);
2803         if (!params.command.isEmpty())
2804             map.insert("command", params.command);
2805         if (!params.expression.isEmpty())
2806             map.insert("expression", params.expression);
2807         if (!params.message.isEmpty())
2808             map.insert("message", params.message);
2809         list.append(map);
2810     });
2811     SessionManager::setValue("Breakpoints", list);
2812 }
2813 
loadSessionData()2814 void BreakpointManager::loadSessionData()
2815 {
2816     clear();
2817 
2818     const QVariant value = SessionManager::value("Breakpoints");
2819     const QList<QVariant> list = value.toList();
2820     for (const QVariant &var : list) {
2821         const QMap<QString, QVariant> map = var.toMap();
2822         BreakpointParameters params(BreakpointByFileAndLine);
2823         QVariant v = map.value("filename");
2824         if (v.isValid())
2825             params.fileName = FilePath::fromVariant(v);
2826         v = map.value("linenumber");
2827         if (v.isValid())
2828             params.lineNumber = v.toString().toInt();
2829         v = map.value("condition");
2830         if (v.isValid())
2831             params.condition = v.toString();
2832         v = map.value("address");
2833         if (v.isValid())
2834             params.address = v.toString().toULongLong();
2835         v = map.value("ignorecount");
2836         if (v.isValid())
2837             params.ignoreCount = v.toString().toInt();
2838         v = map.value("threadspec");
2839         if (v.isValid())
2840             params.threadSpec = v.toString().toInt();
2841         v = map.value("funcname");
2842         if (v.isValid())
2843             params.functionName = v.toString();
2844         v = map.value("disabled");
2845         if (v.isValid())
2846             params.enabled = !v.toInt();
2847         v = map.value("oneshot");
2848         if (v.isValid())
2849             params.oneShot = v.toInt();
2850         v = map.value("usefullpath");
2851         if (v.isValid())
2852             params.pathUsage = static_cast<BreakpointPathUsage>(v.toInt());
2853         v = map.value("tracepoint");
2854         if (v.isValid())
2855             params.tracepoint = bool(v.toInt());
2856         v = map.value("type");
2857         if (v.isValid() && v.toInt() != UnknownBreakpointType)
2858             params.type = BreakpointType(v.toInt());
2859         v = map.value("module");
2860         if (v.isValid())
2861             params.module = v.toString();
2862         v = map.value("command");
2863         if (v.isValid())
2864             params.command = v.toString();
2865         v = map.value("expression");
2866         if (v.isValid())
2867             params.expression = v.toString();
2868         v = map.value("message");
2869         if (v.isValid())
2870             params.message = v.toString();
2871         if (params.isValid())
2872             BreakpointManager::createBreakpoint(params);
2873         else
2874             qWarning("Not restoring invalid breakpoint: %s", qPrintable(params.toString()));
2875     }
2876 }
2877 
2878 } // namespace Internal
2879 } // namespace Debugger
2880