OSDN Git Service

2c1fa88874e19b616be43de5ace0041bdbe483f2
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / breakhandler.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "breakhandler.h"
35 #include "breakpointmarker.h"
36
37 #include "debuggeractions.h"
38 #include "debuggercore.h"
39 #include "debuggerengine.h"
40 #include "debuggerstringutils.h"
41 #include "stackframe.h"
42
43 #include <utils/qtcassert.h>
44
45 #include <QtCore/QDir>
46 #include <QtCore/QFileInfo>
47 #include <QtCore/QTimerEvent>
48
49
50 //////////////////////////////////////////////////////////////////
51 //
52 // BreakHandler
53 //
54 //////////////////////////////////////////////////////////////////
55
56 namespace Debugger {
57 namespace Internal {
58
59 static QString stateToString(BreakpointState state)
60 {
61     switch (state) {
62         case BreakpointNew:
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");
78         case BreakpointDead:
79             return BreakHandler::tr("Dead");
80         default:
81             break;
82     }
83     //: Invalid breakpoint state.
84     return BreakHandler::tr("<invalid state>");
85 }
86
87 static QString msgBreakpointAtSpecialFunc(const char *func)
88 {
89     return BreakHandler::tr("Breakpoint at \"%1\"").arg(QString::fromAscii(func));
90 }
91
92 static QString typeToString(BreakpointType type)
93 {
94     switch (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()\"");
115         case Watchpoint:
116             return BreakHandler::tr("Watchpoint");
117         case UnknownType:
118             break;
119     }
120     return BreakHandler::tr("Unknown Breakpoint Type");
121 }
122
123 BreakHandler::BreakHandler()
124   : m_syncTimerId(-1)
125 {}
126
127 BreakHandler::~BreakHandler()
128 {}
129
130 QIcon BreakHandler::breakpointIcon()
131 {
132     static QIcon icon(_(":/debugger/images/breakpoint_16.png"));
133     return icon;
134 }
135
136 QIcon BreakHandler::disabledBreakpointIcon()
137 {
138     static QIcon icon(_(":/debugger/images/breakpoint_disabled_16.png"));
139     return icon;
140 }
141
142 QIcon BreakHandler::pendingBreakpointIcon()
143 {
144     static QIcon icon(_(":/debugger/images/breakpoint_pending_16.png"));
145     return icon;
146 }
147
148 QIcon BreakHandler::watchpointIcon()
149 {
150     static QIcon icon(_(":/debugger/images/watchpoint.png"));
151     return icon;
152 }
153
154 QIcon BreakHandler::tracepointIcon()
155 {
156     static QIcon icon(_(":/debugger/images/tracepoint.png"));
157     return icon;
158 }
159
160 QIcon BreakHandler::emptyIcon()
161 {
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"));
165     return icon;
166 }
167
168 int BreakHandler::columnCount(const QModelIndex &parent) const
169 {
170     return parent.isValid() ? 0 : 8;
171 }
172
173 int BreakHandler::rowCount(const QModelIndex &parent) const
174 {
175     return parent.isValid() ? 0 : m_storage.size();
176 }
177
178 static inline bool fileNameMatch(const QString &f1, const QString &f2)
179 {
180 #ifdef Q_OS_WIN
181     return f1.compare(f2, Qt::CaseInsensitive) == 0;
182 #else
183     return f1 == f2;
184 #endif
185 }
186
187 static bool isSimilarTo(const BreakpointParameters &data, const BreakpointResponse &needle)
188 {
189     // Clear hit.
190     // Clear miss.
191     if (needle.type != UnknownType && data.type != UnknownType
192             && data.type != needle.type)
193         return false;
194
195     // Clear hit.
196     if (data.address && data.address == needle.address)
197         return true;
198
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)
204         return true;
205
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)
211         return true;
212
213     return false;
214 }
215
216 BreakpointId BreakHandler::findSimilarBreakpoint(const BreakpointResponse &needle) const
217 {
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)
226             return id;
227
228         if (isSimilarTo(data, needle))
229             return id;
230     }
231     return BreakpointId();
232 }
233
234 BreakpointId BreakHandler::findBreakpointByNumber(int bpNumber) const
235 {
236     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
237     for ( ; it != et; ++it)
238         if (it->response.number == bpNumber)
239             return it.key();
240     return BreakpointId();
241 }
242
243 BreakpointId BreakHandler::findBreakpointByFunction(const QString &functionName) const
244 {
245     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
246     for ( ; it != et; ++it)
247         if (it->data.functionName == functionName)
248             return it.key();
249     return BreakpointId();
250 }
251
252 BreakpointId BreakHandler::findBreakpointByAddress(quint64 address) const
253 {
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)
257             return it.key();
258     return BreakpointId();
259 }
260
261 BreakpointId BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
262     int lineNumber, bool useMarkerPosition)
263 {
264     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
265     for ( ; it != et; ++it)
266         if (it->isLocatedAt(fileName, lineNumber, useMarkerPosition))
267             return it.key();
268     return BreakpointId();
269 }
270
271 const BreakpointParameters &BreakHandler::breakpointData(BreakpointId id) const
272 {
273     static BreakpointParameters dummy;
274     ConstIterator it = m_storage.find(id);
275     QTC_ASSERT(it != m_storage.end(), return dummy);
276     return it->data;
277 }
278
279 BreakpointId BreakHandler::findWatchpoint(const BreakpointParameters &data) const
280 {
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)
287             return it.key();
288     return BreakpointId();
289 }
290
291 void BreakHandler::saveBreakpoints()
292 {
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())
303             continue;
304         if (data.type != BreakpointByFileAndLine)
305             map.insert(_("type"), data.type);
306         if (!data.fileName.isEmpty())
307             map.insert(_("filename"), data.fileName);
308         if (data.lineNumber)
309             map.insert(_("linenumber"), data.lineNumber);
310         if (!data.functionName.isEmpty())
311             map.insert(_("funcname"), data.functionName);
312         if (data.address)
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);
320         if (!data.enabled)
321             map.insert(_("disabled"), one);
322         if (data.pathUsage != BreakpointPathUsageEngineDefault)
323             map.insert(_("usefullpath"), QString::number(data.pathUsage));
324         if (data.tracepoint)
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);
330         list.append(map);
331     }
332     debuggerCore()->setSessionValue("Breakpoints", list);
333     //qDebug() << "SAVED BREAKPOINTS" << this << list.size();
334 }
335
336 void BreakHandler::loadBreakpoints()
337 {
338     QTC_ASSERT(debuggerCore(), return);
339     //qDebug() << "LOADING BREAKPOINTS...";
340     QVariant value = debuggerCore()->sessionValue("Breakpoints");
341     QList<QVariant> list = value.toList();
342     //clear();
343     foreach (const QVariant &var, list) {
344         const QMap<QString, QVariant> map = var.toMap();
345         BreakpointParameters data(BreakpointByFileAndLine);
346         QVariant v = map.value(_("filename"));
347         if (v.isValid())
348             data.fileName = v.toString();
349         v = map.value(_("linenumber"));
350         if (v.isValid())
351             data.lineNumber = v.toString().toInt();
352         v = map.value(_("condition"));
353         if (v.isValid())
354             data.condition = v.toString().toLatin1();
355         v = map.value(_("address"));
356         if (v.isValid())
357             data.address = v.toString().toULongLong();
358         v = map.value(_("ignorecount"));
359         if (v.isValid())
360             data.ignoreCount = v.toString().toInt();
361         v = map.value(_("threadspec"));
362         if (v.isValid())
363             data.threadSpec = v.toString().toInt();
364         v = map.value(_("funcname"));
365         if (v.isValid())
366             data.functionName = v.toString();
367         v = map.value(_("disabled"));
368         if (v.isValid())
369             data.enabled = !v.toInt();
370         v = map.value(_("usefullpath"));
371         if (v.isValid())
372             data.pathUsage = static_cast<BreakpointPathUsage>(v.toInt());
373         v = map.value(_("tracepoint"));
374         if (v.isValid())
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"));
380         if (v.isValid())
381             data.module = v.toString();
382         v = map.value(_("command"));
383         if (v.isValid())
384             data.command = v.toString();
385         appendBreakpoint(data);
386     }
387     //qDebug() << "LOADED BREAKPOINTS" << this << list.size();
388 }
389
390 void BreakHandler::updateMarkers()
391 {
392     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
393     for ( ; it != et; ++it)
394         updateMarker(it.key());
395 }
396
397 void BreakHandler::updateMarker(BreakpointId id)
398 {
399     Iterator it = m_storage.find(id);
400     QTC_ASSERT(it != m_storage.end(), return);
401
402     QString markerFileName = it->markerFileName();
403     int markerLineNumber = it->markerLineNumber();
404     if (it->marker && (markerFileName != it->marker->fileName()
405                 || markerLineNumber != it->marker->lineNumber()))
406         it->destroyMarker();
407
408     if (!it->marker && !markerFileName.isEmpty() && markerLineNumber > 0)
409         it->marker = new BreakpointMarker(id, markerFileName, markerLineNumber);
410 }
411
412 QVariant BreakHandler::headerData(int section,
413     Qt::Orientation orientation, int role) const
414 {
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")
419         };
420         return headers[section];
421     }
422     return QVariant();
423 }
424
425 BreakpointId BreakHandler::findBreakpointByIndex(const QModelIndex &index) const
426 {
427     int r = index.row();
428     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
429     for (int i = 0; it != et; ++it, ++i)
430         if (i == r)
431             return it.key();
432     return BreakpointId();
433 }
434
435 BreakpointIds BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
436 {
437     QSet<BreakpointId> ids;
438     foreach (const QModelIndex &index, list)
439         ids.insert(findBreakpointByIndex(index));
440     return ids.toList();
441 }
442
443 Qt::ItemFlags BreakHandler::flags(const QModelIndex &index) const
444 {
445 //    switch (index.column()) {
446 //        //case 0:
447 //        //    return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
448 //        default:
449             return QAbstractTableModel::flags(index);
450 //    }
451 }
452
453 QString BreakHandler::displayFromThreadSpec(int spec)
454 {
455     return spec == -1 ? BreakHandler::tr("(all)") : QString::number(spec);
456 }
457
458 int BreakHandler::threadSpecFromDisplay(const QString &str)
459 {
460     bool ok = false;
461     int result = str.toInt(&ok);
462     return ok ? result : -1;
463 }
464
465 QVariant BreakHandler::data(const QModelIndex &mi, int role) const
466 {
467     static const QString empty = QString(QLatin1Char('-'));
468
469     if (!mi.isValid())
470         return QVariant();
471
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;
478
479     bool orig = false;
480     switch (it->state) {
481         case BreakpointInsertRequested:
482         case BreakpointInsertProceeding:
483         case BreakpointChangeRequested:
484         case BreakpointChangeProceeding:
485         case BreakpointInserted:
486         case BreakpointRemoveRequested:
487         case BreakpointRemoveProceeding:
488             break;
489         case BreakpointNew:
490         case BreakpointDead:
491             orig = true;
492             break;
493     };
494
495     switch (mi.column()) {
496         case 0:
497             if (role == Qt::DisplayRole) {
498                 return QString::number(id);
499                 //return QString("%1 - %2").arg(id).arg(response.number);
500             }
501             if (role == Qt::DecorationRole)
502                 return it->icon();
503             break;
504         case 1:
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);
520                 return empty;
521             }
522             break;
523         case 2:
524             if (role == Qt::DisplayRole) {
525                 QString str;
526                 if (!response.fileName.isEmpty())
527                     str = response.fileName;
528                 if (str.isEmpty() && !data.fileName.isEmpty())
529                     str = data.fileName;
530                 if (str.isEmpty()) {
531                     QString s = QFileInfo(str).fileName();
532                     if (!s.isEmpty())
533                         str = s;
534                 }
535                 // FIXME: better?
536                 //if (data.multiple && str.isEmpty() && !response.fileName.isEmpty())
537                 //    str = response.fileName;
538                 if (!str.isEmpty())
539                     return QDir::toNativeSeparators(str);
540                 return empty;
541             }
542             break;
543         case 3:
544             if (role == Qt::DisplayRole) {
545                 if (response.lineNumber > 0)
546                     return response.lineNumber;
547                 if (data.lineNumber > 0)
548                     return data.lineNumber;
549                 return empty;
550             }
551             if (role == Qt::UserRole + 1)
552                 return data.lineNumber;
553             break;
554         case 4:
555             if (role == Qt::DisplayRole) {
556                 QString displayValue;
557                 const quint64 address = orig ? data.address : response.address;
558                 if (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);
564                 }
565                 return displayValue;
566             }
567             break;
568         case 5:
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;
575             break;
576         case 6:
577             if (role == Qt::DisplayRole) {
578                 const int ignoreCount =
579                     orig ? data.ignoreCount : response.ignoreCount;
580                 return ignoreCount ? QVariant(ignoreCount) : QVariant(QString());
581             }
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;
586             break;
587         case 7:
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);
594             break;
595     }
596     switch (role) {
597     case Qt::ToolTipRole:
598         if (debuggerCore()->boolSetting(UseToolTipsInBreakpointsView))
599                 return QVariant(it->toToolTip());
600         break;
601     case EngineCapabilitiesRole:  {
602         const unsigned caps = it.value().engine ?
603                               it.value().engine->debuggerCapabilities() :
604                               unsigned(AllDebuggerCapabilities);
605         return QVariant(caps);
606     }
607     }
608     return QVariant();
609 }
610
611 #define GETTER(type, getter) \
612 type BreakHandler::getter(BreakpointId id) const \
613 { \
614     ConstIterator it = m_storage.find(id); \
615     QTC_ASSERT(it != m_storage.end(), \
616         qDebug() << "ID" << id << "NOT KNOWN"; \
617         return type()); \
618     return it->data.getter; \
619 }
620
621 #define SETTER(type, getter, setter) \
622 void BreakHandler::setter(BreakpointId id, const type &value) \
623 { \
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) \
628         return; \
629     it->data.getter = value; \
630     if (it->state != BreakpointNew) { \
631         it->state = BreakpointChangeRequested; \
632         scheduleSynchronization(); \
633     } \
634 }
635
636 #define PROPERTY(type, getter, setter) \
637     GETTER(type, getter) \
638     SETTER(type, getter, setter)
639
640
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)
650
651 bool BreakHandler::isEnabled(BreakpointId id) const
652 {
653     ConstIterator it = m_storage.find(id);
654     QTC_ASSERT(it != m_storage.end(), return false);
655     return it->data.enabled;
656 }
657
658 void BreakHandler::setEnabled(BreakpointId id, bool on)
659 {
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)
664         return;
665     it->data.enabled = on;
666     it->destroyMarker();
667     updateMarker(id);
668     if (it->engine) {
669         it->state = BreakpointChangeRequested;
670         scheduleSynchronization();
671     }
672 }
673
674 bool BreakHandler::isTracepoint(BreakpointId id) const
675 {
676     ConstIterator it = m_storage.find(id);
677     QTC_ASSERT(it != m_storage.end(), return false);
678     return it->data.tracepoint;
679 }
680
681 void BreakHandler::setTracepoint(BreakpointId id, bool on)
682 {
683     Iterator it = m_storage.find(id);
684     QTC_ASSERT(it != m_storage.end(), return);
685     if (it->data.tracepoint == on)
686         return;
687     it->data.tracepoint = on;
688     it->destroyMarker();
689
690     updateMarker(id);
691     if (it->engine) {
692         it->state = BreakpointChangeRequested;
693         scheduleSynchronization();
694     }
695 }
696
697 void BreakHandler::setMarkerFileAndLine(BreakpointId id,
698     const QString &fileName, int lineNumber)
699 {
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)
703         return;
704     it->response.fileName = fileName;
705     it->response.lineNumber = lineNumber;
706     it->destroyMarker();
707     updateMarker(id);
708     emit layoutChanged();
709 }
710
711 BreakpointState BreakHandler::state(BreakpointId id) const
712 {
713     ConstIterator it = m_storage.find(id);
714     QTC_ASSERT(it != m_storage.end(), qDebug() << id; return BreakpointDead);
715     return it->state;
716 }
717
718 DebuggerEngine *BreakHandler::engine(BreakpointId id) const
719 {
720     ConstIterator it = m_storage.find(id);
721     QTC_ASSERT(it != m_storage.end(), qDebug() << id; return 0);
722     return it->engine;
723 }
724
725 void BreakHandler::setEngine(BreakpointId id, DebuggerEngine *value)
726 {
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);
731     it->engine = value;
732     it->state = BreakpointInsertRequested;
733     it->response = BreakpointResponse();
734     it->response.fileName = it->data.fileName;
735     updateMarker(id);
736     scheduleSynchronization();
737 }
738
739 static bool isAllowedTransition(BreakpointState from, BreakpointState to)
740 {
741     switch (from) {
742     case BreakpointNew:
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;
762     case BreakpointDead:
763         return false;
764     }
765     qDebug() << "UNKNOWN BREAKPOINT STATE:" << from;
766     return false;
767 }
768
769 void BreakHandler::setState(BreakpointId id, BreakpointState state)
770 {
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);
777
778     if (it->state == state) {
779         qDebug() << "STATE UNCHANGED: " << id << state;
780         return;
781     }
782
783     it->state = state;
784     layoutChanged();
785 }
786
787 void BreakHandler::notifyBreakpointChangeAfterInsertNeeded(BreakpointId id)
788 {
789     QTC_ASSERT(state(id) == BreakpointInsertProceeding, qDebug() << state(id));
790     setState(id, BreakpointChangeRequested);
791 }
792
793 void BreakHandler::notifyBreakpointInsertProceeding(BreakpointId id)
794 {
795     QTC_ASSERT(state(id) == BreakpointInsertRequested, qDebug() << state(id));
796     setState(id, BreakpointInsertProceeding);
797 }
798
799 void BreakHandler::notifyBreakpointInsertOk(BreakpointId id)
800 {
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);
805 }
806
807 void BreakHandler::notifyBreakpointInsertFailed(BreakpointId id)
808 {
809     QTC_ASSERT(state(id) == BreakpointInsertProceeding, qDebug() << state(id));
810     setState(id, BreakpointDead);
811 }
812
813 void BreakHandler::notifyBreakpointRemoveProceeding(BreakpointId id)
814 {
815     QTC_ASSERT(state(id) == BreakpointRemoveRequested, qDebug() << state(id));
816     setState(id, BreakpointRemoveProceeding);
817 }
818
819 void BreakHandler::notifyBreakpointRemoveOk(BreakpointId id)
820 {
821     QTC_ASSERT(state(id) == BreakpointRemoveProceeding, qDebug() << state(id));
822     setState(id, BreakpointDead);
823     cleanupBreakpoint(id);
824 }
825
826 void BreakHandler::notifyBreakpointRemoveFailed(BreakpointId id)
827 {
828     QTC_ASSERT(state(id) == BreakpointRemoveProceeding, qDebug() << state(id));
829     setState(id, BreakpointDead);
830     cleanupBreakpoint(id);
831 }
832
833 void BreakHandler::notifyBreakpointChangeProceeding(BreakpointId id)
834 {
835     QTC_ASSERT(state(id) == BreakpointChangeRequested, qDebug() << state(id));
836     setState(id, BreakpointChangeProceeding);
837 }
838
839 void BreakHandler::notifyBreakpointChangeOk(BreakpointId id)
840 {
841     QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
842     setState(id, BreakpointInserted);
843 }
844
845 void BreakHandler::notifyBreakpointChangeFailed(BreakpointId id)
846 {
847     QTC_ASSERT(state(id) == BreakpointChangeProceeding, qDebug() << state(id));
848     setState(id, BreakpointDead);
849 }
850
851 void BreakHandler::notifyBreakpointReleased(BreakpointId id)
852 {
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;
857     it->engine = 0;
858     it->response = BreakpointResponse();
859     delete it->marker;
860     it->marker = 0;
861     updateMarker(id);
862     layoutChanged();
863 }
864
865 void BreakHandler::notifyBreakpointAdjusted(BreakpointId id,
866         const BreakpointParameters &data)
867 {
868     QTC_ASSERT(state(id) == BreakpointInserted, qDebug() << state(id));
869     Iterator it = m_storage.find(id);
870     QTC_ASSERT(it != m_storage.end(), return);
871     it->data = data;
872     //if (it->needsChange())
873     //    setState(id, BreakpointChangeRequested);
874 }
875
876 void BreakHandler::notifyBreakpointNeedsReinsertion(BreakpointId id)
877 {
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;
882 }
883
884 void BreakHandler::removeBreakpoint(BreakpointId id)
885 {
886     Iterator it = m_storage.find(id);
887     QTC_ASSERT(it != m_storage.end(), return);
888     switch (it->state) {
889     case BreakpointInserted:
890         setState(id, BreakpointRemoveRequested);
891         scheduleSynchronization();
892         break;
893     case BreakpointNew:
894         it->state = BreakpointDead;
895         cleanupBreakpoint(id);
896         break;
897     default:
898         qWarning("Warning: Cannot remove breakpoint %llu in state '%s'.",
899                id, qPrintable(stateToString(it->state)));
900         it->state = BreakpointRemoveRequested;
901         break;
902     }
903 }
904
905 void BreakHandler::appendBreakpoint(const BreakpointParameters &data)
906 {
907     QTC_ASSERT(data.type != UnknownType, return);
908
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;
912
913     BreakpointId id(++currentId);
914     BreakpointItem item;
915     item.data = data;
916     item.response.fileName = data.fileName;
917     item.response.lineNumber = data.lineNumber;
918
919     const int row = m_storage.size();
920     beginInsertRows(QModelIndex(), row, row);
921     m_storage.insert(id, item);
922     endInsertRows();
923
924     updateMarker(id);
925     scheduleSynchronization();
926
927 }
928
929 void BreakHandler::saveSessionData()
930 {
931     saveBreakpoints();
932 }
933
934 void BreakHandler::loadSessionData()
935 {
936     m_storage.clear();
937     loadBreakpoints();
938 }
939
940 void BreakHandler::removeSessionData()
941 {
942     Iterator it = m_storage.begin(), et = m_storage.end();
943     for ( ; it != et; ++it)
944         it->destroyMarker();
945     m_storage.clear();
946     layoutChanged();
947 }
948
949 void BreakHandler::breakByFunction(const QString &functionName)
950 {
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)
959             return;
960     }
961     BreakpointParameters data(BreakpointByFunction);
962     data.functionName = functionName;
963     appendBreakpoint(data);
964 }
965
966 QIcon BreakHandler::icon(BreakpointId id) const
967 {
968     ConstIterator it = m_storage.find(id);
969     QTC_ASSERT(it != m_storage.end(),
970         qDebug() << "NO ICON FOR ID" << id;
971         return pendingBreakpointIcon());
972     return it->icon();
973 }
974
975 void BreakHandler::scheduleSynchronization()
976 {
977     if (m_syncTimerId == -1)
978         m_syncTimerId = startTimer(10);
979 }
980
981 void BreakHandler::timerEvent(QTimerEvent *event)
982 {
983     QTC_ASSERT(event->timerId() == m_syncTimerId, return);
984     killTimer(m_syncTimerId);
985     m_syncTimerId = -1;
986     saveBreakpoints();  // FIXME: remove?
987     debuggerCore()->synchronizeBreakpoints();
988 }
989
990 void BreakHandler::gotoLocation(BreakpointId id) const
991 {
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) {
996         if (engine)
997             engine->gotoLocation(it->data.address);
998     } else {
999         if (engine)
1000             engine->gotoLocation(
1001                 Location(it->markerFileName(), it->markerLineNumber(), false));
1002     }
1003 }
1004
1005 void BreakHandler::updateLineNumberFromMarker(BreakpointId id, int lineNumber)
1006 {
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;
1013     }
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;
1020     }
1021     updateMarker(id);
1022     emit layoutChanged();
1023 }
1024
1025 BreakpointIds BreakHandler::allBreakpointIds() const
1026 {
1027     BreakpointIds ids;
1028     ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
1029     for ( ; it != et; ++it)
1030         ids.append(it.key());
1031     return ids;
1032 }
1033
1034 BreakpointIds BreakHandler::unclaimedBreakpointIds() const
1035 {
1036     return engineBreakpointIds(0);
1037 }
1038
1039 BreakpointIds BreakHandler::engineBreakpointIds(DebuggerEngine *engine) const
1040 {
1041     BreakpointIds ids;
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());
1046     return ids;
1047 }
1048
1049 void BreakHandler::cleanupBreakpoint(BreakpointId id)
1050 {
1051     QTC_ASSERT(state(id) == BreakpointDead, qDebug() << state(id));
1052     BreakpointItem item = m_storage.take(id);
1053     item.destroyMarker();
1054     layoutChanged();
1055 }
1056
1057 const BreakpointResponse &BreakHandler::response(BreakpointId id) const
1058 {
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;
1063 }
1064
1065 bool BreakHandler::needsChange(BreakpointId id) const
1066 {
1067     ConstIterator it = m_storage.find(id);
1068     QTC_ASSERT(it != m_storage.end(), return false);
1069     return it->needsChange();
1070 }
1071
1072 void BreakHandler::setResponse(BreakpointId id,
1073     const BreakpointResponse &response, bool takeOver)
1074 {
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.
1081     if (takeOver) {
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;
1089     }
1090     updateMarker(id);
1091 }
1092
1093 void BreakHandler::setBreakpointData(BreakpointId id,
1094     const BreakpointParameters &data)
1095 {
1096     Iterator it = m_storage.find(id);
1097     QTC_ASSERT(it != m_storage.end(), return);
1098     if (data == it->data)
1099         return;
1100     it->data = data;
1101     if (it->needsChange() && it->engine && it->state != BreakpointNew) {
1102         setState(id, BreakpointChangeRequested);
1103         scheduleSynchronization();
1104     } else {
1105         it->destroyMarker();
1106         updateMarker(id);
1107         layoutChanged();
1108     }
1109 }
1110
1111 //////////////////////////////////////////////////////////////////
1112 //
1113 // Storage
1114 //
1115 //////////////////////////////////////////////////////////////////
1116
1117 BreakHandler::BreakpointItem::BreakpointItem()
1118   : state(BreakpointNew), engine(0), marker(0)
1119 {}
1120
1121 void BreakHandler::BreakpointItem::destroyMarker()
1122 {
1123     BreakpointMarker *m = marker;
1124     marker = 0;
1125     delete m;
1126 }
1127
1128 QString BreakHandler::BreakpointItem::markerFileName() const
1129 {
1130     // Some heuristics to find a "good" file name.
1131     if (!data.fileName.isEmpty()) {
1132         QFileInfo fi(data.fileName);
1133         if (fi.exists())
1134             return fi.absoluteFilePath();
1135     }
1136     if (!response.fileName.isEmpty()) {
1137         QFileInfo fi(response.fileName);
1138         if (fi.exists())
1139             return fi.absoluteFilePath();
1140     }
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;
1147 }
1148
1149
1150 int BreakHandler::BreakpointItem::markerLineNumber() const
1151 {
1152     return response.lineNumber ? response.lineNumber : data.lineNumber;
1153 }
1154
1155 static void formatAddress(QTextStream &str, quint64 address)
1156 {
1157     if (address) {
1158         str << "0x";
1159         str.setIntegerBase(16);
1160         str << address;
1161         str.setIntegerBase(10);
1162     }
1163 }
1164
1165 bool BreakHandler::BreakpointItem::needsChange() const
1166 {
1167     if (!data.conditionsMatch(response.condition))
1168         return true;
1169     if (data.ignoreCount != response.ignoreCount)
1170         return true;
1171     if (data.enabled != response.enabled)
1172         return true;
1173     if (data.threadSpec != response.threadSpec)
1174         return true;
1175     if (data.command != response.command)
1176         return true;
1177     return false;
1178 }
1179
1180 bool BreakHandler::BreakpointItem::isLocatedAt
1181     (const QString &fileName, int lineNumber, bool useMarkerPosition) const
1182 {
1183     int line = useMarkerPosition ? response.lineNumber : data.lineNumber;
1184     return lineNumber == line
1185         && (fileNameMatch(fileName, response.fileName)
1186             || fileNameMatch(fileName, markerFileName()));
1187 }
1188
1189 QIcon BreakHandler::BreakpointItem::icon() const
1190 {
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();
1197     if (!data.enabled)
1198         return BreakHandler::disabledBreakpointIcon();
1199     if (state == BreakpointInserted)
1200         return BreakHandler::breakpointIcon();
1201     return BreakHandler::pendingBreakpointIcon();
1202 }
1203
1204 QString BreakHandler::BreakpointItem::toToolTip() const
1205 {
1206     QString rc;
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>";
1215     if (engine) {
1216         str << "<tr><td>" << tr("Engine:")
1217         << "</td><td>" << engine->objectName() << "</td></tr>";
1218     }
1219     if (!response.pending) {
1220         str << "<tr><td>" << tr("Breakpoint Number:")
1221             << "</td><td>" << response.number << "</td></tr>";
1222     }
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>&mdash;</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
1242         << "</td></tr>";
1243     }
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)
1248         << "</td></tr>"
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:")
1253         << "</td><td>-"
1254         << "</td><td>" << response.correctedLineNumber << "</td></tr>";
1255     }
1256     if (data.type == BreakpointByFunction || data.type == BreakpointByFileAndLine) {
1257         str << "<tr><td>" << tr("Module:")
1258             << "</td><td>" << data.module
1259             << "</td><td>" << response.module
1260             << "</td></tr>";
1261     }
1262     str << "<tr><td>" << tr("Breakpoint Address:")
1263         << "</td><td>";
1264     formatAddress(str, data.address);
1265     str << "</td><td>";
1266     formatAddress(str, response.address);
1267     str << "</td></tr>";
1268     if (response.multiple) {
1269         str << "<tr><td>" << tr("Multiple Addresses:")
1270             << "</td><td>";
1271         foreach (quint64 address, response.addresses) {
1272             formatAddress(str, address);
1273             str << " ";
1274         }
1275         str << "</td></tr>";
1276     }
1277     if (!data.command.isEmpty() || !response.command.isEmpty()) {
1278         str << "<tr><td>" << tr("Command:")
1279             << "</td><td>" << data.command
1280             << "</td><td>" << response.command
1281             << "</td></tr>";
1282     }
1283     if (!data.condition.isEmpty() || !response.condition.isEmpty()) {
1284         str << "<tr><td>" << tr("Condition:")
1285             << "</td><td>" << data.condition
1286             << "</td><td>" << response.condition
1287             << "</td></tr>";
1288     }
1289     if (data.ignoreCount || response.ignoreCount) {
1290         str << "<tr><td>" << tr("Ignore Count:") << "</td><td>";
1291         if (data.ignoreCount)
1292             str << data.ignoreCount;
1293         str << "</td><td>";
1294         if (response.ignoreCount)
1295             str << response.ignoreCount;
1296         str << "</td></tr>";
1297     }
1298     if (data.threadSpec >= 0 || response.threadSpec >= 0) {
1299         str << "<tr><td>" << tr("Thread Specification:")
1300             << "</td><td>";
1301         if (data.threadSpec >= 0)
1302             str << data.threadSpec;
1303         str << "</td><td>";
1304         if (response.threadSpec >= 0)
1305             str << response.threadSpec;
1306         str << "</td></tr>";
1307     }
1308     str  << "</table></body></html>";
1309     return rc;
1310 }
1311
1312 } // namespace Internal
1313 } // namespace Debugger