1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "breakhandler.h"
35 #include "breakpointmarker.h"
37 #include "debuggeractions.h"
38 #include "debuggercore.h"
39 #include "debuggerengine.h"
40 #include "debuggerstringutils.h"
41 #include "stackframe.h"
43 #include <utils/qtcassert.h>
45 #include <QtCore/QDir>
46 #include <QtCore/QFileInfo>
47 #include <QtCore/QTimerEvent>
50 //////////////////////////////////////////////////////////////////
54 //////////////////////////////////////////////////////////////////
59 static QString stateToString(BreakpointState state)
63 return BreakHandler::tr("New");
64 case BreakpointInsertRequested:
65 return BreakHandler::tr("Insertion requested");
66 case BreakpointInsertProceeding:
67 return BreakHandler::tr("Insertion proceeding");
68 case BreakpointChangeRequested:
69 return BreakHandler::tr("Change requested");
70 case BreakpointChangeProceeding:
71 return BreakHandler::tr("Change proceeding");
72 case BreakpointInserted:
73 return BreakHandler::tr("Breakpoint inserted");
74 case BreakpointRemoveRequested:
75 return BreakHandler::tr("Removal requested");
76 case BreakpointRemoveProceeding:
77 return BreakHandler::tr("Removal proceeding");
79 return BreakHandler::tr("Dead");
83 //: Invalid breakpoint state.
84 return BreakHandler::tr("<invalid state>");
87 static QString msgBreakpointAtSpecialFunc(const char *func)
89 return BreakHandler::tr("Breakpoint at \"%1\"").arg(QString::fromAscii(func));
92 static QString typeToString(BreakpointType type)
95 case BreakpointByFileAndLine:
96 return BreakHandler::tr("Breakpoint by File and Line");
97 case BreakpointByFunction:
98 return BreakHandler::tr("Breakpoint by Function");
99 case BreakpointByAddress:
100 return BreakHandler::tr("Breakpoint by Address");
101 case BreakpointAtThrow:
102 return msgBreakpointAtSpecialFunc("throw");
103 case BreakpointAtCatch:
104 return msgBreakpointAtSpecialFunc("catch");
105 case BreakpointAtFork:
106 return msgBreakpointAtSpecialFunc("fork");
107 case BreakpointAtExec:
108 return msgBreakpointAtSpecialFunc("exec");
109 //case BreakpointAtVFork:
110 // return msgBreakpointAtSpecialFunc("vfork");
111 case BreakpointAtSysCall:
112 return msgBreakpointAtSpecialFunc("syscall");
113 case BreakpointAtMain:
114 return BreakHandler::tr("Breakpoint at Function \"main()\"");
116 return BreakHandler::tr("Watchpoint");
120 return BreakHandler::tr("Unknown Breakpoint Type");
123 BreakHandler::BreakHandler()
127 BreakHandler::~BreakHandler()
130 QIcon BreakHandler::breakpointIcon()
132 static QIcon icon(_(":/debugger/images/breakpoint_16.png"));
136 QIcon BreakHandler::disabledBreakpointIcon()
138 static QIcon icon(_(":/debugger/images/breakpoint_disabled_16.png"));
142 QIcon BreakHandler::pendingBreakpointIcon()
144 static QIcon icon(_(":/debugger/images/breakpoint_pending_16.png"));
148 QIcon BreakHandler::watchpointIcon()
150 static QIcon icon(_(":/debugger/images/watchpoint.png"));
154 QIcon BreakHandler::tracepointIcon()
156 static QIcon icon(_(":/debugger/images/tracepoint.png"));
160 QIcon BreakHandler::emptyIcon()
162 static QIcon icon(_(":/debugger/images/breakpoint_pending_16.png"));
163 //static QIcon icon(_(":/debugger/images/watchpoint.png"));
164 //static QIcon icon(_(":/debugger/images/debugger_empty_14.png"));
168 int BreakHandler::columnCount(const QModelIndex &parent) const
170 return parent.isValid() ? 0 : 8;
173 int BreakHandler::rowCount(const QModelIndex &parent) const
175 return parent.isValid() ? 0 : m_storage.size();
178 static inline bool fileNameMatch(const QString &f1, const QString &f2)
181 return f1.compare(f2, Qt::CaseInsensitive) == 0;
187 static bool isSimilarTo(const BreakpointParameters &data, const BreakpointResponse &needle)
191 if (needle.type != UnknownType && data.type != UnknownType
192 && data.type != needle.type)
196 if (data.address && data.address == needle.address)
199 // At least at a position we were looking for.
200 // FIXME: breaks multiple breakpoints at the same location
201 if (!data.fileName.isEmpty()
202 && fileNameMatch(data.fileName, needle.fileName)
203 && data.lineNumber == needle.lineNumber)
206 // At least at a position we were looking for.
207 // FIXME: breaks multiple breakpoints at the same location
208 if (!data.fileName.isEmpty()
209 && fileNameMatch(data.fileName, needle.fileName)
210 && data.lineNumber == needle.lineNumber)
216 BreakpointId BreakHandler::findSimilarBreakpoint(const BreakpointResponse &needle) const
218 // Search a breakpoint we might refer to.
219 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
220 for ( ; it != et; ++it) {
221 const BreakpointId id = it.key();
222 const BreakpointParameters &data = it->data;
223 const BreakpointResponse &response = it->response;
224 //qDebug() << "COMPARING " << data.toString() << " WITH " << needle.toString();
225 if (response.number && response.number == needle.number)
228 if (isSimilarTo(data, needle))
231 return BreakpointId();
234 BreakpointId BreakHandler::findBreakpointByNumber(int bpNumber) const
236 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
237 for ( ; it != et; ++it)
238 if (it->response.number == bpNumber)
240 return BreakpointId();
243 BreakpointId BreakHandler::findBreakpointByFunction(const QString &functionName) const
245 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
246 for ( ; it != et; ++it)
247 if (it->data.functionName == functionName)
249 return BreakpointId();
252 BreakpointId BreakHandler::findBreakpointByAddress(quint64 address) const
254 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
255 for ( ; it != et; ++it)
256 if (it->data.address == address || it->response.address == address)
258 return BreakpointId();
261 BreakpointId BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
262 int lineNumber, bool useMarkerPosition)
264 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
265 for ( ; it != et; ++it)
266 if (it->isLocatedAt(fileName, lineNumber, useMarkerPosition))
268 return BreakpointId();
271 const BreakpointParameters &BreakHandler::breakpointData(BreakpointId id) const
273 static BreakpointParameters dummy;
274 ConstIterator it = m_storage.find(id);
275 QTC_ASSERT(it != m_storage.end(), return dummy);
279 BreakpointId BreakHandler::findWatchpoint(const BreakpointParameters &data) const
281 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
282 for ( ; it != et; ++it)
283 if (it->data.isWatchpoint()
284 && it->data.address == data.address
285 && it->data.size == data.size
286 && it->data.bitpos == data.bitpos)
288 return BreakpointId();
291 void BreakHandler::saveBreakpoints()
293 const QString one = _("1");
294 //qDebug() << "SAVING BREAKPOINTS...";
295 QTC_ASSERT(debuggerCore(), return);
296 QList<QVariant> list;
297 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
298 for ( ; it != et; ++it) {
299 const BreakpointParameters &data = it->data;
300 QMap<QString, QVariant> map;
301 // Do not persist Watchpoints.
302 if (data.isWatchpoint())
304 if (data.type != BreakpointByFileAndLine)
305 map.insert(_("type"), data.type);
306 if (!data.fileName.isEmpty())
307 map.insert(_("filename"), data.fileName);
309 map.insert(_("linenumber"), data.lineNumber);
310 if (!data.functionName.isEmpty())
311 map.insert(_("funcname"), data.functionName);
313 map.insert(_("address"), data.address);
314 if (!data.condition.isEmpty())
315 map.insert(_("condition"), data.condition);
316 if (data.ignoreCount)
317 map.insert(_("ignorecount"), data.ignoreCount);
318 if (data.threadSpec >= 0)
319 map.insert(_("threadspec"), data.threadSpec);
321 map.insert(_("disabled"), one);
322 if (data.pathUsage != BreakpointPathUsageEngineDefault)
323 map.insert(_("usefullpath"), QString::number(data.pathUsage));
325 map.insert(_("tracepoint"), one);
326 if (!data.module.isEmpty())
327 map.insert(_("module"), data.module);
328 if (!data.command.isEmpty())
329 map.insert(_("command"), data.command);
332 debuggerCore()->setSessionValue("Breakpoints", list);
333 //qDebug() << "SAVED BREAKPOINTS" << this << list.size();
336 void BreakHandler::loadBreakpoints()
338 QTC_ASSERT(debuggerCore(), return);
339 //qDebug() << "LOADING BREAKPOINTS...";
340 QVariant value = debuggerCore()->sessionValue("Breakpoints");
341 QList<QVariant> list = value.toList();
343 foreach (const QVariant &var, list) {
344 const QMap<QString, QVariant> map = var.toMap();
345 BreakpointParameters data(BreakpointByFileAndLine);
346 QVariant v = map.value(_("filename"));
348 data.fileName = v.toString();
349 v = map.value(_("linenumber"));
351 data.lineNumber = v.toString().toInt();
352 v = map.value(_("condition"));
354 data.condition = v.toString().toLatin1();
355 v = map.value(_("address"));
357 data.address = v.toString().toULongLong();
358 v = map.value(_("ignorecount"));
360 data.ignoreCount = v.toString().toInt();
361 v = map.value(_("threadspec"));
363 data.threadSpec = v.toString().toInt();
364 v = map.value(_("funcname"));
366 data.functionName = v.toString();
367 v = map.value(_("disabled"));
369 data.enabled = !v.toInt();
370 v = map.value(_("usefullpath"));
372 data.pathUsage = static_cast<BreakpointPathUsage>(v.toInt());
373 v = map.value(_("tracepoint"));
375 data.tracepoint = bool(v.toInt());
376 v = map.value(_("type"));
377 if (v.isValid() && v.toInt() != UnknownType)
378 data.type = BreakpointType(v.toInt());
379 v = map.value(_("module"));
381 data.module = v.toString();
382 v = map.value(_("command"));
384 data.command = v.toString();
385 appendBreakpoint(data);
387 //qDebug() << "LOADED BREAKPOINTS" << this << list.size();
390 void BreakHandler::updateMarkers()
392 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
393 for ( ; it != et; ++it)
394 updateMarker(it.key());
397 void BreakHandler::updateMarker(BreakpointId id)
399 Iterator it = m_storage.find(id);
400 QTC_ASSERT(it != m_storage.end(), return);
402 QString markerFileName = it->markerFileName();
403 int markerLineNumber = it->markerLineNumber();
404 if (it->marker && (markerFileName != it->marker->fileName()
405 || markerLineNumber != it->marker->lineNumber()))
408 if (!it->marker && !markerFileName.isEmpty() && markerLineNumber > 0)
409 it->marker = new BreakpointMarker(id, markerFileName, markerLineNumber);
412 QVariant BreakHandler::headerData(int section,
413 Qt::Orientation orientation, int role) const
415 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
416 static QString headers[] = {
417 tr("Number"), tr("Function"), tr("File"), tr("Line"),
418 tr("Address"), tr("Condition"), tr("Ignore"), tr("Threads")
420 return headers[section];
425 BreakpointId BreakHandler::findBreakpointByIndex(const QModelIndex &index) const
428 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
429 for (int i = 0; it != et; ++it, ++i)
432 return BreakpointId();
435 BreakpointIds BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
437 QSet<BreakpointId> ids;
438 foreach (const QModelIndex &index, list)
439 ids.insert(findBreakpointByIndex(index));
443 Qt::ItemFlags BreakHandler::flags(const QModelIndex &index) const
445 // switch (index.column()) {
447 // // return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
449 return QAbstractTableModel::flags(index);
453 QString BreakHandler::displayFromThreadSpec(int spec)
455 return spec == -1 ? BreakHandler::tr("(all)") : QString::number(spec);
458 int BreakHandler::threadSpecFromDisplay(const QString &str)
461 int result = str.toInt(&ok);
462 return ok ? result : -1;
465 QVariant BreakHandler::data(const QModelIndex &mi, int role) const
467 static const QString empty = QString(QLatin1Char('-'));
472 BreakpointId id = findBreakpointByIndex(mi);
473 //qDebug() << "DATA: " << id << role << mi.column();
474 ConstIterator it = m_storage.find(id);
475 QTC_ASSERT(it != m_storage.end(), return QVariant());
476 const BreakpointParameters &data = it->data;
477 const BreakpointResponse &response = it->response;
481 case BreakpointInsertRequested:
482 case BreakpointInsertProceeding:
483 case BreakpointChangeRequested:
484 case BreakpointChangeProceeding:
485 case BreakpointInserted:
486 case BreakpointRemoveRequested:
487 case BreakpointRemoveProceeding:
495 switch (mi.column()) {
497 if (role == Qt::DisplayRole) {
498 return QString::number(id);
499 //return QString("%1 - %2").arg(id).arg(response.number);
501 if (role == Qt::DecorationRole)
505 if (role == Qt::DisplayRole) {
506 if (!response.functionName.isEmpty())
507 return response.functionName;
508 if (!data.functionName.isEmpty())
509 return data.functionName;
510 if (data.type == BreakpointAtMain
511 || data.type == BreakpointAtThrow
512 || data.type == BreakpointAtCatch
513 || data.type == BreakpointAtFork
514 || data.type == BreakpointAtExec
515 //|| data.type == BreakpointAtVFork
516 || data.type == BreakpointAtSysCall)
517 return typeToString(data.type);
518 if (data.type == Watchpoint)
519 return tr("Watchpoint at 0x%1").arg(data.address, 0, 16);
524 if (role == Qt::DisplayRole) {
526 if (!response.fileName.isEmpty())
527 str = response.fileName;
528 if (str.isEmpty() && !data.fileName.isEmpty())
531 QString s = QFileInfo(str).fileName();
536 //if (data.multiple && str.isEmpty() && !response.fileName.isEmpty())
537 // str = response.fileName;
539 return QDir::toNativeSeparators(str);
544 if (role == Qt::DisplayRole) {
545 if (response.lineNumber > 0)
546 return response.lineNumber;
547 if (data.lineNumber > 0)
548 return data.lineNumber;
551 if (role == Qt::UserRole + 1)
552 return data.lineNumber;
555 if (role == Qt::DisplayRole) {
556 QString displayValue;
557 const quint64 address = orig ? data.address : response.address;
559 displayValue += QString::fromAscii("0x%1").arg(address, 0, 16);
560 if (0 && !response.extra.isEmpty()) {
561 if (!displayValue.isEmpty())
562 displayValue += QLatin1Char(' ');
563 displayValue += QString::fromAscii(response.extra);
569 if (role == Qt::DisplayRole)
570 return orig ? data.condition : response.condition;
571 if (role == Qt::ToolTipRole)
572 return tr("Breakpoint will only be hit if this condition is met.");
573 if (role == Qt::UserRole + 1)
574 return data.condition;
577 if (role == Qt::DisplayRole) {
578 const int ignoreCount =
579 orig ? data.ignoreCount : response.ignoreCount;
580 return ignoreCount ? QVariant(ignoreCount) : QVariant(QString());
582 if (role == Qt::ToolTipRole)
583 return tr("Breakpoint will only be hit after being ignored so many times.");
584 if (role == Qt::UserRole + 1)
585 return data.ignoreCount;
588 if (role == Qt::DisplayRole)
589 return displayFromThreadSpec(orig ? data.threadSpec : response.threadSpec);
590 if (role == Qt::ToolTipRole)
591 return tr("Breakpoint will only be hit in the specified thread(s).");
592 if (role == Qt::UserRole + 1)
593 return displayFromThreadSpec(data.threadSpec);
597 case Qt::ToolTipRole:
598 if (debuggerCore()->boolSetting(UseToolTipsInBreakpointsView))
599 return QVariant(it->toToolTip());
601 case EngineCapabilitiesRole: {
602 const unsigned caps = it.value().engine ?
603 it.value().engine->debuggerCapabilities() :
604 unsigned(AllDebuggerCapabilities);
605 return QVariant(caps);
611 #define GETTER(type, getter) \
612 type BreakHandler::getter(BreakpointId id) const \
614 ConstIterator it = m_storage.find(id); \
615 QTC_ASSERT(it != m_storage.end(), \
616 qDebug() << "ID" << id << "NOT KNOWN"; \
618 return it->data.getter; \
621 #define SETTER(type, getter, setter) \
622 void BreakHandler::setter(BreakpointId id, const type &value) \
624 Iterator it = m_storage.find(id); \
625 QTC_ASSERT(it != m_storage.end(), \
626 qDebug() << "ID" << id << "NOT KNOWN"; return); \
627 if (it->data.getter == value) \
629 it->data.getter = value; \
630 if (it->state != BreakpointNew) { \
631 it->state = BreakpointChangeRequested; \
632 scheduleSynchronization(); \
636 #define PROPERTY(type, getter, setter) \
637 GETTER(type, getter) \
638 SETTER(type, getter, setter)
641 PROPERTY(BreakpointPathUsage, pathUsage, setPathUsage)
642 PROPERTY(QString, fileName, setFileName)
643 PROPERTY(QString, functionName, setFunctionName)
644 PROPERTY(BreakpointType, type, setType)
645 PROPERTY(int, threadSpec, setThreadSpec)
646 PROPERTY(QByteArray, condition, setCondition)
647 GETTER(int, lineNumber)
648 PROPERTY(quint64, address, setAddress)
649 PROPERTY(int, ignoreCount, setIgnoreCount)
651 bool BreakHandler::isEnabled(BreakpointId id) const
653 ConstIterator it = m_storage.find(id);
654 QTC_ASSERT(it != m_storage.end(), return false);
655 return it->data.enabled;
658 void BreakHandler::setEnabled(BreakpointId id, bool on)
660 Iterator it = m_storage.find(id);
661 QTC_ASSERT(it != m_storage.end(), return);
662 //qDebug() << "SET ENABLED: " << id << it->data.isEnabled() << on;
663 if (it->data.enabled == on)
665 it->data.enabled = on;
669 it->state = BreakpointChangeRequested;
670 scheduleSynchronization();
674 bool BreakHandler::isTracepoint(BreakpointId id) const
676 ConstIterator it = m_storage.find(id);
677 QTC_ASSERT(it != m_storage.end(), return false);
678 return it->data.tracepoint;
681 void BreakHandler::setTracepoint(BreakpointId id, bool on)
683 Iterator it = m_storage.find(id);
684 QTC_ASSERT(it != m_storage.end(), return);
685 if (it->data.tracepoint == on)
687 it->data.tracepoint = on;
692 it->state = BreakpointChangeRequested;
693 scheduleSynchronization();
697 void BreakHandler::setMarkerFileAndLine(BreakpointId id,
698 const QString &fileName, int lineNumber)
700 Iterator it = m_storage.find(id);
701 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return);
702 if (it->response.fileName == fileName && it->response.lineNumber == lineNumber)
704 it->response.fileName = fileName;
705 it->response.lineNumber = lineNumber;
708 emit layoutChanged();
711 BreakpointState BreakHandler::state(BreakpointId id) const
713 ConstIterator it = m_storage.find(id);
714 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return BreakpointDead);
718 DebuggerEngine *BreakHandler::engine(BreakpointId id) const
720 ConstIterator it = m_storage.find(id);
721 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return 0);
725 void BreakHandler::setEngine(BreakpointId id, DebuggerEngine *value)
727 Iterator it = m_storage.find(id);
728 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return);
729 QTC_ASSERT(it->state == BreakpointNew, qDebug() << id);
730 QTC_ASSERT(!it->engine, qDebug() << id; return);
732 it->state = BreakpointInsertRequested;
733 it->response = BreakpointResponse();
734 it->response.fileName = it->data.fileName;
736 scheduleSynchronization();
739 static bool isAllowedTransition(BreakpointState from, BreakpointState to)
743 return to == BreakpointInsertRequested;
744 case BreakpointInsertRequested:
745 return to == BreakpointInsertProceeding;
746 case BreakpointInsertProceeding:
747 return to == BreakpointInserted
748 || to == BreakpointDead
749 || to == BreakpointChangeRequested;
750 case BreakpointChangeRequested:
751 return to == BreakpointChangeProceeding;
752 case BreakpointChangeProceeding:
753 return to == BreakpointInserted
754 || to == BreakpointDead;
755 case BreakpointInserted:
756 return to == BreakpointChangeRequested
757 || to == BreakpointRemoveRequested;
758 case BreakpointRemoveRequested:
759 return to == BreakpointRemoveProceeding;
760 case BreakpointRemoveProceeding:
761 return to == BreakpointDead;
765 qDebug() << "UNKNOWN BREAKPOINT STATE:" << from;
769 void BreakHandler::setState(BreakpointId id, BreakpointState state)
771 Iterator it = m_storage.find(id);
772 //qDebug() << "BREAKPOINT STATE TRANSITION" << id << it->state << state;
773 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return);
774 QTC_ASSERT(isAllowedTransition(it->state, state),
775 qDebug() << "UNEXPECTED BREAKPOINT STATE TRANSITION"
776 << it->state << state);
778 if (it->state == state) {
779 qDebug() << "STATE UNCHANGED: " << id << state;
787 void BreakHandler::notifyBreakpointChangeAfterInsertNeeded(BreakpointId id)
789 QTC_ASSERT(state(id) == BreakpointInsertProceeding, qDebug() << state(id));
790 setState(id, BreakpointChangeRequested);
793 void BreakHandler::notifyBreakpointInsertProceeding(BreakpointId id)
795 QTC_ASSERT(state(id) == BreakpointInsertRequested, qDebug() << state(id));
796 setState(id, BreakpointInsertProceeding);
799 void BreakHandler::notifyBreakpointInsertOk(BreakpointId id)
801 QTC_ASSERT(state(id) == BreakpointInsertProceeding, qDebug() << state(id));
802 setState(id, BreakpointInserted);
803 ConstIterator it = m_storage.find(id);
804 QTC_ASSERT(it != m_storage.end(), return);
807 void BreakHandler::notifyBreakpointInsertFailed(BreakpointId id)
809 QTC_ASSERT(state(id) == BreakpointInsertProceeding, qDebug() << state(id));
810 setState(id, BreakpointDead);
813 void BreakHandler::notifyBreakpointRemoveProceeding(BreakpointId id)
815 QTC_ASSERT(state(id) == BreakpointRemoveRequested, qDebug() << state(id));
816 setState(id, BreakpointRemoveProceeding);
819 void BreakHandler::notifyBreakpointRemoveOk(BreakpointId id)
821 QTC_ASSERT(state(id) == BreakpointRemoveProceeding, qDebug() << state(id));
822 setState(id, BreakpointDead);
823 cleanupBreakpoint(id);
826 void BreakHandler::notifyBreakpointRemoveFailed(BreakpointId id)
828 QTC_ASSERT(state(id) == BreakpointRemoveProceeding, qDebug() << state(id));
829 setState(id, BreakpointDead);
830 cleanupBreakpoint(id);
833 void BreakHandler::notifyBreakpointChangeProceeding(BreakpointId id)
835 QTC_ASSERT(state(id) == BreakpointChangeRequested, qDebug() << state(id));
836 setState(id, BreakpointChangeProceeding);
839 void BreakHandler::notifyBreakpointChangeOk(BreakpointId id)
841 QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
842 setState(id, BreakpointInserted);
845 void BreakHandler::notifyBreakpointChangeFailed(BreakpointId id)
847 QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
848 setState(id, BreakpointDead);
851 void BreakHandler::notifyBreakpointReleased(BreakpointId id)
853 //QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
854 Iterator it = m_storage.find(id);
855 QTC_ASSERT(it != m_storage.end(), return);
856 it->state = BreakpointNew;
858 it->response = BreakpointResponse();
865 void BreakHandler::notifyBreakpointAdjusted(BreakpointId id,
866 const BreakpointParameters &data)
868 QTC_ASSERT(state(id) == BreakpointInserted, qDebug() << state(id));
869 Iterator it = m_storage.find(id);
870 QTC_ASSERT(it != m_storage.end(), return);
872 //if (it->needsChange())
873 // setState(id, BreakpointChangeRequested);
876 void BreakHandler::notifyBreakpointNeedsReinsertion(BreakpointId id)
878 QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
879 Iterator it = m_storage.find(id);
880 QTC_ASSERT(it != m_storage.end(), return);
881 it->state = BreakpointInsertRequested;
884 void BreakHandler::removeBreakpoint(BreakpointId id)
886 Iterator it = m_storage.find(id);
887 QTC_ASSERT(it != m_storage.end(), return);
889 case BreakpointInserted:
890 setState(id, BreakpointRemoveRequested);
891 scheduleSynchronization();
894 it->state = BreakpointDead;
895 cleanupBreakpoint(id);
898 qWarning("Warning: Cannot remove breakpoint %llu in state '%s'.",
899 id, qPrintable(stateToString(it->state)));
900 it->state = BreakpointRemoveRequested;
905 void BreakHandler::appendBreakpoint(const BreakpointParameters &data)
907 QTC_ASSERT(data.type != UnknownType, return);
909 // Ok to be not thread-safe. The order does not matter and only the gui
910 // produces authoritative ids.
911 static quint64 currentId = 0;
913 BreakpointId id(++currentId);
916 item.response.fileName = data.fileName;
917 item.response.lineNumber = data.lineNumber;
919 const int row = m_storage.size();
920 beginInsertRows(QModelIndex(), row, row);
921 m_storage.insert(id, item);
925 scheduleSynchronization();
929 void BreakHandler::saveSessionData()
934 void BreakHandler::loadSessionData()
940 void BreakHandler::removeSessionData()
942 Iterator it = m_storage.begin(), et = m_storage.end();
943 for ( ; it != et; ++it)
949 void BreakHandler::breakByFunction(const QString &functionName)
951 // One breakpoint per function is enough for now. This does not handle
952 // combinations of multiple conditions and ignore counts, though.
953 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
954 for ( ; it != et; ++it) {
955 const BreakpointParameters &data = it->data;
956 if (data.functionName == functionName
957 && data.condition.isEmpty()
958 && data.ignoreCount == 0)
961 BreakpointParameters data(BreakpointByFunction);
962 data.functionName = functionName;
963 appendBreakpoint(data);
966 QIcon BreakHandler::icon(BreakpointId id) const
968 ConstIterator it = m_storage.find(id);
969 QTC_ASSERT(it != m_storage.end(),
970 qDebug() << "NO ICON FOR ID" << id;
971 return pendingBreakpointIcon());
975 void BreakHandler::scheduleSynchronization()
977 if (m_syncTimerId == -1)
978 m_syncTimerId = startTimer(10);
981 void BreakHandler::timerEvent(QTimerEvent *event)
983 QTC_ASSERT(event->timerId() == m_syncTimerId, return);
984 killTimer(m_syncTimerId);
986 saveBreakpoints(); // FIXME: remove?
987 debuggerCore()->synchronizeBreakpoints();
990 void BreakHandler::gotoLocation(BreakpointId id) const
992 ConstIterator it = m_storage.find(id);
993 QTC_ASSERT(it != m_storage.end(), return);
994 DebuggerEngine *engine = debuggerCore()->currentEngine();
995 if (it->data.type == BreakpointByAddress) {
997 engine->gotoLocation(it->data.address);
1000 engine->gotoLocation(
1001 Location(it->markerFileName(), it->markerLineNumber(), false));
1005 void BreakHandler::updateLineNumberFromMarker(BreakpointId id, int lineNumber)
1007 Iterator it = m_storage.find(id);
1008 it->response.pending = false;
1009 QTC_ASSERT(it != m_storage.end(), return);
1010 if (it->response.lineNumber != lineNumber) {
1011 // FIXME: Should we tell gdb about the change?
1012 it->response.lineNumber = lineNumber;
1014 // Ignore updates to the "real" line number while the debugger is
1015 // running, as this can be triggered by moving the breakpoint to
1016 // the next line that generated code.
1017 if (it->response.number == 0) {
1018 // FIXME: Do we need yet another data member?
1019 it->data.lineNumber = lineNumber;
1022 emit layoutChanged();
1025 BreakpointIds BreakHandler::allBreakpointIds() const
1028 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
1029 for ( ; it != et; ++it)
1030 ids.append(it.key());
1034 BreakpointIds BreakHandler::unclaimedBreakpointIds() const
1036 return engineBreakpointIds(0);
1039 BreakpointIds BreakHandler::engineBreakpointIds(DebuggerEngine *engine) const
1042 ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
1043 for ( ; it != et; ++it)
1044 if (it->engine == engine)
1045 ids.append(it.key());
1049 void BreakHandler::cleanupBreakpoint(BreakpointId id)
1051 QTC_ASSERT(state(id) == BreakpointDead, qDebug() << state(id));
1052 BreakpointItem item = m_storage.take(id);
1053 item.destroyMarker();
1057 const BreakpointResponse &BreakHandler::response(BreakpointId id) const
1059 static BreakpointResponse dummy;
1060 ConstIterator it = m_storage.find(id);
1061 QTC_ASSERT(it != m_storage.end(), qDebug() << id; return dummy);
1062 return it->response;
1065 bool BreakHandler::needsChange(BreakpointId id) const
1067 ConstIterator it = m_storage.find(id);
1068 QTC_ASSERT(it != m_storage.end(), return false);
1069 return it->needsChange();
1072 void BreakHandler::setResponse(BreakpointId id,
1073 const BreakpointResponse &response, bool takeOver)
1075 Iterator it = m_storage.find(id);
1076 QTC_ASSERT(it != m_storage.end(), return);
1077 BreakpointItem &item = it.value();
1078 item.response = response;
1079 item.destroyMarker();
1080 // Take over corrected values from response.
1082 if (item.data.type == BreakpointByFileAndLine
1083 && response.correctedLineNumber > 0)
1084 item.data.lineNumber = response.correctedLineNumber;
1085 if ((item.data.type == BreakpointByFileAndLine
1086 || item.data.type == BreakpointByFunction)
1087 && !response.module.isEmpty())
1088 item.data.module = response.module;
1093 void BreakHandler::setBreakpointData(BreakpointId id,
1094 const BreakpointParameters &data)
1096 Iterator it = m_storage.find(id);
1097 QTC_ASSERT(it != m_storage.end(), return);
1098 if (data == it->data)
1101 if (it->needsChange() && it->engine && it->state != BreakpointNew) {
1102 setState(id, BreakpointChangeRequested);
1103 scheduleSynchronization();
1105 it->destroyMarker();
1111 //////////////////////////////////////////////////////////////////
1115 //////////////////////////////////////////////////////////////////
1117 BreakHandler::BreakpointItem::BreakpointItem()
1118 : state(BreakpointNew), engine(0), marker(0)
1121 void BreakHandler::BreakpointItem::destroyMarker()
1123 BreakpointMarker *m = marker;
1128 QString BreakHandler::BreakpointItem::markerFileName() const
1130 // Some heuristics to find a "good" file name.
1131 if (!data.fileName.isEmpty()) {
1132 QFileInfo fi(data.fileName);
1134 return fi.absoluteFilePath();
1136 if (!response.fileName.isEmpty()) {
1137 QFileInfo fi(response.fileName);
1139 return fi.absoluteFilePath();
1141 if (response.fileName.endsWith(data.fileName))
1142 return response.fileName;
1143 if (data.fileName.endsWith(response.fileName))
1144 return data.fileName;
1145 return response.fileName.size() > data.fileName.size()
1146 ? response.fileName : data.fileName;
1150 int BreakHandler::BreakpointItem::markerLineNumber() const
1152 return response.lineNumber ? response.lineNumber : data.lineNumber;
1155 static void formatAddress(QTextStream &str, quint64 address)
1159 str.setIntegerBase(16);
1161 str.setIntegerBase(10);
1165 bool BreakHandler::BreakpointItem::needsChange() const
1167 if (!data.conditionsMatch(response.condition))
1169 if (data.ignoreCount != response.ignoreCount)
1171 if (data.enabled != response.enabled)
1173 if (data.threadSpec != response.threadSpec)
1175 if (data.command != response.command)
1180 bool BreakHandler::BreakpointItem::isLocatedAt
1181 (const QString &fileName, int lineNumber, bool useMarkerPosition) const
1183 int line = useMarkerPosition ? response.lineNumber : data.lineNumber;
1184 return lineNumber == line
1185 && (fileNameMatch(fileName, response.fileName)
1186 || fileNameMatch(fileName, markerFileName()));
1189 QIcon BreakHandler::BreakpointItem::icon() const
1191 // FIXME: This seems to be called on each cursor blink as soon as the
1192 // cursor is near a line with a breakpoint marker (+/- 2 lines or so).
1193 if (data.isTracepoint())
1194 return BreakHandler::tracepointIcon();
1195 if (data.type == Watchpoint)
1196 return BreakHandler::watchpointIcon();
1198 return BreakHandler::disabledBreakpointIcon();
1199 if (state == BreakpointInserted)
1200 return BreakHandler::breakpointIcon();
1201 return BreakHandler::pendingBreakpointIcon();
1204 QString BreakHandler::BreakpointItem::toToolTip() const
1207 QTextStream str(&rc);
1208 str << "<html><body><table>"
1209 //<< "<tr><td>" << tr("Id:") << "</td><td>" << m_id << "</td></tr>"
1210 << "<tr><td>" << tr("State:")
1211 << "</td><td>" << (data.enabled ? tr("Enabled") : tr("Disabled"));
1212 if (response.pending)
1213 str << tr(", pending");
1214 str << ", " << state << " (" << stateToString(state) << ")</td></tr>";
1216 str << "<tr><td>" << tr("Engine:")
1217 << "</td><td>" << engine->objectName() << "</td></tr>";
1219 if (!response.pending) {
1220 str << "<tr><td>" << tr("Breakpoint Number:")
1221 << "</td><td>" << response.number << "</td></tr>";
1223 str << "<tr><td>" << tr("Breakpoint Type:")
1224 << "</td><td>" << typeToString(data.type) << "</td></tr>";
1225 if (!response.extra.isEmpty()) {
1226 str << "<tr><td>" << tr("Extra Information:")
1227 << "</td><td>" << response.extra << "</td></tr>"; }
1228 str << "<tr><td>" << tr("Marker File:")
1229 << "</td><td>" << QDir::toNativeSeparators(markerFileName()) << "</td></tr>"
1230 << "<tr><td>" << tr("Marker Line:")
1231 << "</td><td>" << markerLineNumber() << "</td></tr>"
1232 << "</table><br><hr><table>"
1233 << "<tr><th>" << tr("Property")
1234 << "</th><th>" << tr("Requested")
1235 << "</th><th>" << tr("Obtained") << "</th></tr>"
1236 << "<tr><td>" << tr("Internal Number:")
1237 << "</td><td>—</td><td>" << response.number << "</td></tr>";
1238 if (data.type == BreakpointByFunction) {
1239 str << "<tr><td>" << tr("Function Name:")
1240 << "</td><td>" << data.functionName
1241 << "</td><td>" << response.functionName
1244 if (data.type == BreakpointByFileAndLine) {
1245 str << "<tr><td>" << tr("File Name:")
1246 << "</td><td>" << QDir::toNativeSeparators(data.fileName)
1247 << "</td><td>" << QDir::toNativeSeparators(response.fileName)
1249 << "<tr><td>" << tr("Line Number:")
1250 << "</td><td>" << data.lineNumber
1251 << "</td><td>" << response.lineNumber << "</td></tr>"
1252 << "<tr><td>" << tr("Corrected Line Number:")
1254 << "</td><td>" << response.correctedLineNumber << "</td></tr>";
1256 if (data.type == BreakpointByFunction || data.type == BreakpointByFileAndLine) {
1257 str << "<tr><td>" << tr("Module:")
1258 << "</td><td>" << data.module
1259 << "</td><td>" << response.module
1262 str << "<tr><td>" << tr("Breakpoint Address:")
1264 formatAddress(str, data.address);
1266 formatAddress(str, response.address);
1267 str << "</td></tr>";
1268 if (response.multiple) {
1269 str << "<tr><td>" << tr("Multiple Addresses:")
1271 foreach (quint64 address, response.addresses) {
1272 formatAddress(str, address);
1275 str << "</td></tr>";
1277 if (!data.command.isEmpty() || !response.command.isEmpty()) {
1278 str << "<tr><td>" << tr("Command:")
1279 << "</td><td>" << data.command
1280 << "</td><td>" << response.command
1283 if (!data.condition.isEmpty() || !response.condition.isEmpty()) {
1284 str << "<tr><td>" << tr("Condition:")
1285 << "</td><td>" << data.condition
1286 << "</td><td>" << response.condition
1289 if (data.ignoreCount || response.ignoreCount) {
1290 str << "<tr><td>" << tr("Ignore Count:") << "</td><td>";
1291 if (data.ignoreCount)
1292 str << data.ignoreCount;
1294 if (response.ignoreCount)
1295 str << response.ignoreCount;
1296 str << "</td></tr>";
1298 if (data.threadSpec >= 0 || response.threadSpec >= 0) {
1299 str << "<tr><td>" << tr("Thread Specification:")
1301 if (data.threadSpec >= 0)
1302 str << data.threadSpec;
1304 if (response.threadSpec >= 0)
1305 str << response.threadSpec;
1306 str << "</td></tr>";
1308 str << "</table></body></html>";
1312 } // namespace Internal
1313 } // namespace Debugger