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 ¶ms, 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 ¶ms) 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 ¶ms)
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(¶ms, &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>—</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 ¶ms)
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 ¶ms)
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 ¶ms)
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 ¶ms, 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(¶ms, &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 ¶ms = 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