OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / pdb / pdbengine.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 #define QT_NO_CAST_FROM_ASCII
34
35 #include "pdbengine.h"
36
37 #include "debuggerstartparameters.h"
38 #include "debuggeractions.h"
39 #include "debuggercore.h"
40 #include "debuggerdialogs.h"
41 #include "debuggerplugin.h"
42 #include "debuggerstringutils.h"
43 #include "debuggertooltipmanager.h"
44
45 #include "breakhandler.h"
46 #include "moduleshandler.h"
47 #include "registerhandler.h"
48 #include "stackhandler.h"
49 #include "watchhandler.h"
50 #include "watchutils.h"
51
52 #include "../gdb/gdbmi.h"
53
54 #include <utils/qtcassert.h>
55
56 #include <texteditor/itexteditor.h>
57 #include <coreplugin/ifile.h>
58 #include <coreplugin/icore.h>
59
60 #include <QtCore/QDateTime>
61 #include <QtCore/QDebug>
62 #include <QtCore/QDir>
63 #include <QtCore/QFileInfo>
64 #include <QtCore/QTimer>
65 #include <QtCore/QVariant>
66
67 #include <QtGui/QApplication>
68 #include <QtGui/QMessageBox>
69 #include <QtGui/QToolTip>
70
71
72 #define DEBUG_SCRIPT 1
73 #if DEBUG_SCRIPT
74 #   define SDEBUG(s) qDebug() << s
75 #else
76 #   define SDEBUG(s)
77 #endif
78 # define XSDEBUG(s) qDebug() << s
79
80
81 #define CB(callback) &PdbEngine::callback, STRINGIFY(callback)
82
83 namespace Debugger {
84 namespace Internal {
85
86 ///////////////////////////////////////////////////////////////////////
87 //
88 // PdbEngine
89 //
90 ///////////////////////////////////////////////////////////////////////
91
92 PdbEngine::PdbEngine(const DebuggerStartParameters &startParameters)
93     : DebuggerEngine(startParameters)
94 {
95     setObjectName(QLatin1String("PdbEngine"));
96 }
97
98 PdbEngine::~PdbEngine()
99 {}
100
101 void PdbEngine::executeDebuggerCommand(const QString &command)
102 {
103     QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
104     //XSDEBUG("PdbEngine::executeDebuggerCommand:" << command);
105     if (state() == DebuggerNotReady) {
106         showMessage(_("PDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
107         return;
108     }
109     QTC_ASSERT(m_pdbProc.state() == QProcess::Running, notifyEngineIll());
110     postCommand(command.toLatin1(), CB(handleExecuteDebuggerCommand));
111 }
112
113 void PdbEngine::handleExecuteDebuggerCommand(const PdbResponse &response)
114 {
115     Q_UNUSED(response);
116 }
117
118 void PdbEngine::postDirectCommand(const QByteArray &command)
119 {
120     QTC_ASSERT(m_pdbProc.state() == QProcess::Running, notifyEngineIll());
121     showMessage(_(command), LogInput);
122     m_pdbProc.write(command + '\n');
123 }
124
125 void PdbEngine::postCommand(const QByteArray &command,
126 //                 PdbCommandFlags flags,
127                  PdbCommandCallback callback,
128                  const char *callbackName,
129                  const QVariant &cookie)
130 {
131     QTC_ASSERT(m_pdbProc.state() == QProcess::Running, notifyEngineIll());
132     PdbCommand cmd;
133     cmd.command = command;
134     cmd.callback = callback;
135     cmd.callbackName = callbackName;
136     cmd.cookie = cookie;
137     m_commands.enqueue(cmd);
138     qDebug() << "ENQUEUE: " << command << cmd.callbackName;
139     showMessage(_(cmd.command), LogInput);
140     m_pdbProc.write(cmd.command + '\n');
141 }
142
143 void PdbEngine::shutdownInferior()
144 {
145     QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
146     notifyInferiorShutdownOk();
147 }
148
149 void PdbEngine::shutdownEngine()
150 {
151     QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
152     m_pdbProc.kill();
153 }
154
155 void PdbEngine::setupEngine()
156 {
157     QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
158
159     m_pdb = _("python");
160     showMessage(_("STARTING PDB ") + m_pdb);
161
162     connect(&m_pdbProc, SIGNAL(error(QProcess::ProcessError)),
163         SLOT(handlePdbError(QProcess::ProcessError)));
164     connect(&m_pdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
165         SLOT(handlePdbFinished(int, QProcess::ExitStatus)));
166     connect(&m_pdbProc, SIGNAL(readyReadStandardOutput()),
167         SLOT(readPdbStandardOutput()));
168     connect(&m_pdbProc, SIGNAL(readyReadStandardError()),
169         SLOT(readPdbStandardError()));
170
171     connect(this, SIGNAL(outputReady(QByteArray)),
172         SLOT(handleOutput2(QByteArray)), Qt::QueuedConnection);
173
174     // We will stop immediately, so setup a proper callback.
175     PdbCommand cmd;
176     cmd.callback = &PdbEngine::handleFirstCommand;
177     m_commands.enqueue(cmd);
178
179     m_pdbProc.start(m_pdb, QStringList() << _("-i"));
180
181     if (!m_pdbProc.waitForStarted()) {
182         const QString msg = tr("Unable to start pdb '%1': %2")
183             .arg(m_pdb, m_pdbProc.errorString());
184         notifyEngineSetupFailed();
185         showMessage(_("ADAPTER START FAILED"));
186         if (!msg.isEmpty()) {
187             const QString title = tr("Adapter start failed");
188             Core::ICore::instance()->showWarningWithOptions(title, msg);
189         }
190         notifyEngineSetupFailed();
191         return;
192     }
193     notifyEngineSetupOk();
194 }
195
196 void PdbEngine::setupInferior()
197 {
198     QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
199
200     QString fileName = QFileInfo(startParameters().executable).absoluteFilePath();
201     QFile scriptFile(fileName);
202     if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
203         showMessageBox(QMessageBox::Critical, tr("Python Error"),
204             _("Cannot open script file %1:\n%2").
205                arg(fileName, scriptFile.errorString()));
206         notifyInferiorSetupFailed();
207         return;
208     }
209     notifyInferiorSetupOk();
210 }
211
212 void PdbEngine::runEngine()
213 {
214     QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
215     showStatusMessage(tr("Running requested..."), 5000);
216     const QByteArray dumperSourcePath =
217         Core::ICore::instance()->resourcePath().toLocal8Bit() + "/gdbmacros/";
218     QString fileName = QFileInfo(startParameters().executable).absoluteFilePath();
219     postDirectCommand("import sys");
220     postDirectCommand("sys.argv.append('" + fileName.toLocal8Bit() + "')");
221     postDirectCommand("execfile('/usr/bin/pdb')");
222     postDirectCommand("execfile('" + dumperSourcePath + "pdumper.py')");
223     attemptBreakpointSynchronization();
224     notifyEngineRunAndInferiorStopOk();
225     continueInferior();
226 }
227
228 void PdbEngine::interruptInferior()
229 {
230     notifyInferiorStopOk();
231 }
232
233 void PdbEngine::executeStep()
234 {
235     resetLocation();
236     notifyInferiorRunRequested();
237     notifyInferiorRunOk();
238     postCommand("step", CB(handleUpdateAll));
239 }
240
241 void PdbEngine::executeStepI()
242 {
243     resetLocation();
244     notifyInferiorRunRequested();
245     notifyInferiorRunOk();
246     postCommand("step", CB(handleUpdateAll));
247 }
248
249 void PdbEngine::executeStepOut()
250 {
251     resetLocation();
252     notifyInferiorRunRequested();
253     notifyInferiorRunOk();
254     postCommand("finish", CB(handleUpdateAll));
255 }
256
257 void PdbEngine::executeNext()
258 {
259     resetLocation();
260     notifyInferiorRunRequested();
261     notifyInferiorRunOk();
262     postCommand("next", CB(handleUpdateAll));
263 }
264
265 void PdbEngine::executeNextI()
266 {
267     resetLocation();
268     notifyInferiorRunRequested();
269     notifyInferiorRunOk();
270     postCommand("next", CB(handleUpdateAll));
271 }
272
273 void PdbEngine::continueInferior()
274 {
275     resetLocation();
276     notifyInferiorRunRequested();
277     notifyInferiorRunOk();
278     // Callback will be triggered e.g. when breakpoint is hit.
279     postCommand("continue", CB(handleUpdateAll));
280 }
281
282 void PdbEngine::executeRunToLine(const ContextData &data)
283 {
284     Q_UNUSED(data)
285     SDEBUG("FIXME:  PdbEngine::runToLineExec()");
286 }
287
288 void PdbEngine::executeRunToFunction(const QString &functionName)
289 {
290     Q_UNUSED(functionName)
291     XSDEBUG("FIXME:  PdbEngine::runToFunctionExec()");
292 }
293
294 void PdbEngine::executeJumpToLine(const ContextData &data)
295 {
296     Q_UNUSED(data)
297     XSDEBUG("FIXME:  PdbEngine::jumpToLineExec()");
298 }
299
300 void PdbEngine::activateFrame(int frameIndex)
301 {
302     resetLocation();
303     if (state() != InferiorStopOk && state() != InferiorUnrunnable)
304         return;
305
306     StackHandler *handler = stackHandler();
307     int oldIndex = handler->currentIndex();
308
309     //if (frameIndex == handler->stackSize()) {
310     //    reloadFullStack();
311     //    return;
312     //}
313
314     QTC_ASSERT(frameIndex < handler->stackSize(), return);
315
316     if (oldIndex != frameIndex) {
317         // Assuming the command always succeeds this saves a roundtrip.
318         // Otherwise the lines below would need to get triggered
319         // after a response to this -stack-select-frame here.
320         handler->setCurrentIndex(frameIndex);
321         //postCommand("-stack-select-frame " + QByteArray::number(frameIndex),
322         //    CB(handleStackSelectFrame));
323     }
324     gotoLocation(handler->currentFrame());
325 }
326
327 void PdbEngine::selectThread(int index)
328 {
329     Q_UNUSED(index)
330 }
331
332 bool PdbEngine::acceptsBreakpoint(BreakpointId id) const
333 {
334     const QString fileName = breakHandler()->fileName(id);
335     return fileName.endsWith(QLatin1String(".py"));
336 }
337
338 void PdbEngine::insertBreakpoint(BreakpointId id)
339 {
340     BreakHandler *handler = breakHandler();
341     QTC_ASSERT(handler->state(id) == BreakpointInsertRequested, /**/);
342     handler->notifyBreakpointInsertProceeding(id);
343
344     QByteArray loc;
345     if (handler->type(id) == BreakpointByFunction)
346         loc = handler->functionName(id).toLatin1();
347     else
348         loc = handler->fileName(id).toLocal8Bit() + ':'
349          + QByteArray::number(handler->lineNumber(id));
350
351     postCommand("break " + loc, CB(handleBreakInsert), QVariant(id));
352 }
353
354 void PdbEngine::handleBreakInsert(const PdbResponse &response)
355 {
356     //qDebug() << "BP RESPONSE: " << response.data;
357     // "Breakpoint 1 at /pdb/math.py:10"
358     BreakpointId id(response.cookie.toInt());
359     BreakHandler *handler = breakHandler();
360     QTC_ASSERT(response.data.startsWith("Breakpoint "), return);
361     int pos1 = response.data.indexOf(" at ");
362     QTC_ASSERT(pos1 != -1, return);
363     QByteArray bpnr = response.data.mid(11, pos1 - 11);
364     int pos2 = response.data.lastIndexOf(":");
365     QByteArray file = response.data.mid(pos1 + 4, pos2 - pos1 - 4);
366     QByteArray line = response.data.mid(pos2 + 1);
367     BreakpointResponse br;
368     br.number = bpnr.toInt();
369     br.fileName = _(file);
370     br.lineNumber = line.toInt();
371     handler->setResponse(id, br);
372 }
373
374 void PdbEngine::removeBreakpoint(BreakpointId id)
375 {
376     BreakHandler *handler = breakHandler();
377     QTC_ASSERT(handler->state(id) == BreakpointRemoveRequested, /**/);
378     handler->notifyBreakpointRemoveProceeding(id);
379     BreakpointResponse br = handler->response(id);
380     showMessage(_("DELETING BP %1 IN %2").arg(br.number)
381         .arg(handler->fileName(id)));
382     postCommand("clear " + QByteArray::number(br.number));
383     // Pretend it succeeds without waiting for response.
384     handler->notifyBreakpointRemoveOk(id);
385 }
386
387 void PdbEngine::loadSymbols(const QString &moduleName)
388 {
389     Q_UNUSED(moduleName)
390 }
391
392 void PdbEngine::loadAllSymbols()
393 {
394 }
395
396 void PdbEngine::reloadModules()
397 {
398     //postCommand("qdebug('listmodules')", CB(handleListModules));
399 }
400
401 void PdbEngine::handleListModules(const PdbResponse &response)
402 {
403     GdbMi out;
404     out.fromString(response.data.trimmed());
405     Modules modules;
406     foreach (const GdbMi &item, out.children()) {
407         Module module;
408         module.moduleName = _(item.findChild("name").data());
409         QString path = _(item.findChild("value").data());
410         int pos = path.indexOf(_("' from '"));
411         if (pos != -1) {
412             path = path.mid(pos + 8);
413             if (path.size() >= 2)
414                 path.chop(2);
415         } else if (path.startsWith(_("<module '"))
416                 && path.endsWith(_("' (built-in)>"))) {
417             path = _("(builtin)");
418         }
419         module.modulePath = path;
420         modules.append(module);
421     }
422     modulesHandler()->setModules(modules);
423 }
424
425 void PdbEngine::requestModuleSymbols(const QString &moduleName)
426 {
427     postCommand("qdebug('listsymbols','" + moduleName.toLatin1() + "')",
428         CB(handleListSymbols), moduleName);
429 }
430
431 void PdbEngine::handleListSymbols(const PdbResponse &response)
432 {
433     GdbMi out;
434     out.fromString(response.data.trimmed());
435     Symbols symbols;
436     QString moduleName = response.cookie.toString();
437     foreach (const GdbMi &item, out.children()) {
438         Symbol symbol;
439         symbol.name = _(item.findChild("name").data());
440         symbols.append(symbol);
441     }
442    debuggerCore()->showModuleSymbols(moduleName, symbols);
443 }
444
445 //////////////////////////////////////////////////////////////////////
446 //
447 // Tooltip specific stuff
448 //
449 //////////////////////////////////////////////////////////////////////
450
451
452 static WatchData m_toolTip;
453 static QPoint m_toolTipPos;
454 static QHash<QString, WatchData> m_toolTipCache;
455
456 bool PdbEngine::setToolTipExpression(const QPoint &mousePos,
457     TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
458 {
459     Q_UNUSED(mousePos)
460     Q_UNUSED(editor)
461
462     if (state() != InferiorStopOk) {
463         //SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED");
464         return false;
465     }
466     // Check mime type and get expression (borrowing some C++ - functions)
467     const QString javaPythonMimeType =
468         QLatin1String("application/javascript");
469     if (!editor->file() || editor->file()->mimeType() != javaPythonMimeType)
470         return false;
471
472     int line;
473     int column;
474     QString exp = cppExpressionAt(editor, ctx.position, &line, &column);
475
476 /*
477     if (m_toolTipCache.contains(exp)) {
478         const WatchData & data = m_toolTipCache[exp];
479         q->watchHandler()->removeChildren(data.iname);
480         insertData(data);
481         return;
482     }
483 */
484
485     QToolTip::hideText();
486     if (exp.isEmpty() || exp.startsWith(QLatin1Char('#')))  {
487         QToolTip::hideText();
488         return false;
489     }
490
491     if (!hasLetterOrNumber(exp)) {
492         QToolTip::showText(m_toolTipPos, tr("'%1' contains no identifier").arg(exp));
493         return true;
494     }
495
496     if (exp.startsWith(QLatin1Char('"')) && exp.endsWith(QLatin1Char('"'))) {
497         QToolTip::showText(m_toolTipPos, tr("String literal %1").arg(exp));
498         return true;
499     }
500
501     if (exp.startsWith(QLatin1String("++")) || exp.startsWith(QLatin1String("--")))
502         exp.remove(0, 2);
503
504     if (exp.endsWith(QLatin1String("++")) || exp.endsWith(QLatin1String("--")))
505         exp.remove(0, 2);
506
507     if (exp.startsWith(QLatin1Char('<')) || exp.startsWith(QLatin1Char('[')))
508         return false;
509
510     if (hasSideEffects(exp)) {
511         QToolTip::showText(m_toolTipPos,
512             tr("Cowardly refusing to evaluate expression '%1' "
513                "with potential side effects").arg(exp));
514         return true;
515     }
516
517 #if 0
518     //if (status() != InferiorStopOk)
519     //    return;
520
521     // FIXME: 'exp' can contain illegal characters
522     m_toolTip = WatchData();
523     m_toolTip.exp = exp;
524     m_toolTip.name = exp;
525     m_toolTip.iname = tooltipIName;
526     insertData(m_toolTip);
527 #endif
528     return false;
529 }
530
531
532 //////////////////////////////////////////////////////////////////////
533 //
534 // Watch specific stuff
535 //
536 //////////////////////////////////////////////////////////////////////
537
538 void PdbEngine::assignValueInDebugger(const Internal::WatchData *, const QString &expression, const QVariant &value)
539 {
540     Q_UNUSED(expression);
541     Q_UNUSED(value);
542     SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value.toString()));
543 #if 0
544     m_scriptEngine->evaluate(expression + QLatin1Char('=') + value.toString());
545     updateLocals();
546 #endif
547 }
548
549
550 void PdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags)
551 {
552     Q_UNUSED(data);
553     Q_UNUSED(flags);
554     updateAll();
555 }
556
557 void PdbEngine::handlePdbError(QProcess::ProcessError error)
558 {
559     qDebug() << "HANDLE PDB ERROR";
560     showMessage(_("HANDLE PDB ERROR"));
561     switch (error) {
562     case QProcess::Crashed:
563         break; // will get a processExited() as well
564     // impossible case QProcess::FailedToStart:
565     case QProcess::ReadError:
566     case QProcess::WriteError:
567     case QProcess::Timedout:
568     default:
569         //setState(EngineShutdownRequested, true);
570         m_pdbProc.kill();
571         showMessageBox(QMessageBox::Critical, tr("Pdb I/O Error"),
572                        errorMessage(error));
573         break;
574     }
575 }
576
577 QString PdbEngine::errorMessage(QProcess::ProcessError error) const
578 {
579     switch (error) {
580         case QProcess::FailedToStart:
581             return tr("The Pdb process failed to start. Either the "
582                 "invoked program '%1' is missing, or you may have insufficient "
583                 "permissions to invoke the program.")
584                 .arg(m_pdb);
585         case QProcess::Crashed:
586             return tr("The Pdb process crashed some time after starting "
587                 "successfully.");
588         case QProcess::Timedout:
589             return tr("The last waitFor...() function timed out. "
590                 "The state of QProcess is unchanged, and you can try calling "
591                 "waitFor...() again.");
592         case QProcess::WriteError:
593             return tr("An error occurred when attempting to write "
594                 "to the Pdb process. For example, the process may not be running, "
595                 "or it may have closed its input channel.");
596         case QProcess::ReadError:
597             return tr("An error occurred when attempting to read from "
598                 "the Pdb process. For example, the process may not be running.");
599         default:
600             return tr("An unknown error in the Pdb process occurred. ");
601     }
602 }
603
604 void PdbEngine::handlePdbFinished(int code, QProcess::ExitStatus type)
605 {
606     qDebug() << "PDB FINISHED";
607     showMessage(_("PDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
608     notifyEngineSpontaneousShutdown();
609 }
610
611 void PdbEngine::readPdbStandardError()
612 {
613     QByteArray err = m_pdbProc.readAllStandardError();
614     qDebug() << "\nPDB STDERR" << err;
615     //qWarning() << "Unexpected pdb stderr:" << err;
616     //showMessage(_("Unexpected pdb stderr: " + err));
617     //handleOutput(err);
618 }
619
620 void PdbEngine::readPdbStandardOutput()
621 {
622     QByteArray out = m_pdbProc.readAllStandardOutput();
623     qDebug() << "\nPDB STDOUT" << out;
624     handleOutput(out);
625 }
626
627 void PdbEngine::handleOutput(const QByteArray &data)
628 {
629     //qDebug() << "READ: " << data;
630     m_inbuffer.append(data);
631     qDebug() << "BUFFER FROM: '" << m_inbuffer << "'";
632     while (true) {
633         int pos = m_inbuffer.indexOf("(Pdb)");
634         if (pos == -1)
635             pos = m_inbuffer.indexOf(">>>");
636         if (pos == -1)
637             break;
638         QByteArray response = m_inbuffer.left(pos).trimmed();
639         m_inbuffer = m_inbuffer.mid(pos + 6);
640         emit outputReady(response);
641     }
642     qDebug() << "BUFFER LEFT: '" << m_inbuffer << "'";
643     //m_inbuffer.clear();
644 }
645
646
647 void PdbEngine::handleOutput2(const QByteArray &data)
648 {
649     PdbResponse response;
650     response.data = data;
651     showMessage(_(data));
652     QTC_ASSERT(!m_commands.isEmpty(), qDebug() << "RESPONSE: " << data; return)
653     PdbCommand cmd = m_commands.dequeue();
654     response.cookie = cmd.cookie;
655     qDebug() << "DEQUE: " << cmd.command << cmd.callbackName;
656     if (cmd.callback) {
657         //qDebug() << "EXECUTING CALLBACK " << cmd.callbackName
658         //    << " RESPONSE: " << response.data;
659         (this->*cmd.callback)(response);
660     } else {
661         qDebug() << "NO CALLBACK FOR RESPONSE: " << response.data;
662     }
663 }
664 /*
665 void PdbEngine::handleResponse(const QByteArray &response0)
666 {
667     QByteArray response = response0;
668     qDebug() << "RESPONSE: '" << response << "'";
669     if (response.startsWith("--Call--")) {
670         qDebug() << "SKIPPING '--Call--' MARKER";
671         response = response.mid(9);
672     }
673     if (response.startsWith("--Return--")) {
674         qDebug() << "SKIPPING '--Return--' MARKER";
675         response = response.mid(11);
676     }
677     if (response.startsWith("> ")) {
678         int pos1 = response.indexOf('(');
679         int pos2 = response.indexOf(')', pos1);
680         if (pos1 != -1 && pos2 != -1) {
681             int lineNumber = response.mid(pos1 + 1, pos2 - pos1 - 1).toInt();
682             QByteArray fileName = response.mid(2, pos1 - 2);
683             qDebug() << " " << pos1 << pos2 << lineNumber << fileName
684                 << response.mid(pos1 + 1, pos2 - pos1 - 1);
685             StackFrame frame;
686             frame.file = _(fileName);
687             frame.line = lineNumber;
688             if (frame.line > 0 && QFileInfo(frame.file).exists()) {
689                 gotoLocation(frame);
690                 notifyInferiorSpontaneousStop();
691                 return;
692             }
693         }
694     }
695     qDebug() << "COULD NOT PARSE RESPONSE: '" << response << "'";
696 }
697 */
698
699 void PdbEngine::handleFirstCommand(const PdbResponse &response)
700 {
701     Q_UNUSED(response);
702 }
703
704 void PdbEngine::handleUpdateAll(const PdbResponse &response)
705 {
706     Q_UNUSED(response);
707     notifyInferiorSpontaneousStop();
708     updateAll();
709 }
710
711 void PdbEngine::updateAll()
712 {
713     postCommand("bt", CB(handleBacktrace));
714     //updateLocals();
715 }
716
717 void PdbEngine::updateLocals()
718 {
719     WatchHandler *handler = watchHandler();
720
721     QByteArray watchers;
722     //if (!m_toolTipExpression.isEmpty())
723     //    watchers += m_toolTipExpression.toLatin1()
724     //        + '#' + tooltipINameForExpression(m_toolTipExpression.toLatin1());
725
726     QHash<QByteArray, int> watcherNames = handler->watcherNames();
727     QHashIterator<QByteArray, int> it(watcherNames);
728     while (it.hasNext()) {
729         it.next();
730         if (!watchers.isEmpty())
731             watchers += "##";
732         watchers += it.key() + "#watch." + QByteArray::number(it.value());
733     }
734
735     QByteArray options;
736     if (debuggerCore()->boolSetting(UseDebuggingHelpers))
737         options += "fancy,";
738     if (debuggerCore()->boolSetting(AutoDerefPointers))
739         options += "autoderef,";
740     if (options.isEmpty())
741         options += "defaults,";
742     options.chop(1);
743
744     postCommand("qdebug('" + options + "','"
745         + handler->expansionRequests() + "','"
746         + handler->typeFormatRequests() + "','"
747         + handler->individualFormatRequests() + "','"
748         + watchers.toHex() + "')", CB(handleListLocals));
749 }
750
751 void PdbEngine::handleBacktrace(const PdbResponse &response)
752 {
753     //qDebug() << " BACKTRACE: '" << response.data << "'";
754     // "  /usr/lib/python2.6/bdb.py(368)run()"
755     // "-> exec cmd in globals, locals"
756     // "  <string>(1)<module>()"
757     // "  /python/math.py(19)<module>()"
758     // "-> main()"
759     // "  /python/math.py(14)main()"
760     // "-> print cube(3)"
761     // "  /python/math.py(7)cube()"
762     // "-> x = square(a)"
763     // "> /python/math.py(2)square()"
764     // "-> def square(a):"
765
766     // Populate stack view.
767     StackFrames stackFrames;
768     int level = 0;
769     int currentIndex = -1;
770     foreach (const QByteArray &line, response.data.split('\n')) {
771         //qDebug() << "  LINE: '" << line << "'";
772         if (line.startsWith("> ") || line.startsWith("  ")) {
773             int pos1 = line.indexOf('(');
774             int pos2 = line.indexOf(')', pos1);
775             if (pos1 != -1 && pos2 != -1) {
776                 int lineNumber = line.mid(pos1 + 1, pos2 - pos1 - 1).toInt();
777                 QByteArray fileName = line.mid(2, pos1 - 2);
778                 //qDebug() << " " << pos1 << pos2 << lineNumber << fileName
779                 //    << line.mid(pos1 + 1, pos2 - pos1 - 1);
780                 StackFrame frame;
781                 frame.file = _(fileName);
782                 frame.line = lineNumber;
783                 frame.function = _(line.mid(pos2 + 1));
784                 if (frame.line > 0 && QFileInfo(frame.file).exists()) {
785                     if (line.startsWith("> "))
786                         currentIndex = level;
787                     frame.level = level;
788                     stackFrames.prepend(frame);
789                     ++level;
790                 }
791             }
792         }
793     }
794     const int frameCount = stackFrames.size();
795     for (int i = 0; i != frameCount; ++i)
796         stackFrames[i].level = frameCount - stackFrames[i].level - 1;
797     stackHandler()->setFrames(stackFrames);
798
799     // Select current frame.
800     if (currentIndex != -1) {
801         currentIndex = frameCount - currentIndex - 1;
802         stackHandler()->setCurrentIndex(currentIndex);
803         gotoLocation(stackFrames.at(currentIndex));
804     }
805
806     updateLocals();
807 }
808
809 void PdbEngine::handleListLocals(const PdbResponse &response)
810 {
811     //qDebug() << " LOCALS: '" << response.data << "'";
812     QByteArray out = response.data.trimmed();
813
814     GdbMi all;
815     all.fromStringMultiple(out);
816     //qDebug() << "ALL: " << all.toString();
817
818     //GdbMi data = all.findChild("data");
819     QList<WatchData> list;
820     WatchHandler *handler = watchHandler();
821     foreach (const GdbMi &child, all.children()) {
822         WatchData dummy;
823         dummy.iname = child.findChild("iname").data();
824         dummy.name = _(child.findChild("name").data());
825         //qDebug() << "CHILD: " << child.toString();
826         parseWatchData(handler->expandedINames(), dummy, child, &list);
827     }
828     handler->insertBulkData(list);
829 }
830
831 unsigned PdbEngine::debuggerCapabilities() const
832 {
833     return ReloadModuleCapability|BreakConditionCapability;
834 }
835
836 DebuggerEngine *createPdbEngine(const DebuggerStartParameters &startParameters)
837 {
838     return new PdbEngine(startParameters);
839 }
840
841
842 } // namespace Internal
843 } // namespace Debugger