OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / gdb / symbian.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 (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "symbian.h"
34 #include "registerhandler.h"
35 #include "threadshandler.h"
36 #include <trkutils.h>
37
38 #include <utils/qtcassert.h>
39
40 #include <QtCore/QDebug>
41 #include <QtCore/QTextStream>
42 #include <QtCore/QFileInfo>
43
44 namespace Debugger {
45 namespace Internal {
46
47 ///////////////////////////////////////////////////////////////////////////
48 //
49 // MemoryRange
50 //
51 ///////////////////////////////////////////////////////////////////////////
52
53 MemoryRange::MemoryRange(uint f, uint t)
54     : from(f), to(t)
55 {
56     QTC_ASSERT(f <= t, qDebug() << "F: " << f << " T: " << t);
57 }
58
59 bool MemoryRange::intersects(const MemoryRange &other) const
60 {
61     Q_UNUSED(other);
62     QTC_ASSERT(false, /**/);
63     return false; // FIXME
64 }
65
66 void MemoryRange::operator-=(const MemoryRange &other)
67 {
68     if (from == 0 && to == 0)
69         return;
70     MEMORY_DEBUG("      SUB: "  << *this << " - " << other);
71     if (other.from <= from && to <= other.to) {
72         from = to = 0;
73         return;
74     }
75     if (other.from <= from && other.to <= to) {
76         from = qMax(from, other.to);
77         return;
78     }
79     if (from <= other.from && to <= other.to) {
80         to = qMin(other.from, to);
81         return;
82     }
83     // This would split the range.
84     QTC_ASSERT(false, qDebug() << "Memory::operator-() not handled for: "
85         << *this << " - " << other);
86 }
87
88 QDebug operator<<(QDebug d, const MemoryRange &range)
89 {
90     return d << QString("[%1,%2] (size %3) ")
91         .arg(range.from, 0, 16).arg(range.to, 0, 16).arg(range.size());
92 }
93
94 namespace Symbian {
95
96 static const char *registerNames[KnownRegisters] =
97 {
98     "A1", "A2", "A3", "A4",
99     0, 0, 0, 0,
100     0, 0, 0, "AP",
101     "IP", "SP", "LR", "PC",
102     "PSTrk", 0, 0, 0,
103     0, 0, 0, 0,
104     0, "PSGdb"
105 };
106
107 const char *registerName(int i)
108 {
109     return registerNames[i];
110 }
111
112 QByteArray dumpRegister(uint n, uint value)
113 {
114     QByteArray ba;
115     ba += ' ';
116     if (n < KnownRegisters && registerNames[n]) {
117         ba += registerNames[n];
118     } else {
119         ba += '#';
120         ba += QByteArray::number(n);
121     }
122     ba += '=';
123     ba += trk::hexxNumber(value);
124     return ba;
125 }
126
127 ///////////////////////////////////////////////////////////////////////////
128 //
129 // Thread
130 //
131 ///////////////////////////////////////////////////////////////////////////
132
133 Thread::Thread(unsigned theId) : id(theId)
134 {
135     resetRegisters();
136 }
137
138 void Thread::resetRegisters()
139 {
140     qFill(registers, registers + RegisterCount, uint(0));
141     registerValid = false;
142 }
143
144 QByteArray Thread::gdbReportRegisters() const
145 {
146     QByteArray ba;
147     for (int i = 0; i < 16; ++i) {
148         const uint reg = trk::swapEndian(registers[i]);
149         ba += trk::hexNumber(reg, 8);
150     }
151     return ba;
152 }
153
154 QByteArray Thread::registerContentsLogMessage() const
155 {
156     QByteArray logMsg;
157     for (int i = 0; i < RegisterCount; ++i) {
158         logMsg += dumpRegister(i, registers[i]);
159         logMsg += ' ';
160     }
161     return logMsg;
162 }
163
164 QByteArray Thread::gdbRegisterLogMessage(bool verbose) const
165 {
166     QByteArray logMsg = "Register contents: (Thread 0x";
167     logMsg += QByteArray::number(id, 16);
168     logMsg += " ) ";
169     if (verbose)
170         logMsg += registerContentsLogMessage();
171     return logMsg;
172 }
173
174 QByteArray Thread::gdbReportSingleRegister(unsigned i) const
175 {
176     if (i == RegisterPSGdb)
177         i = RegisterPSTrk;
178     if (i >= RegisterCount)
179         return QByteArray("0000"); // Unknown
180     QByteArray ba;
181     appendInt(&ba, registers[i], trk::LittleEndian);
182     return ba.toHex();
183 }
184
185 QByteArray Thread::gdbSingleRegisterLogMessage(unsigned i) const
186 {
187     if (i == RegisterPSGdb)
188         i = RegisterPSTrk;
189     if (i >= RegisterCount)
190         return QByteArray("Read single unknown register #") + QByteArray::number(i);
191     QByteArray logMsg = "Read Register ";
192     logMsg += dumpRegister(i, registers[i]);
193     return logMsg;
194 }
195
196 ///////////////////////////////////////////////////////////////////////////
197 //
198 // Snapshot
199 //
200 ///////////////////////////////////////////////////////////////////////////
201
202 Snapshot::Snapshot()
203 {
204     reset();
205     threadInfo.reserve(10);
206 }
207
208 void Snapshot::reset()
209 {
210     MEMORY_DEBUG("RESET SNAPSHOT MEMORY INITIALLY: " << memory.size() << " BLOCK LEFT");
211     for (Memory::Iterator it = memory.begin(); it != memory.end(); ) {
212         MEMORY_DEBUG("EXAMINING " << it.key());
213         it = memory.erase(it);
214     }
215     MEMORY_DEBUG("RESET SNAPSHOT MEMORY FINALLY: " << memory.size() << " BLOCK LEFT");
216
217     const int threadCount = threadInfo.size();
218     for (int i =0; i < threadCount; i++) {
219         threadInfo[i].resetRegisters();
220         threadInfo[i].state.clear();
221     }
222
223     wantedMemory = MemoryRange();
224     lineFromAddress = 0;
225     lineToAddress = 0;
226 }
227
228 void Snapshot::resetMemory()
229 {
230     memory.clear();
231     reset();
232 }
233
234 void Snapshot::fullReset()
235 {
236     threadInfo.clear();
237     resetMemory();
238 }
239
240 void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba)
241 {
242     QTC_ASSERT(range.size() == uint(ba.size()),
243         qDebug() << "RANGE: " << range << " BA SIZE: " << ba.size(); return);
244
245     MEMORY_DEBUG("INSERT: " << range);
246     // Try to combine with existing chunk.
247     Snapshot::Memory::iterator it = memory.begin();
248     Snapshot::Memory::iterator et = memory.end();
249     for ( ; it != et; ++it) {
250         if (range.from == it.key().to) {
251             MEMORY_DEBUG("COMBINING " << it.key() << " AND " << range);
252             QByteArray data = *it;
253             data.append(ba);
254             const MemoryRange res(it.key().from, range.to);
255             memory.remove(it.key());
256             MEMORY_DEBUG(" TO(1)  " << res);
257             insertMemory(res, data);
258             return;
259         }
260         if (it.key().from == range.to) {
261             MEMORY_DEBUG("COMBINING " << range << " AND " << it.key());
262             QByteArray data = ba;
263             data.append(*it);
264             const MemoryRange res(range.from, it.key().to);
265             memory.remove(it.key());
266             MEMORY_DEBUG(" TO(2)  " << res);
267             insertMemory(res, data);
268             return;
269         }
270     }
271
272     // Not combinable, add chunk.
273     memory.insert(range, ba);
274 }
275
276 QString Snapshot::toString() const
277 {
278     typedef QMap<MemoryRange, QByteArray>::const_iterator MemCacheConstIt;
279     QString rc;
280     QTextStream str(&rc);
281     foreach(const Thread &thread, threadInfo) {
282         str << " Thread " << thread.id << ' ' << thread.state
283             << " Register valid " << thread.registerValid << ' ';
284         if (thread.registerValid) {
285             for (int i = 0; i < RegisterCount; i++) {
286                 if (i)
287                     str << ", ";
288                 str << " R" << i << "=0x";
289                 str.setIntegerBase(16);
290                 str << thread.registers[i];
291                 str.setIntegerBase(10);
292             }
293         }
294     }
295     str << '\n';
296     // For next step.
297     if (!memory.isEmpty()) {
298         str.setIntegerBase(16);
299         str << "Memory:\n";
300         const MemCacheConstIt mcend = memory.constEnd();
301         for (MemCacheConstIt it = memory.constBegin(); it != mcend; ++it)
302             str << "  0x" << it.key().from << " - 0x" << it.key().to << '\n';
303     }
304     return rc;
305 }
306
307 void Snapshot::addThread(uint id)
308 {
309     if (!id || id == uint(-1)) {
310         qWarning("Cowardly refusing to add thread %d", id);
311         return;
312     }
313
314     const int index = indexOfThread(id);
315     if (index == -1) {
316         threadInfo.push_back(Thread(id));
317     } else {
318         threadInfo[index].resetRegisters();
319         qWarning("Attempt to re-add existing thread %d", id);
320     }
321 }
322
323 void Snapshot::removeThread(uint id)
324 {
325     const int index = indexOfThread(id);
326     if (index != -1) {
327         threadInfo.remove(index);
328     } else {
329         qWarning("Attempt to remove non-existing thread %d", id);
330     }
331 }
332
333 int Snapshot::indexOfThread(uint id) const
334 {
335     const int count = threadInfo.size();
336     for (int i = 0; i < count; i++)
337         if (threadInfo.at(i).id == id)
338             return i;
339     return -1;
340 }
341
342 uint *Snapshot::registers(uint threadId)
343 {
344     const int index = indexOfThread(threadId);
345     QTC_ASSERT(index != -1, { qWarning("No such thread %d", threadId); return 0; } );
346     return threadInfo[index].registers;
347 }
348
349 const uint *Snapshot::registers(uint threadId) const
350 {
351     const int index = indexOfThread(threadId);
352     QTC_ASSERT(index != -1, return 0; );
353     return threadInfo.at(index).registers;
354 }
355
356 uint Snapshot::registerValue(uint threadId, uint index)
357 {
358     if (const uint *regs = registers(threadId))
359         return regs[index];
360     return 0;
361 }
362
363 void Snapshot::setRegisterValue(uint threadId, uint index, uint value)
364 {
365     uint *regs = registers(threadId);
366     QTC_ASSERT(regs, return; );
367     regs[index] = value;
368 }
369
370 bool Snapshot::registersValid(uint threadId) const
371 {
372     const int index = indexOfThread(threadId);
373     return index != -1 ? threadInfo.at(index).registerValid : false;
374 }
375
376 void Snapshot::setRegistersValid(uint threadId, bool e)
377 {
378     const int index = indexOfThread(threadId);
379     QTC_ASSERT(index != -1, return; );
380     threadInfo[index].registerValid = e;
381 }
382
383 void Snapshot::setThreadState(uint threadId, const QString &state)
384 {
385     const int index = indexOfThread(threadId);
386     QTC_ASSERT(index != -1, return; );
387     threadInfo[index].state = state;
388 }
389
390 QByteArray Snapshot::gdbQsThreadInfo() const
391 {
392     // FIXME: Limit packet length by using qsThreadInfo packages ('m', ..'l')
393     QByteArray response(1, 'l');
394     const int count = threadInfo.size();
395     for (int i = 0; i < count; i++) {
396         if (i)
397             response += ',';
398         response += trk::hexNumber(threadInfo.at(i).id);
399     }
400     return response;
401 }
402
403 // $qThreadExtraInfo,1f9#55
404 QByteArray Snapshot::gdbQThreadExtraInfo(const QByteArray &cmd) const
405 {
406     const int pos = cmd.indexOf(',');
407     if (pos != 1) {
408         const uint threadId = cmd.mid(pos + 1).toUInt(0, 16);
409         const int threadIndex = indexOfThread(threadId);
410         if (threadIndex != -1 && !threadInfo.at(threadIndex).state.isEmpty())
411             return threadInfo.at(threadIndex).state.toAscii().toHex();
412     }
413     return QByteArray("Nothing special").toHex();
414 }
415
416 static void gdbAppendRegister(QByteArray *ba, uint regno, uint value)
417 {
418     ba->append(trk::hexNumber(regno, 2));
419     ba->append(':');
420     ba->append(trk::hexNumber(trk::swapEndian(value), 8));
421     ba->append(';');
422 }
423
424 QByteArray Snapshot::gdbStopMessage(uint threadId, int signalNumber, bool reportThreadId) const
425 {
426     QByteArray ba = ('T' + trk::hexNumber(signalNumber, 2));
427     if (reportThreadId) {
428         ba += "thread:";
429         ba += trk::hexNumber(threadId, 3);
430         ba += ';';
431     }
432     const int threadIndex = indexOfThread(threadId);
433     QTC_ASSERT(threadIndex != -1, return QByteArray(); );
434     const Thread &thread = threadInfo.at(threadIndex);
435     for (int i = 0; i < 16; ++i)
436         gdbAppendRegister(&ba, i, thread.registers[i]);
437     // FIXME: those are not understood by gdb 6.4
438     //for (int i = 16; i < 25; ++i)
439     //    appendRegister(&ba, i, 0x0);
440     gdbAppendRegister(&ba, RegisterPSGdb, thread.registers[RegisterPSTrk]);
441     return ba;
442 }
443
444 // Format log message for memory access with some smartness about registers
445 QByteArray Snapshot::memoryReadLogMessage(uint addr, uint threadId, bool verbose, const QByteArray &ba) const
446 {
447     QByteArray logMsg = "memory contents";
448     const uint *regs = registers(threadId);
449     if (verbose && regs) {
450         logMsg += " addr: " + trk::hexxNumber(addr);
451         // indicate dereferencing of registers
452         if (ba.size() == 4) {
453             if (addr == regs[RegisterPC]) {
454                 logMsg += "[PC]";
455             } else if (addr == regs[RegisterPSTrk]) {
456                 logMsg += "[PSTrk]";
457             } else if (addr == regs[RegisterSP]) {
458                 logMsg += "[SP]";
459             } else if (addr == regs[RegisterLR]) {
460                 logMsg += "[LR]";
461             } else if (addr > regs[RegisterSP] &&
462                     (addr - regs[RegisterSP]) < 10240) {
463                 logMsg += "[SP+"; // Stack area ...stack seems to be top-down
464                 logMsg += QByteArray::number(addr - regs[RegisterSP]);
465                 logMsg += ']';
466             }
467         }
468         logMsg += " length ";
469         logMsg += QByteArray::number(ba.size());
470         logMsg += " :";
471         logMsg += trk::stringFromArray(ba, ba.size()).toAscii();
472     }
473     return logMsg;
474 }
475
476 void Snapshot::syncRegisters(uint threadId, RegisterHandler *handler) const
477 {
478     // Take advantage of direct access to cached register values.
479     const int threadIndex = indexOfThread(threadId);
480     QTC_ASSERT(threadIndex != -1, return ;);
481     const Thread &thread = threadInfo.at(threadIndex);
482     QTC_ASSERT(thread.registerValid, return ;);
483
484     Registers debuggerRegisters = handler->registers();
485     QTC_ASSERT(debuggerRegisters.size() >= RegisterPSGdb,
486         qDebug() << "HAVE: " << debuggerRegisters.size(); return);
487
488     for (int i = 0; i < RegisterCount; ++i) {
489         const int gdbIndex = i == RegisterPSTrk ? int(RegisterPSGdb) : i;
490         Register &reg = debuggerRegisters[gdbIndex];
491         reg.value = trk::hexxNumber(thread.registers[i]);
492     }
493     handler->setAndMarkRegisters(debuggerRegisters);
494 }
495
496 void Snapshot::parseGdbStepRange(const QByteArray &cmd, bool so)
497 {
498     const int pos = cmd.indexOf(',', 8);
499     lineFromAddress = cmd.mid(8, pos - 8).toUInt(0, 16);
500     lineToAddress = cmd.mid(pos + 1).toUInt(0, 16);
501     stepOver = so;
502 }
503
504 void Snapshot::syncThreads(ThreadsHandler *handler) const
505 {
506     // Take advantage of direct access to cached register values.
507     Threads threads;
508     const unsigned count = threadInfo.size();
509     for (unsigned t = 0; t < count; t++) {
510         ThreadData thread(t + 1); // Fake gdb thread ids starting from 1
511             thread.targetId = QString::number(threadInfo.at(t).id);
512             thread.state = threadInfo.at(t).state;
513             threads.append(thread);
514     }
515     handler->setThreads(threads);
516 }
517
518 // Answer to gdb's 'qSupported' query:
519 // Increase buffer size for qXfer::libraries XML response
520 const char *gdbQSupported =
521     "PacketSize=20000;"
522     "QPassSignals+;"
523     "QStartNoAckMode+;"
524     "qXfer:libraries:read+;"
525     // "qXfer:auxv:read+;"
526     "qXfer:features:read+;"
527     "qRelocInsn-"; // Relocate instructions for trace (gdb 7.2+): Not supported.
528
529 // Answer to gdb "qXfer:features:read:target.xml:" request
530 // "l<target><architecture>symbianelf</architecture></target>"
531 // "l<target><architecture>arm-none-symbianelf</architecture></target>"
532
533 const char *gdbArchitectureXml = "l<target><architecture>arm</architecture></target>";
534
535 QVector<QByteArray> gdbStartupSequence()
536 {
537     QVector<QByteArray> s;
538     s.reserve(10);
539     s.push_back(QByteArray("set breakpoint always-inserted on"));
540     s.push_back(QByteArray("set breakpoint auto-hw on"));
541     s.push_back(QByteArray("set trust-readonly-sections on")); // No difference?
542     s.push_back(QByteArray("set displaced-stepping on")); // No difference?
543     s.push_back(QByteArray("set mem inaccessible-by-default"));
544     s.push_back(QByteArray("mem 0x00400000 0x70000000 cache"));
545     s.push_back(QByteArray("mem 0x70000000 0x80000000 cache ro"));
546     // FIXME: replace with  stack-cache for newer gdb?
547     s.push_back(QByteArray("set remotecache on"));  // "info dcache" to check
548     return s;
549 }
550
551 // Local symbol file handling
552
553 enum { symDebug = 0 };
554
555 // Build complete file name of a local sym file from DLL
556 // 'QtCore.dll' to 'c:\\foo\QtCore.dll.sym'.
557
558 static inline QString symFileName(const QString &folder,
559                                   const QString &libName)
560 {
561     QString fileName = folder;
562     fileName.append(QLatin1Char('/'));
563     fileName.append(libName);
564     fileName.append(QLatin1String(".sym"));
565     return fileName;
566 }
567
568 // Look up in local symbol file matching remote library loaded in
569 // cache pointed to by environmentname or in standard location
570 // (next to application.sym file).
571 QString localSymFileForLibrary(const QByteArray &libName,
572                                // urel/udeb: exe directory
573                                const QString &standardSymDirectory)
574 {
575     // Check
576     const QByteArray envSymFileCacheDirectory = qgetenv("QTC_SYMBIAN_SYMBOLFILE_CACHE");
577     if (envSymFileCacheDirectory.isEmpty() && standardSymDirectory.isEmpty())
578         return QString();
579     // Base name
580     int lastSlashPos = libName.lastIndexOf('/');
581     if (lastSlashPos == -1)
582         lastSlashPos = libName.lastIndexOf('\\');
583     const QString libBaseName = QString::fromLatin1(lastSlashPos != - 1 ? libName.mid(lastSlashPos + 1) : libName);
584     // Check environment variable
585     if (!envSymFileCacheDirectory.isEmpty()) {
586         const QFileInfo envFi(symFileName(QString::fromLatin1(envSymFileCacheDirectory), libBaseName));
587         if (symDebug)
588             qDebug("SYM-ENV: %s exists %d\n", qPrintable(envFi.absoluteFilePath()), envFi.isFile());
589         if (envFi.isFile())
590             return envFi.absoluteFilePath();
591     }
592     // Check standard location
593     if (!standardSymDirectory.isEmpty()) {
594         const QFileInfo standardFi(symFileName(standardSymDirectory, libBaseName));
595         if (symDebug)
596             qDebug("SYM-STANDARD: %s exists %d\n", qPrintable(standardFi.absoluteFilePath()), standardFi.isFile());
597         if (standardFi.isFile())
598             return standardFi.absoluteFilePath();
599     }
600     return QString();
601 }
602
603 // Return a load command for a local symbol file for a library with address.
604 QByteArray symFileLoadCommand(const QString &symFileNameIn,
605                               quint64 code, quint64 data)
606 {
607     QByteArray symFileName = symFileNameIn.toLatin1();
608     symFileName.replace('\\', '/'); // gdb wants forward slashes
609     QByteArray command = "add-symbol-file \"";
610     command += symFileName;
611     command += "\" 0x";
612     command += QByteArray::number(code, 16);
613     if (data) {
614         command += " -s .data 0x";
615         command += QByteArray::number(data, 16);
616     }
617     return command;
618 }
619
620 QString msgLoadLocalSymFile(const QString &symFileName,
621                             const QByteArray &libName, quint64 code)
622 {
623     return QString::fromAscii("Loading symbol file '%1' for '%2' at 0x%3").
624             arg(symFileName, QString::fromLatin1(libName)).
625             arg(code, 0, 16);
626 }
627
628 } // namespace Symbian
629
630 // Generic gdb server helpers: Read address/length off a memory
631 // command like 'm845,455','X845,455:'
632 QPair<quint64, unsigned> parseGdbReadMemoryRequest(const QByteArray &cmd)
633 {
634     QPair<quint64, unsigned> rc(0, 0);
635     const int pos = cmd.indexOf(',');
636     if (pos == -1)
637         return rc;
638     bool ok;
639     rc.first = cmd.mid(1, pos - 1).toULongLong(&ok, 16);
640     if (!ok)
641         return rc;
642     const int colonPos = cmd.indexOf(':');
643     if (colonPos == -1)
644         rc.second = cmd.mid(pos + 1).toUInt(&ok, 16);
645     else
646         rc.second = cmd.mid(pos + 1, colonPos - pos - 1 ).toUInt(&ok, 16);
647     if (!ok)
648         rc.first = 0;
649     return rc;
650 }
651
652 // Generic gdb server helpers: Parse 'register write' ('P') request
653 // return register number/value
654 QPair<uint, uint> parseGdbWriteRegisterWriteRequest(const QByteArray &cmd)
655 {
656     const int pos = cmd.indexOf('=');
657     const QByteArray regName = cmd.mid(1, pos - 1);
658     const QByteArray valueName = cmd.mid(pos + 1);
659     bool ok = false;
660     const uint registerNumber = regName.toUInt(&ok, 16);
661     const uint value = trk::swapEndian(valueName.toUInt(&ok, 16));
662     return QPair<uint, uint>(registerNumber, value);
663 }
664
665 // Generic gdb server helpers: Parse 'set breakpoint' ('Z0') request
666 // return address/length
667 QPair<quint64, unsigned> parseGdbSetBreakpointRequest(const QByteArray &cmd)
668 {
669     // $Z0,786a4ccc,4#99
670     const int pos = cmd.lastIndexOf(',');
671     bool ok1 = false;
672     bool ok2 = false;
673     const quint64 addr = cmd.mid(3, pos - 3).toULongLong(&ok1, 16);
674     const uint len = cmd.mid(pos + 1).toUInt(&ok2, 16);
675     return ok1 && ok2 ? QPair<quint64, unsigned>(addr, len) : QPair<quint64, unsigned>(0, 0);
676 }
677
678 } // namespace Internal
679 } // namespace Debugger