1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #define QT_NO_CAST_FROM_ASCII
36 #include "lldbengineguest.h"
38 #include "debuggeractions.h"
39 #include "debuggerconstants.h"
40 #include "debuggerdialogs.h"
41 #include "debuggerplugin.h"
42 #include "debuggerstringutils.h"
44 #include "breakhandler.h"
45 #include "breakpoint.h"
46 #include "moduleshandler.h"
47 #include "registerhandler.h"
48 #include "stackhandler.h"
49 #include "watchhandler.h"
50 #include "watchutils.h"
51 #include "threadshandler.h"
53 #include <utils/qtcassert.h>
54 #include <QtCore/QDebug>
55 #include <QtCore/QProcess>
56 #include <QtCore/QFileInfo>
57 #include <QtCore/QThread>
58 #include <QtCore/QMutexLocker>
60 #include <lldb/API/LLDB.h>
62 #define DEBUG_FUNC_ENTER \
63 showMessage(QString(QLatin1String("LLDB guest engine: %1 ")) \
64 .arg(QLatin1String(Q_FUNC_INFO))); \
65 qDebug("%s", Q_FUNC_INFO)
67 #define SYNC_INFERIOR_OR(x) if (m_running) { x; }
73 void LldbEventListener::listen(lldb::SBListener *listener)
77 if (listener->WaitForEvent(1000, event))
78 emit lldbEvent(&event);
82 LldbEngineGuest::LldbEngineGuest()
85 , m_worker (new LldbEventListener)
86 , m_lldb (new lldb::SBDebugger)
87 , m_target (new lldb::SBTarget)
88 , m_process (new lldb::SBProcess)
89 , m_listener(new lldb::SBListener("bla"))
90 , m_relistFrames (false)
91 #if defined(HAVE_LLDB_PRIVATE)
92 , py (new PythonLLDBToGdbMiHack)
95 qRegisterMetaType<lldb::SBListener *>("lldb::SBListener *");
96 qRegisterMetaType<lldb::SBEvent *>("lldb::SBEvent *");
98 m_worker->moveToThread(&m_wThread);
99 connect(m_worker, SIGNAL(lldbEvent(lldb::SBEvent *)), this,
100 SLOT(lldbEvent(lldb::SBEvent *)), Qt::BlockingQueuedConnection);
102 setObjectName(QLatin1String("LLDBEngineGuest"));
105 LldbEngineGuest::~LldbEngineGuest()
114 void LldbEngineGuest::nuke()
119 void LldbEngineGuest::setupEngine()
123 lldb::SBDebugger::Initialize();
125 *m_lldb = lldb::SBDebugger::Create();
126 m_lldb->Initialize();
127 if (m_lldb->IsValid())
128 notifyEngineSetupOk();
130 notifyEngineSetupFailed();
134 void LldbEngineGuest::setupInferior(const QString &executable,
135 const QStringList &args, const QStringList &env)
139 foreach (const QString &s, args) {
140 m_arguments.append(s.toLocal8Bit());
142 foreach (const QString &s, env) {
143 m_environment.append(s.toLocal8Bit());
146 qDebug("creating target for %s", executable.toLocal8Bit().data());
147 showStatusMessage(QLatin1String("starting ") + executable);
148 *m_target = m_lldb->CreateTarget(executable.toLocal8Bit().data());
149 if (!m_target->IsValid()) {
150 notifyInferiorSetupFailed();
155 const char **argp = new const char *[m_arguments.count() + 1];
156 argp[m_arguments.count()] = 0;
157 for (int i = 0; i < m_arguments.count(); i++) {
158 argp[i] = m_arguments[i].data();
161 const char **envp = new const char *[m_environment.count() + 1];
162 envp[m_environment.count()] = 0;
163 for (int i = 0; i < m_environment.count(); i++) {
164 envp[i] = m_environment[i].data();
167 *m_process = m_target->Launch(argp, envp, NULL, NULL, true, err);
169 if (!err.Success()) {
170 showMessage(QString::fromLocal8Bit(err.GetCString()));
171 qDebug() << err.GetCString();
172 notifyInferiorSetupFailed();
176 * note, the actual string ptrs are still valid. They are in m_environment.
177 * They probably leak. Considered the marvelous API, there is not much we can do
181 if (!m_process->IsValid())
182 notifyEngineRunFailed();
183 QTC_ASSERT(m_listener->IsValid(), qDebug() << false);
184 m_listener->StartListeningForEvents(m_process->GetBroadcaster(), UINT32_MAX);
185 QMetaObject::invokeMethod(m_worker, "listen", Qt::QueuedConnection,
186 Q_ARG(lldb::SBListener *, m_listener));
187 notifyInferiorSetupOk();
190 void LldbEngineGuest::runEngine()
193 m_process->Continue();
196 void LldbEngineGuest::shutdownInferior()
202 void LldbEngineGuest::shutdownEngine()
205 m_currentFrame = lldb::SBFrame();
206 m_currentThread = lldb::SBThread();
207 m_breakpoints.clear();
208 m_localesCache.clear();
211 * this leaks. However, Terminate is broken and lldb leaks anyway
212 * We should kill the engine guest process
215 *m_lldb = lldb::SBDebugger();
216 // leakd.Terminate();
217 notifyEngineShutdownOk();
220 void LldbEngineGuest::detachDebugger()
225 void LldbEngineGuest::executeStep()
229 if (!m_currentThread.IsValid())
231 m_currentThread.StepInto();
234 void LldbEngineGuest::executeStepOut()
238 if (!m_currentThread.IsValid())
240 m_currentThread.StepOut();
243 void LldbEngineGuest::executeNext()
247 if (!m_currentThread.IsValid())
249 m_currentThread.StepOver();
252 void LldbEngineGuest::executeStepI()
256 if (!m_currentThread.IsValid())
258 m_currentThread.StepInstruction(false);
261 void LldbEngineGuest::executeNextI()
265 if (!m_currentThread.IsValid())
267 m_currentThread.StepInstruction(true);
270 void LldbEngineGuest::continueInferior()
274 notifyInferiorRunRequested();
275 m_process->Continue();
276 showStatusMessage(QLatin1String("resuming inferior"));
278 void LldbEngineGuest::interruptInferior()
283 notifyInferiorStopOk();
284 m_relistFrames = true;
288 void LldbEngineGuest::executeRunToLine(const ContextData &data);
296 void LldbEngineGuest::executeRunToFunction(const QString &functionName)
301 Q_UNUSED(functionName);
303 void LldbEngineGuest::executeJumpToLine(const ContextData &data);
311 void LldbEngineGuest::activateFrame(qint64 token)
314 SYNC_INFERIOR_OR(showMessage(QLatin1String(
315 "activateFrame called while inferior running")); return);
317 currentFrameChanged(token);
318 m_localesCache.clear();
320 lldb::SBFrame fr = m_currentThread.GetFrameAtIndex(token);
322 lldb::SBSymbolContext context = fr.GetSymbolContext(lldb::eSymbolContextEverything);
323 lldb::SBValueList values = fr.GetVariables(true, true, false, true);
325 QByteArray iname = "local";
326 for (uint i = 0; i < values.GetSize(); i++) {
327 lldb::SBValue v = values.GetValueAtIndex(i);
328 if (!v.IsInScope(fr))
330 getWatchDataR(v, 1, iname, wd);
332 updateWatchData(true, wd);
335 void LldbEngineGuest::requestUpdateWatchData(const Internal::WatchData &data,
336 const Internal::WatchUpdateFlags &)
339 SYNC_INFERIOR_OR(return);
341 lldb::SBValue v = m_localesCache.value(QString::fromUtf8(data.iname));
343 for (uint j = 0; j < v.GetNumChildren(); j++) {
344 lldb::SBValue vv = v.GetChildAtIndex(j);
345 getWatchDataR(vv, 1, data.iname, wd);
347 updateWatchData(false, wd);
350 void LldbEngineGuest::getWatchDataR(lldb::SBValue v, int level,
351 const QByteArray &p_iname, QList<WatchData> &wd)
353 QByteArray iname = p_iname + '.' + QByteArray(v.GetName());
354 m_localesCache.insert(QString::fromLocal8Bit(iname), v);
356 #if defined(HAVE_LLDB_PRIVATE)
357 wd += py->expand(p_iname, v, m_currentFrame, *m_process);
360 d.name = QString::fromLocal8Bit(v.GetName());
362 d.type = QByteArray(v.GetTypeName()).trimmed();
363 d.value = (QString::fromLocal8Bit(v.GetValue(m_currentFrame)));
364 d.hasChildren = v.GetNumChildren();
365 d.state = WatchData::State(0);
370 for (uint j = 0; j < v.GetNumChildren(); j++) {
371 lldb::SBValue vv = v.GetChildAtIndex(j);
372 getWatchDataR(vv, level, iname, wd);
377 void LldbEngineGuest::disassemble(quint64 pc)
380 SYNC_INFERIOR_OR(return);
382 if (!m_currentThread.IsValid())
384 for (uint j = 0; j < m_currentThread.GetNumFrames(); j++) {
385 lldb::SBFrame fr = m_currentThread.GetFrameAtIndex(j);
386 if (pc == fr.GetPCAddress().GetLoadAddress(*m_target)) {
387 QString linesStr = QString::fromLocal8Bit(fr.Disassemble());
388 DisassemblerLines lines;
389 foreach (const QString &lineStr, linesStr.split(QLatin1Char('\n'))) {
390 lines.appendLine(DisassemblerLine(lineStr));
392 disassembled(pc, lines);
396 void LldbEngineGuest::fetchFrameSource(qint64 frame)
398 QFile f(m_frame_to_file.value(frame));
399 f.open(QFile::ReadOnly);
400 frameSourceFetched(frame, QFileInfo(m_frame_to_file.value(frame)).fileName()
401 , QString::fromLocal8Bit(f.readAll()));
404 void LldbEngineGuest::addBreakpoint(BreakpointId id,
405 const Internal::BreakpointParameters &bp_)
408 SYNC_INFERIOR_OR(notifyAddBreakpointFailed(id); return);
410 Internal::BreakpointParameters bp(bp_);
412 lldb::SBBreakpoint llbp = m_target->BreakpointCreateByLocation(
413 bp.fileName.toLocal8Bit().constData(), bp.lineNumber);
414 if (llbp.IsValid()) {
415 m_breakpoints.insert(id, llbp);
417 llbp.SetIgnoreCount(bp.ignoreCount);
418 bp.ignoreCount = llbp.GetIgnoreCount();
419 bp.enabled = llbp.IsEnabled();
421 lldb::SBBreakpointLocation location = llbp.GetLocationAtIndex(0);
422 if (location.IsValid()) {
423 bp.address = location.GetLoadAddress();
425 // FIXME get those from lldb
426 bp.lineNumber = bp.lineNumber;
427 bp.fileName = bp.fileName;
428 notifyAddBreakpointOk(id);
429 showMessage(QLatin1String("[BB] ok."));
430 notifyBreakpointAdjusted(id, bp);
432 m_breakpoints.take(id);
433 showMessage(QLatin1String("[BB] failed. cant resolve yet"));
434 // notifyAddBreakpointFailed(id);
435 // notifyAddBreakpointOk(id);
438 showMessage(QLatin1String("[BB] failed. dunno."));
439 notifyAddBreakpointFailed(id);
443 void LldbEngineGuest::removeBreakpoint(BreakpointId id)
446 SYNC_INFERIOR_OR(notifyRemoveBreakpointFailed(id); return);
448 lldb::SBBreakpoint llbp = m_breakpoints.take(id);
449 llbp.SetEnabled(false);
450 notifyRemoveBreakpointOk(id);
453 void LldbEngineGuest::changeBreakpoint(BreakpointId id,
454 const Internal::BreakpointParameters &bp)
463 void LldbEngineGuest::selectThread(qint64 token)
466 SYNC_INFERIOR_OR(return);
468 m_frame_to_file.clear();
469 for (uint i = 0; i < m_process->GetNumThreads(); i++) {
470 lldb::SBThread t = m_process->GetThreadAtIndex(i);
471 if (t.GetThreadID() == token) {
474 int firstResolvableFrame = -1;
475 for (uint j = 0; j < t.GetNumFrames(); j++) {
476 lldb::SBFrame fr = t.GetFrameAtIndex(j);
478 qDebug("warning: frame %i is garbage", j);
481 lldb::SBSymbolContext context =
482 fr.GetSymbolContext(lldb::eSymbolContextEverything);
483 lldb::SBSymbol sym = fr.GetSymbol();
484 lldb::SBFunction func = fr.GetFunction();
485 lldb::SBCompileUnit tu = fr.GetCompileUnit();
486 lldb::SBModule module = fr.GetModule();
487 lldb::SBBlock block = fr.GetBlock();
488 lldb::SBBlock fblock = fr.GetFrameBlock();
489 lldb::SBLineEntry le = fr.GetLineEntry();
490 lldb::SBValueList values = fr.GetVariables(true, true, true, false);
492 qDebug()<<"\tframe "<<fr.GetFrameID();
493 qDebug() << "\t\tPC: " << ("0x" + QByteArray::number(
494 fr.GetPCAddress().GetLoadAddress(*m_target), 16)).data();
495 qDebug() << "\t\tFP: " << ("0x" + QByteArray::number(fr.GetFP(), 16)).data();
496 qDebug() << "\t\tSP: " << ("0x" + QByteArray::number(fr.GetSP(), 16)).data();
497 qDebug() << "\t\tsymbol: " << sym.IsValid() << sym.GetName() << sym.GetMangledName();
498 qDebug() << "\t\tfunction:" << func.IsValid();
499 qDebug() << "\t\ttu: " << tu.IsValid();
503 qDebug() << "\t\tmodule: " << module.IsValid() << module.GetFileSpec().IsValid()
504 << module.GetFileSpec().GetFilename();
505 qDebug() << "\t\tblock: " << block.IsValid() << block.GetInlinedName();
506 qDebug() << "\t\tfblock: " << block.IsValid() << block.GetInlinedName();
507 qDebug() << "\t\tle: " << le.IsValid() << le.GetLine()<<le.GetColumn();
508 qDebug() << "\t\tvalues: "<<values.IsValid() << values.GetSize();
509 qDebug() << "\t\tcontext: " << context.IsValid();
510 qDebug() << "\t\t\tmodule: " << context.GetModule().IsValid();
511 qDebug() << "\t\t\tfunction: " << context.GetFunction().IsValid();
512 qDebug() << "\t\t\tblock: " << context.GetBlock().IsValid();
513 qDebug() << "\t\t\tle: " << context.GetLineEntry().IsValid();
514 qDebug() << "\t\t\tsymbol: " << context.GetSymbol().IsValid();
515 // qDebug() << "\t\tdisassemly -->\n" << fr.Disassemble() << "<--";
519 QString sourceFilePath;
522 lineNumber = le.GetLine();
523 if (le.GetFileSpec().IsValid()) {
524 sourceFile = QString::fromLocal8Bit(le.GetFileSpec().GetFilename());
525 sourceFilePath = QString::fromLocal8Bit(le.GetFileSpec().GetDirectory())
526 + QLatin1String("/") + sourceFile;
527 if (firstResolvableFrame < 0)
528 firstResolvableFrame = j;
531 sourceFilePath = QFileInfo(sourceFilePath).canonicalFilePath();
533 QString functionName;
535 functionName = QString::fromLocal8Bit(func.GetName());
537 functionName = QString::fromLocal8Bit(sym.GetName());
540 frame.level = fr.GetFrameID();
542 frame.function = QString::fromLocal8Bit(func.GetName());
544 frame.function = QString::fromLocal8Bit(sym.GetName());
545 frame.from = QString::fromLocal8Bit(module.GetFileSpec().GetFilename());
546 frame.address = fr.GetPCAddress().GetLoadAddress(*m_target);
547 frame.line = lineNumber;
548 frame.file = sourceFilePath;
549 frame.usable = QFileInfo(frame.file).isReadable();
550 frames.append(frame);
551 m_frame_to_file.insert(j, frame.file);
553 currentThreadChanged(token);
555 activateFrame(firstResolvableFrame > -1 ? firstResolvableFrame : 0);
561 void LldbEngineGuest::updateThreads()
564 SYNC_INFERIOR_OR(return);
566 /* There is no way to find the StopReason of a _process_
567 * We try to emulate gdb here, by assuming there must be exactly one 'guilty' thread.
568 * However, if there are no threads at all, it must be that the process
569 * no longer exists. Let's tear down the whole session.
571 if (m_process->GetNumThreads() < 1) {
572 notifyEngineSpontaneousShutdown();
574 m_process->Destroy();
578 for (uint i = 0; i < m_process->GetNumThreads(); i++) {
579 lldb::SBThread t = m_process->GetThreadAtIndex(i);
581 qDebug("warning: thread %i is garbage", i);
585 thread.id = t.GetThreadID();
586 thread.targetId = QString::number(t.GetThreadID());
588 thread.state = QString::number(t.GetStopReason());
590 switch (t.GetStopReason()) {
591 case lldb::eStopReasonInvalid:
592 case lldb::eStopReasonNone:
593 case lldb::eStopReasonTrace:
594 thread.state = QLatin1String("running");
596 case lldb::eStopReasonBreakpoint:
597 case lldb::eStopReasonWatchpoint:
598 showStatusMessage(QLatin1String("hit breakpoint"));
599 thread.state = QLatin1String("hit breakpoint");
600 if (m_currentThread.GetThreadID() != t.GetThreadID()) {
602 currentThreadChanged(t.GetThreadID());
603 m_relistFrames = true;
606 case lldb::eStopReasonSignal:
607 showStatusMessage(QLatin1String("stopped"));
608 thread.state = QLatin1String("stopped");
609 if (m_currentThread.GetThreadID() != t.GetThreadID()) {
611 currentThreadChanged(t.GetThreadID());
612 m_relistFrames = true;
615 case lldb::eStopReasonException:
616 showStatusMessage(QLatin1String("application crashed."));
617 thread.state = QLatin1String("crashed");
618 if (m_currentThread.GetThreadID() != t.GetThreadID()) {
620 currentThreadChanged(t.GetThreadID());
621 m_relistFrames = true;
624 case lldb::eStopReasonPlanComplete:
625 thread.state = QLatin1String("crazy things happened");
629 thread.lineNumber = 0;
630 thread.name = QString::fromLocal8Bit(t.GetName());
632 lldb::SBFrame fr = t.GetFrameAtIndex(0);
634 qDebug("warning: frame 0 is garbage");
637 lldb::SBSymbolContext context = fr.GetSymbolContext(lldb::eSymbolContextEverything);
638 lldb::SBSymbol sym = fr.GetSymbol();
639 lldb::SBFunction func = fr.GetFunction();
640 lldb::SBLineEntry le = fr.GetLineEntry();
642 QString sourceFilePath;
645 lineNumber = le.GetLine();
646 if (le.GetFileSpec().IsValid()) {
647 sourceFile = QString::fromLocal8Bit(le.GetFileSpec().GetFilename());
648 sourceFilePath = QString::fromLocal8Bit(le.GetFileSpec().GetDirectory())
649 + QLatin1String("/") + sourceFile;
652 QString functionName;
654 functionName = QString::fromLocal8Bit(func.GetName());
656 functionName = QString::fromLocal8Bit(sym.GetName());
658 lldb::SBValueList values = fr.GetVariables(true, true, false, false);
659 thread.fileName = sourceFile;
660 thread.function = functionName;
661 thread.address = fr.GetPCAddress().GetLoadAddress(*m_target);
662 thread.lineNumber = lineNumber;
663 threads.append(thread);
665 listThreads(threads);
666 if (m_relistFrames) {
667 selectThread(m_currentThread.GetThreadID());
668 m_relistFrames = false;
672 void LldbEngineGuest::lldbEvent(lldb::SBEvent *ev)
674 qDebug() << "lldbevent" << ev->GetType() <<
675 m_process->GetState() << (int)state();
677 uint32_t etype = ev->GetType();
681 switch (m_process->GetState()) {
682 case lldb::eStateRunning: // 5
686 notifyInferiorPid(m_process->GetProcessID());
688 case EngineRunRequested:
689 notifyEngineRunAndInferiorRunOk();
691 case InferiorRunRequested:
692 notifyInferiorRunOk();
695 notifyInferiorRunRequested();
696 notifyInferiorRunOk();
702 case lldb::eStateExited: // 9
707 case InferiorShutdownRequested:
708 notifyInferiorShutdownOk();
711 m_relistFrames = true;
713 notifyEngineSpontaneousShutdown();
715 m_process->Destroy();
722 case lldb::eStateStopped: // 4
727 case InferiorShutdownRequested:
728 notifyInferiorShutdownOk();
731 m_relistFrames = true;
733 notifyInferiorSpontaneousStop();
736 m_relistFrames = true;
741 case lldb::eStateCrashed: // 7
746 case InferiorShutdownRequested:
747 notifyInferiorShutdownOk();
750 m_relistFrames = true;
752 notifyInferiorSpontaneousStop();
759 qDebug("unexpected ProcessEvent");
769 } // namespace Internal
770 } // namespace Debugger