diff options
-rw-r--r-- | share/qtcreator/debugger/dumper.py | 5 | ||||
-rw-r--r-- | share/qtcreator/debugger/gdbbridge.py | 5 | ||||
-rw-r--r-- | share/qtcreator/debugger/lldbbridge.py | 332 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 2 | ||||
-rw-r--r-- | src/plugins/debugger/lldb/lldbengine.cpp | 586 | ||||
-rw-r--r-- | src/plugins/debugger/lldb/lldbengine.h | 55 |
6 files changed, 403 insertions, 582 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 418b28ac19..b7667a5c29 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -1702,10 +1702,7 @@ class DumperBase: formats = (',formats=\"%s\"' % str(value)[1:-1]) if len(value) else '' msg += '{type="%s"%s%s},' % (key, editable, formats) msg += ']' - self.reportDumpers(msg) - - def reportDumpers(self, msg): - raise NotImplementedError + return msg def reloadDumpers(self, args): for mod in self.dumpermodules: diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index fb45f2ce86..123d2f5957 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -1665,10 +1665,7 @@ class Dumper(DumperBase): gdb.execute("quit") def loadDumpers(self, args): - self.setupDumpers() - - def reportDumpers(self, msg): - print(msg) + print(self.setupDumpers()) def profile1(self, args): """Internal profiling""" diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 3f18e6f20f..8a4da00499 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -547,10 +547,9 @@ class Dumper(DumperBase): self.report('output="%s"' % result.GetOutput()) else: self.report('error="%s"' % result.GetError()) - self.reportData() def put(self, stuff): - sys.stdout.write(stuff) + self.out += stuff def isMovableType(self, type): if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer): @@ -656,7 +655,6 @@ class Dumper(DumperBase): return None def setupInferior(self, args): - self.reportToken(args) error = lldb.SBError() self.executable_ = args['executable'] @@ -697,12 +695,8 @@ class Dumper(DumperBase): else: self.target = self.debugger.CreateTarget(None, None, None, True, error) - if self.target.IsValid(): - for bp in args['bkpts']: - self.insertBreakpoint(bp) - - state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed" - self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_)) + state = 1 if self.target.IsValid() else 0 + self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args) def runEngine(self, args): self.prepare(args) @@ -710,7 +704,6 @@ class Dumper(DumperBase): s.start() def prepare(self, args): - self.reportToken(args) error = lldb.SBError() listener = self.debugger.GetListener() @@ -730,7 +723,7 @@ class Dumper(DumperBase): self.debugger.GetListener(), self.remoteChannel_, None, error) if not error.Success(): - self.reportError(error) + self.report(self.describeError(error)) self.reportState("enginerunfailed") return # Even if it stops it seems that LLDB assumes it is running @@ -741,7 +734,6 @@ class Dumper(DumperBase): coreFile = args.get('coreFile', ''); self.process = self.target.LoadCore(coreFile) self.reportState("enginerunokandinferiorunrunnable") - #self.reportContinuation(args) else: launchInfo = lldb.SBLaunchInfo(self.processArgs_) launchInfo.SetWorkingDirectory(os.getcwd()) @@ -751,7 +743,7 @@ class Dumper(DumperBase): self.createBreakpointAtMain() self.process = self.target.Launch(launchInfo, error) if not error.Success(): - self.reportError(error) + self.report(self.describeError(error)) self.reportState("enginerunfailed") return self.report('pid="%s"' % self.process.GetProcessID()) @@ -769,15 +761,23 @@ class Dumper(DumperBase): def describeError(self, error): desc = lldb.SBStream() error.GetDescription(desc) - result = 'error={type="%s"' % error.GetType() + result = 'success="%s",' % int(error.Success()) + result += 'error={type="%s"' % error.GetType() + if error.GetType(): + result += ',status="%s"' % error.GetCString() result += ',code="%s"' % error.GetError() result += ',desc="%s"}' % desc.GetData() return result - def reportError(self, error): - self.report(self.describeError(error)) - if error.GetType(): - self.reportStatus(error.GetCString()) + def describeStatus(self, status): + return 'status="%s",' % status + + def describeLocation(self, frame): + if int(frame.pc) == 0xffffffffffffffff: + return '' + file = fileName(frame.line_entry.file) + line = frame.line_entry.line + return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc) def currentThread(self): return None if self.process is None else self.process.GetSelectedThread() @@ -786,13 +786,6 @@ class Dumper(DumperBase): thread = self.currentThread() return None if thread is None else thread.GetSelectedFrame() - def reportLocation(self, frame): - if int(frame.pc) != 0xffffffffffffffff: - file = fileName(frame.line_entry.file) - line = frame.line_entry.line - self.report('location={file="%s",line="%s",addr="%s"}' - % (file, line, frame.pc)) - def firstStoppedThread(self): for i in xrange(0, self.process.GetNumThreads()): thread = self.process.GetThreadAtIndex(i) @@ -805,9 +798,8 @@ class Dumper(DumperBase): return thread return None - def reportThreads(self, args): - self.reportToken(args) - result = 'threads={threads=[' + def fetchThreads(self, args): + result = 'threads=[' for i in xrange(0, self.process.GetNumThreads()): thread = self.process.GetThreadAtIndex(i) if thread.is_stopped: @@ -834,12 +826,8 @@ class Dumper(DumperBase): result += ',file="%s"' % fileName(frame.line_entry.file) result += '}},' - result += ']},' - self.report(result) - - def reportCurrentThread(self, args): - self.reportToken(args) - self.report('current-thread={id="%s"}' % self.currentThread().id) + result += '],current-thread-id="%s"' % self.currentThread().id + self.reportResult(result, args) def firstUsableFrame(self, thread): for i in xrange(10): @@ -850,17 +838,16 @@ class Dumper(DumperBase): return i return None - def reportStack(self, args): - self.reportToken(args) + def fetchStack(self, args): if not self.process: - self.report('msg="No process"') + self.reportResult('msg="No process"', args) return thread = self.currentThread() if not thread: - self.report('msg="No thread"') + self.reportResult('msg="No thread"', args) return - self.reportLocation(thread.GetFrameAtIndex(0)) # FIXME + self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME isNativeMixed = int(args.get('nativeMixed', 0)) @@ -919,8 +906,10 @@ class Dumper(DumperBase): result += ',hasmore="%d"' % isLimited result += ',limit="%d"' % limit result += '}' - self.report(result) - self.reportContinuation(args) + self.reportResult(result, args) + + def reportResult(self, result, args): + self.report('result={token="%s",%s}' % (args.get("token", 0), result)) def reportToken(self, args): if "token" in args: @@ -928,10 +917,6 @@ class Dumper(DumperBase): # logview pane feature. self.report('token(\"%s\")' % args["token"]) - def reportContinuation(self, args): - if not self.isShuttingDown_ and "continuation" in args: - self.report('continuation=\"%s\"' % args["continuation"]) - def extractBlob(self, base, size): if size == 0: return Blob("") @@ -1171,11 +1156,10 @@ class Dumper(DumperBase): with SubItem(self, child): self.putItem(child) - def reportVariables(self, args = {}): - with self.outputLock: - sys.stdout.write("@\n") - self.reportVariablesHelper(args) - sys.stdout.write("@\n") + def reportVariables(self, args): + self.out = "" + self.reportVariablesHelper(args) + self.reportResult(self.out, args) def reportVariablesHelper(self, args = {}): frame = self.currentFrame() @@ -1186,7 +1170,7 @@ class Dumper(DumperBase): isPartial = len(partialVariable) > 0 self.currentIName = 'local' - self.put('all={data=[') + self.put('data=[') self.anonNumber = 0 shadowed = {} ids = {} # Filter out duplicates entries at the same address. @@ -1248,19 +1232,11 @@ class Dumper(DumperBase): self.handleWatches(args) - self.put('],partial="%d"}' % isPartial) + self.put('],partial="%d"' % isPartial) - def reportData(self, _ = None): + def fetchRegisters(self, args = None): if self.process is None: - self.report('process="none"') - else: - state = self.process.GetState() - if state == lldb.eStateStopped: - self.reportVariables() - - def reportRegisters(self, _ = None): - if self.process is None: - self.report('process="none"') + result = 'process="none"' else: frame = self.currentFrame() if frame: @@ -1273,10 +1249,9 @@ class Dumper(DumperBase): result += ',size="%s"' % reg.GetByteSize() result += ',type="%s"},' % reg.GetType() result += ']' - self.report(result) + self.reportResult(result, args) def setRegister(self, args): - self.reportToken(args) name = args["name"] value = args["value"] result = lldb.SBCommandReturnObject() @@ -1284,55 +1259,50 @@ class Dumper(DumperBase): interp.HandleCommand("register write %s %s" % (name, value), result) success = result.Succeeded() if success: - self.report('output="%s"' % result.GetOutput()) + self.reportResult('output="%s"' % result.GetOutput(), args) return # Try again with register write xmm0 "{0x00 ... 0x02}" syntax: vec = ' '.join(["0x" + value[i:i+2] for i in range(2, len(value), 2)]) success = interp.HandleCommand('register write %s "{%s}"' % (name, vec), result) if success: - self.report('output="%s"' % result.GetOutput()) - return - self.report('error="%s"' % result.GetError()) + self.reportResult('output="%s"' % result.GetOutput(), args) + else: + self.reportResult('error="%s"' % result.GetError(), args) def report(self, stuff): with self.outputLock: sys.stdout.write("@\n" + stuff + "@\n") - def reportStatus(self, msg): - self.report('statusmessage="%s"' % msg) + def reportState(self, state): + self.report('state="%s"' % state) - def interruptInferior(self, _ = None): - self.reportToken(args) + def interruptInferior(self, args): if self.process is None: - self.reportStatus("No process to interrupt.") - return - self.isInterrupting_ = True - error = self.process.Stop() - self.reportError(error) + self.reportResult('status="No process to interrupt",success="0"', args) + else: + self.isInterrupting_ = True + error = self.process.Stop() + self.reportResult(describeError(error), args) - def detachInferior(self, _ = None): - self.reportToken(args) + def detachInferior(self, args): if self.process is None: - self.reportStatus("No process to detach from.") + self.reportResult('status="No process to detach from."', args) else: error = self.process.Detach() - self.reportError(error) - self.reportData() + self.reportResult(self.describeError(error), args) - def continueInferior(self, _ = None): - self.reportToken(args) + def continueInferior(self, args): if self.process is None: - self.reportStatus("No process to continue.") + self.reportResult('status="No process to continue."', args) else: + # Can fail when attaching to GDBserver. error = self.process.Continue() - self.reportError(error) - # Happens when attaching to GDBserver. - if not error.Success(): - self.reportState("inferiorill") + self.reportResult(self.describeError(error), args) - def quitDebugger(self, _ = None): + def quitDebugger(self, args): self.reportState("inferiorshutdownrequested") self.process.Kill() + self.reportResult('', args) def handleEvent(self, event): out = lldb.SBStream() @@ -1407,9 +1377,6 @@ class Dumper(DumperBase): elif eventType == lldb.SBProcess.eBroadcastBitProfileData: pass - def reportState(self, state): - self.report('state="%s"' % state) - def describeBreakpoint(self, bp): isWatch = isinstance(bp, lldb.SBWatchpoint) if isWatch: @@ -1419,32 +1386,28 @@ class Dumper(DumperBase): if not bp.IsValid(): return result += ',hitcount="%s"' % bp.GetHitCount() - if hasattr(bp, 'GetThreadID'): - result += ',threadid="%s"' % bp.GetThreadID() - if hasattr(bp, 'IsOneShot'): - result += ',oneshot="%s"' % (1 if bp.IsOneShot() else 0) - if hasattr(bp, 'GetCondition'): - cond = bp.GetCondition() - result += ',condition="%s"' % self.hexencode("" if cond is None else cond) + result += ',threadid="%s"' % bp.GetThreadID() + result += ',oneshot="%s"' % (1 if bp.IsOneShot() else 0) + cond = bp.GetCondition() + result += ',condition="%s"' % self.hexencode("" if cond is None else cond) result += ',enabled="%s"' % (1 if bp.IsEnabled() else 0) result += ',valid="%s"' % (1 if bp.IsValid() else 0) result += ',ignorecount="%s"' % bp.GetIgnoreCount() result += ',locations=[' lineEntry = None - if hasattr(bp, 'GetNumLocations'): - for i in xrange(bp.GetNumLocations()): - loc = bp.GetLocationAtIndex(i) - addr = loc.GetAddress() - lineEntry = addr.GetLineEntry() - result += '{locid="%s"' % loc.GetID() - result += ',func="%s"' % addr.GetFunction().GetName() - result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0) - result += ',resolved="%s"' % (1 if loc.IsResolved() else 0) - result += ',valid="%s"' % (1 if loc.IsValid() else 0) - result += ',ignorecount="%s"' % loc.GetIgnoreCount() - result += ',file="%s"' % lineEntry.GetFileSpec() - result += ',line="%s"' % lineEntry.GetLine() - result += ',addr="%s"},' % addr.GetFileAddress() + for i in xrange(bp.GetNumLocations()): + loc = bp.GetLocationAtIndex(i) + addr = loc.GetAddress() + lineEntry = addr.GetLineEntry() + result += '{locid="%s"' % loc.GetID() + result += ',func="%s"' % addr.GetFunction().GetName() + result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0) + result += ',resolved="%s"' % (1 if loc.IsResolved() else 0) + result += ',valid="%s"' % (1 if loc.IsValid() else 0) + result += ',ignorecount="%s"' % loc.GetIgnoreCount() + result += ',file="%s"' % lineEntry.GetFileSpec() + result += ',line="%s"' % lineEntry.GetLine() + result += ',addr="%s"},' % addr.GetFileAddress() result += ']' if lineEntry is not None: result += ',file="%s"' % lineEntry.GetFileSpec() @@ -1463,8 +1426,8 @@ class Dumper(DumperBase): self.insertQmlBreakpoint(args) return + extra = '' more = True - modelId = args['modelid'] if bpType == BreakpointByFileAndLine: bp = self.target.BreakpointCreateByLocation( str(args["fileName"]), int(args["lineNumber"])) @@ -1484,7 +1447,7 @@ class Dumper(DumperBase): error = lldb.SBError() bp = self.target.WatchAddress(args["address"], 4, False, True, error) #warn("BPNEW: %s" % bp) - self.reportError(error) + extra = self.describeError(error) elif bpType == WatchpointAtExpression: # FIXME: Top level-only for now. try: @@ -1503,17 +1466,13 @@ class Dumper(DumperBase): if more: bp.SetIgnoreCount(int(args["ignorecount"])) - if hasattr(bp, 'SetCondition'): - bp.SetCondition(self.hexdecode(args["condition"])) + bp.SetCondition(self.hexdecode(args["condition"])) bp.SetEnabled(bool(args["enabled"])) - if hasattr(bp, 'SetOneShot'): - bp.SetOneShot(bool(args["oneshot"])) - self.report('breakpoint-added={%s,modelid="%s"}' % (self.describeBreakpoint(bp), modelId)) + bp.SetOneShot(bool(args["oneshot"])) + self.reportResult(self.describeBreakpoint(bp) + extra, args) def changeBreakpoint(self, args): - self.reportToken(args) lldbId = int(args["lldbid"]) - modelId = args['modelid'] if lldbId > qqWatchpointOffset: bp = self.target.FindWatchpointByID(lldbId) else: @@ -1521,21 +1480,17 @@ class Dumper(DumperBase): bp.SetIgnoreCount(int(args["ignorecount"])) bp.SetCondition(self.hexdecode(args["condition"])) bp.SetEnabled(bool(args["enabled"])) - if hasattr(bp, 'SetOneShot'): - bp.SetOneShot(bool(args["oneshot"])) - self.report('breakpoint-changed={%s,modelid="%s"}' - % (self.describeBreakpoint(bp), modelId)) + bp.SetOneShot(bool(args["oneshot"])) + self.reportResult(self.describeBreakpoint(bp), args) def removeBreakpoint(self, args): - self.reportToken(args) lldbId = int(args['lldbid']) - modelId = args['modelid'] if lldbId > qqWatchpointOffset: res = self.target.DeleteWatchpoint(lldbId - qqWatchpointOffset) res = self.target.BreakpointDelete(lldbId) - self.report('breakpoint-removed={modelid="%s"}' % modelId) + self.reportResult('success="%s"' % int(res), args) - def listModules(self, args): + def fetchModules(self, args): result = 'modules=[' for i in xrange(self.target.GetNumModules()): module = self.target.GetModuleAtIndex(i) @@ -1551,10 +1506,9 @@ class Dumper(DumperBase): #result += '}' result += '},' result += ']' - self.report(result) + self.reportResult(result, args) - def listSymbols(self, args): - self.reportToken(args) + def fetchSymbols(self, args): moduleName = args['module'] #file = lldb.SBFileSpec(moduleName) #module = self.target.FindModule(file) @@ -1562,8 +1516,7 @@ class Dumper(DumperBase): module = self.target.GetModuleAtIndex(i) if module.file.fullpath == moduleName: break - result = 'symbols={module="%s"' % moduleName - result += ',valid="%s"' % module.IsValid() + result = 'symbols={valid="%s"' % module.IsValid() result += ',sections="%s"' % module.GetNumSections() result += ',symbols=[' for symbol in module.symbols: @@ -1576,22 +1529,21 @@ class Dumper(DumperBase): result += ',size="%s"' % (endAddress - startAddress) result += '},' result += ']}' - self.report(result) + self.reportResult(result, args) - def executeNext(self, _ = None): - self.reportToken(args) + def executeNext(self, args): self.currentThread().StepOver() + self.reportResult('', args) - def executeNextI(self, _ = None): - self.reportToken(args) + def executeNextI(self, args): self.currentThread().StepInstruction(lldb.eOnlyThisThread) + self.reportResult('', args) - def executeStep(self, _ = None): - self.reportToken(args) + def executeStep(self, args): self.currentThread().StepInto() + self.reportResult('', args) - def shutdownInferior(self, _ = None): - self.reportToken(args) + def shutdownInferior(self, args): self.isShuttingDown_ = True if self.process is None: self.reportState("inferiorshutdownok") @@ -1600,19 +1552,20 @@ class Dumper(DumperBase): if state == lldb.eStateStopped: self.process.Kill() self.reportState("inferiorshutdownok") + self.reportResult('', args) - def quit(self, args = {}): - self.reportToken(args) + def quit(self, args): self.reportState("engineshutdownok") self.process.Kill() + self.reportResult('', args) - def executeStepI(self, args = {}): - self.reportToken(args) + def executeStepI(self, args): self.currentThread().StepInstruction(lldb.eOnlyThisThread) + self.reportResult('', args) def executeStepOut(self, args = {}): - self.reportToken(args) self.currentThread().StepOut() + self.reportResult('', args) def executeRunToLocation(self, args): self.reportToken(args) @@ -1623,28 +1576,26 @@ class Dumper(DumperBase): bp = self.target.BreakpointCreateByAddress(addr) if bp.GetNumLocations() == 0: self.target.BreakpointDelete(bp.GetID()) - self.reportStatus("No target location found.") - self.reportLocation(frame) + self.reportResult(self.describeStatus("No target location found.") + + self.describeLocation(frame), args) return bp.SetOneShot(True) + self.reportResult('', args) self.process.Continue() else: frame = self.currentFrame() file = args['file'] line = int(args['line']) error = self.currentThread().StepOverUntil(frame, lldb.SBFileSpec(file), line) - if error.GetType(): - self.reportState("running") - self.reportState("stopped") - self.reportError(error) - else: - self.reportData() + self.reportResult(self.describeError(error), args) + self.reportState("running") + self.reportState("stopped") def executeJumpToLocation(self, args): self.reportToken(args) frame = self.currentFrame() if not frame: - self.reportStatus("No frame available.") + self.reportResult(self.describeStatus("No frame available."), args) return addr = args.get('address', 0) if addr: @@ -1654,16 +1605,13 @@ class Dumper(DumperBase): str(args['file']), int(args['line'])) if bp.GetNumLocations() == 0: self.target.BreakpointDelete(bp.GetID()) - self.reportStatus("No target location found.") - self.reportLocation(frame) - return - loc = bp.GetLocationAtIndex(0) - self.target.BreakpointDelete(bp.GetID()) - if frame.SetPC(loc.GetLoadAddress()): - self.report("Jumped.") + status = "No target location found." else: - self.report("Cannot jump.") - self.reportLocation(frame) + loc = bp.GetLocationAtIndex(0) + self.target.BreakpointDelete(bp.GetID()) + res = frame.SetPC(loc.GetLoadAddress()) + status = "Jumped." if res else "Cannot jump." + self.reportResult(self.describeStatus(status) + self.describeLocation(frame), args) def breakList(self): result = lldb.SBCommandReturnObject() @@ -1675,22 +1623,18 @@ class Dumper(DumperBase): self.reportToken(args) thread = args['thread'] self.currentThread().SetSelectedFrame(args['index']) - self.reportContinuation(args) + self.reportResult('', args) def selectThread(self, args): self.reportToken(args) self.process.SetSelectedThreadByID(args['id']) + self.reportResult('', args) - def requestModuleSymbols(self, frame): - self.reportToken(args) - self.handleCommand("target module list " + frame) - - def createFullBacktrace(self, _ = None): - self.reportToken(args) + def fetchFullBacktrace(self, _ = None): command = "thread backtrace all" result = lldb.SBCommandReturnObject() self.debugger.GetCommandInterpreter().HandleCommand(command, result) - self.report('full-backtrace="%s"' % self.hexencode(result.GetOutput())) + self.reportResult(self.hexencode(result.GetOutput()), {}) def executeDebuggerCommand(self, args): self.reportToken(args) @@ -1702,8 +1646,8 @@ class Dumper(DumperBase): error = str(result.GetError()) self.report('success="%d",output="%s",error="%s"' % (success, output, error)) - def updateData(self, args): - self.reportToken(args) + def fetchLocals(self, args): + self.output = '' self.expandedINames = set(args.get('expanded', [])) self.autoDerefPointers = int(args.get('autoderef', '0')) self.sortStructMembers = bool(args.get("sortStructMembers", True)); @@ -1715,8 +1659,7 @@ class Dumper(DumperBase): self.formats = args.get("formats", {}) self.reportVariables(args) - def disassemble(self, args): - self.reportToken(args) + def fetchDisassembler(self, args): functionName = args.get('function', '') flavor = args.get('flavor', '') function = None @@ -1739,8 +1682,7 @@ class Dumper(DumperBase): currentLine = None hunks = dict() sources = dict() - result = 'disassembly={cookie="%s",' % args['cookie'] - result += ',lines=[' + result = 'lines=[' for insn in instructions: comment = insn.GetComment(self.target) addr = insn.GetAddress() @@ -1774,26 +1716,21 @@ class Dumper(DumperBase): if comment: result += ',comment="%s"' % comment result += ',offset="%s"}' % (loadAddr - base) - self.report(result + ']') + self.reportResult(result + ']', args) def loadDumpers(self, args): - self.reportToken(args) - self.setupDumpers() - - def reportDumpers(self, msg): - self.report(msg) + msg = self.setupDumpers() + self.reportResult(msg, args) def fetchMemory(self, args): - self.reportToken(args) address = args['address'] length = args['length'] error = lldb.SBError() contents = self.process.ReadMemory(address, length, error) - result = 'memory={cookie="%s",' % args['cookie'] - result += ',address="%s",' % address + result = 'address="%s",' % address result += self.describeError(error) - result += ',contents="%s"}' % self.hexencode(contents) - self.report(result) + result += ',contents="%s"' % self.hexencode(contents) + self.reportResult(result, args) def findValueByExpression(self, exp): # FIXME: Top level-only for now. @@ -1808,8 +1745,7 @@ class Dumper(DumperBase): value = self.hexdecode(args['value']) lhs = self.findValueByExpression(exp) lhs.SetValueFromCString(value, error) - self.reportError(error) - self.reportVariables() + self.reportResult(describeError(error), args) def createResolvePendingBreakpointsHookBreakpoint(self, args): if self.qmlTriggeredBreakpoint is None: @@ -1832,7 +1768,7 @@ class Tester(Dumper): self.passExceptions = True self.sortStructMembers = True - self.loadDumpers({}) + self.loadDumpers({'token': 1}) error = lldb.SBError() self.target = self.debugger.CreateTarget(binary, None, None, True, error) @@ -1890,8 +1826,8 @@ class Tester(Dumper): if line != 0: self.report = savedReport self.process.SetSelectedThread(stoppedThread) - self.reportVariables() - #self.reportLocation(frame) + self.reportVariables({'token':2}) + #self.describeLocation(frame) self.report("@NS@%s@" % self.qtNamespace()) #self.report("ENV=%s" % os.environ.items()) #self.report("DUMPER=%s" % self.qqDumpers) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 0e26fdcb18..21eec10980 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4719,7 +4719,7 @@ void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms) cmd.arg("partialVariable", params.partialVariable); cmd.arg("sortStructMembers", boolSetting(SortStructMembers)); cmd.flags = Discardable | InUpdateLocals; - cmd.callback = [this](const DebuggerResponse &r) { handleStackFrame(r); }; + cmd.callback = CB(handleStackFrame); runCommand(cmd); cmd.arg("passExceptions", true); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 6500b766bd..ceb135f313 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -107,7 +107,7 @@ LldbEngine::LldbEngine(const DebuggerRunParameters &startParameters) connect(action(AutoDerefPointers), &SavedAction::valueChanged, this, &LldbEngine::updateLocals); connect(action(CreateFullBacktrace), &QAction::triggered, - this, &LldbEngine::createFullBacktrace); + this, &LldbEngine::fetchFullBacktrace); connect(action(UseDebuggingHelpers), &SavedAction::valueChanged, this, &LldbEngine::updateLocals); connect(action(UseDynamicType), &SavedAction::valueChanged, @@ -138,6 +138,7 @@ void LldbEngine::runCommand(const DebuggerCommand &command_) QByteArray token = QByteArray::number(tok); QByteArray cmd = command.function + "({" + command.args + "})"; showMessage(_(token + cmd + '\n'), LogInput); + m_commandForToken[currentToken()] = command; m_lldbProc.write("script theDumper." + cmd + "\n"); } @@ -316,12 +317,11 @@ void LldbEngine::setupInferior() } DebuggerCommand cmd1("loadDumpers"); + cmd1.callback = [this](const DebuggerResponse &response) { + watchHandler()->addDumpers(response.data); + }; runCommand(cmd1); -} -// FIXME: splitting of setupInferior() necessary to support LLDB <= 310 - revert asap -void LldbEngine::setupInferiorStage2() -{ const DebuggerRunParameters &rp = runParameters(); QString executable; @@ -329,33 +329,16 @@ void LldbEngine::setupInferiorStage2() QtcProcess::prepareCommand(QFileInfo(rp.executable).absoluteFilePath(), rp.processArgs, &executable, &args); - DebuggerCommand cmd("setupInferior"); - cmd.arg("executable", executable); - cmd.arg("breakOnMain", rp.breakOnMain); - cmd.arg("useTerminal", rp.useTerminal); - cmd.arg("startMode", rp.startMode); - - cmd.beginList("bkpts"); - foreach (Breakpoint bp, breakHandler()->unclaimedBreakpoints()) { - if (acceptsBreakpoint(bp)) { - showMessage(_("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2") - .arg(bp.id().toString()).arg(bp.state())); - bp.setEngine(this); - bp.notifyBreakpointInsertProceeding(); - cmd.beginGroup(); - bp.addToCommand(&cmd); - cmd.endGroup(); - } else { - showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE") - .arg(bp.id().toString()).arg(bp.state())); - } - } - cmd.endList(); + DebuggerCommand cmd2("setupInferior"); + cmd2.arg("executable", executable); + cmd2.arg("breakOnMain", rp.breakOnMain); + cmd2.arg("useTerminal", rp.useTerminal); + cmd2.arg("startMode", rp.startMode); - cmd.beginList("processArgs"); + cmd2.beginList("processArgs"); foreach (const QString &arg, args.toUnixArgs()) - cmd.arg(arg.toUtf8().toHex()); - cmd.endList(); + cmd2.arg(arg.toUtf8().toHex()); + cmd2.endList(); if (rp.useTerminal) { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); @@ -365,28 +348,45 @@ void LldbEngine::setupInferiorStage2() ? QString::fromLatin1("Attaching to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID) : QString::fromLatin1("Attaching to %1").arg(attachedPID); showMessage(msg, LogMisc); - cmd.arg("attachPid", attachedPID); + cmd2.arg("attachPid", attachedPID); } else { - cmd.arg("startMode", rp.startMode); + cmd2.arg("startMode", rp.startMode); // it is better not to check the start mode on the python sid (as we would have to duplicate the // enum values), and thus we assume that if the rp.attachPID is valid we really have to attach QTC_CHECK(rp.attachPID <= 0 || (rp.startMode == AttachCrashedExternal || rp.startMode == AttachExternal)); - cmd.arg("attachPid", rp.attachPID); - cmd.arg("sysRoot", rp.deviceSymbolsRoot.isEmpty() ? rp.sysRoot : rp.deviceSymbolsRoot); - cmd.arg("remoteChannel", ((rp.startMode == AttachToRemoteProcess + cmd2.arg("attachPid", rp.attachPID); + cmd2.arg("sysRoot", rp.deviceSymbolsRoot.isEmpty() ? rp.sysRoot : rp.deviceSymbolsRoot); + cmd2.arg("remoteChannel", ((rp.startMode == AttachToRemoteProcess || rp.startMode == AttachToRemoteServer) ? rp.remoteChannel : QString())); - cmd.arg("platform", rp.platform); + cmd2.arg("platform", rp.platform); QTC_CHECK(!rp.continueAfterAttach || (rp.startMode == AttachToRemoteProcess || rp.startMode == AttachExternal || rp.startMode == AttachToRemoteServer)); m_continueAtNextSpontaneousStop = false; } - runCommand(cmd); + cmd2.callback = [this](const DebuggerResponse &response) { + bool success = response.data["success"].toInt(); + if (success) { + foreach (Breakpoint bp, breakHandler()->unclaimedBreakpoints()) { + if (acceptsBreakpoint(bp)) { + bp.setEngine(this); + insertBreakpoint(bp); + } else { + showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE") + .arg(bp.id().toString()).arg(bp.state())); + } + } + notifyInferiorSetupOk(); + } else { + notifyInferiorSetupFailed(); + } + }; + runCommand(cmd2); } void LldbEngine::runEngine() @@ -395,10 +395,8 @@ void LldbEngine::runEngine() QTC_ASSERT(state() == EngineRunRequested, qDebug() << state(); return); showStatusMessage(tr("Running requested..."), 5000); DebuggerCommand cmd("runEngine"); - if (rp.startMode == AttachCore) { + if (rp.startMode == AttachCore) cmd.arg("coreFile", rp.coreFile); - cmd.arg("continuation", "updateAll"); - } runCommand(cmd); } @@ -447,7 +445,12 @@ void LldbEngine::continueInferior() { resetLocation(); notifyInferiorRunRequested(); - runCommand("continueInferior"); + DebuggerCommand cmd("continueInferior"); + cmd.callback = [this](const DebuggerResponse &response) { + if (response.resultClass == ResultError) + notifyEngineIll(); + }; + runCommand(cmd); } void LldbEngine::handleResponse(const QByteArray &response) @@ -457,72 +460,31 @@ void LldbEngine::handleResponse(const QByteArray &response) foreach (const GdbMi &item, all.children()) { const QByteArray name = item.name(); - if (name == "all") { - updateLocalsView(item); - watchHandler()->notifyUpdateFinished(); - } else if (name == "dumpers") { - watchHandler()->addDumpers(item); - setupInferiorStage2(); - } else if (name == "stack") - refreshStack(item); - else if (name == "registers") - refreshRegisters(item); - else if (name == "threads") - refreshThreads(item); - else if (name == "current-thread") - refreshCurrentThread(item); - else if (name == "typeinfo") - refreshTypeInfo(item); - else if (name == "state") - refreshState(item); - else if (name == "location") - refreshLocation(item); - else if (name == "modules") - refreshModules(item); - else if (name == "symbols") - refreshSymbols(item); - else if (name == "breakpoint-added") - refreshAddedBreakpoint(item); - else if (name == "breakpoint-changed") - refreshChangedBreakpoint(item); - else if (name == "breakpoint-removed") - refreshRemovedBreakpoint(item); - else if (name == "output") - refreshOutput(item); - else if (name == "disassembly") - refreshDisassembly(item); - else if (name == "memory") - refreshMemory(item); - else if (name == "full-backtrace") - showFullBacktrace(item); - else if (name == "continuation") - handleContinuation(item); - else if (name == "statusmessage") { - QString msg = QString::fromUtf8(item.data()); + if (name == "result") { + QString msg = item["status"].toUtf8(); if (msg.size()) msg[0] = msg.at(0).toUpper(); showStatusMessage(msg); - } - } -} -void LldbEngine::handleContinuation(const GdbMi &data) -{ - if (data.data() == "updateLocals") { - updateLocals(); - } else if (data.data() == "updateAll") { - updateAll(); - } else { - QTC_ASSERT(false, qDebug() << "Unknown continuation: " << data.data()); + int token = item["token"].toInt(); + showMessage(QString::fromLatin1("%1^").arg(token), LogOutput); + if (m_commandForToken.contains(token)) { + DebuggerCommand cmd = m_commandForToken.take(token); + DebuggerResponse response; + response.token = token; + response.data = item; + if (cmd.callback) + cmd.callback(response); + } + } else if (name == "state") + handleStateNotification(item); + else if (name == "location") + handleLocationNotification(item); + else if (name == "output") + handleOutputNotification(item); } } -void LldbEngine::showFullBacktrace(const GdbMi &data) -{ - Internal::openTextEditor(_("Backtrace $"), - QString::fromUtf8(QByteArray::fromHex(data.data()))); -} - void LldbEngine::executeRunToLine(const ContextData &data) { resetLocation(); @@ -559,13 +521,8 @@ void LldbEngine::activateFrame(int frameIndex) return; StackHandler *handler = stackHandler(); - - const int n = handler->stackSize(); - if (frameIndex == n) { - DebuggerCommand cmd("reportStack"); - cmd.arg("nativeMixed", isNativeMixedActive()); - cmd.arg("stacklimit", n * 10 + 3); - runCommand(cmd); + if (frameIndex == handler->stackSize()) { + fetchStack(handler->stackSize() * 10 + 3); return; } @@ -576,20 +533,23 @@ void LldbEngine::activateFrame(int frameIndex) DebuggerCommand cmd("activateFrame"); cmd.arg("index", frameIndex); cmd.arg("thread", threadsHandler()->currentThread().raw()); - cmd.arg("continuation", "updateLocals"); runCommand(cmd); + + updateLocals(); + reloadRegisters(); } void LldbEngine::selectThread(ThreadId threadId) { - DebuggerCommand cmd1("selectThread"); - cmd1.arg("id", threadId.raw()); - runCommand(cmd1); - - DebuggerCommand cmd("reportStack"); - cmd.arg("nativeMixed", isNativeMixedActive()); - cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt()); - cmd.arg("continuation", "updateLocals"); + DebuggerCommand cmd("selectThread"); + cmd.arg("id", threadId.raw()); + cmd.callback = [this](const DebuggerResponse &) { + DebuggerCommand cmd("fetchStack"); + cmd.arg("nativeMixed", isNativeMixedActive()); + cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt()); + runCommand(cmd); + updateLocals(); + }; runCommand(cmd); } @@ -620,6 +580,10 @@ bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const void LldbEngine::insertBreakpoint(Breakpoint bp) { DebuggerCommand cmd("insertBreakpoint"); + cmd.callback = [this, bp](const DebuggerResponse &response) { + QTC_CHECK(bp.state() == BreakpointInsertProceeding); + updateBreakpointData(bp, response.data, true); + }; bp.addToCommand(&cmd); bp.notifyBreakpointInsertProceeding(); runCommand(cmd); @@ -630,6 +594,10 @@ void LldbEngine::changeBreakpoint(Breakpoint bp) const BreakpointResponse &response = bp.response(); DebuggerCommand cmd("changeBreakpoint"); cmd.arg("lldbid", response.id.toByteArray()); + cmd.callback = [this, bp](const DebuggerResponse &response) { + QTC_CHECK(!bp.isValid() || bp.state() == BreakpointChangeProceeding); + updateBreakpointData(bp, response.data, false); + }; bp.addToCommand(&cmd); bp.notifyBreakpointChangeProceeding(); runCommand(cmd); @@ -638,19 +606,23 @@ void LldbEngine::changeBreakpoint(Breakpoint bp) void LldbEngine::removeBreakpoint(Breakpoint bp) { const BreakpointResponse &response = bp.response(); - DebuggerCommand cmd("removeBreakpoint"); - cmd.arg("modelid", bp.id().toByteArray()); - cmd.arg("lldbid", response.id.toByteArray()); - bp.notifyBreakpointRemoveProceeding(); - runCommand(cmd); + if (response.id.isValid()) { + DebuggerCommand cmd("removeBreakpoint"); + cmd.arg("lldbid", response.id.toByteArray()); + cmd.callback = [this, bp](const DebuggerResponse &) { + QTC_CHECK(bp.state() == BreakpointRemoveProceeding); + Breakpoint bp0 = bp; + bp0.notifyBreakpointRemoveOk(); + }; + bp.notifyBreakpointRemoveProceeding(); + runCommand(cmd); + } } -void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added) +void LldbEngine::updateBreakpointData(Breakpoint bp, const GdbMi &bkpt, bool added) { BreakHandler *handler = breakHandler(); BreakpointResponseId rid = BreakpointResponseId(bkpt["lldbid"].data()); - BreakpointModelId id = BreakpointModelId(bkpt["modelid"].data()); - Breakpoint bp = handler->breakpointById(id); if (!bp.isValid()) bp = handler->findBreakpointByResponseId(rid); BreakpointResponse response = bp.response(); @@ -694,50 +666,7 @@ void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added) bp.notifyBreakpointChangeOk(); } -void LldbEngine::refreshDisassembly(const GdbMi &data) -{ - DisassemblerLines result; - - int cookie = data["cookie"].toInt(); - QPointer<DisassemblerAgent> agent = m_disassemblerAgents.key(cookie); - if (!agent.isNull()) { - foreach (const GdbMi &line, data["lines"].children()) { - DisassemblerLine dl; - dl.address = line["address"].toAddress(); - //dl.data = line["data"].toUtf8(); - //dl.rawData = line["rawdata"].data(); - dl.data = line["rawdata"].toUtf8(); - if (!dl.data.isEmpty()) - dl.data += QString(30 - dl.data.size(), QLatin1Char(' ')); - dl.data += line["data"].toUtf8(); - dl.offset = line["offset"].toInt(); - dl.lineNumber = line["line"].toInt(); - dl.fileName = line["file"].toUtf8(); - dl.function = line["function"].toUtf8(); - dl.hunk = line["hunk"].toInt(); - QByteArray comment = line["comment"].data(); - if (!comment.isEmpty()) - dl.data += QString::fromUtf8(" # " + comment); - result.appendLine(dl); - } - agent->setContents(result); - } -} - -void LldbEngine::refreshMemory(const GdbMi &data) -{ - int cookie = data["cookie"].toInt(); - qulonglong addr = data["address"].toAddress(); - QPointer<MemoryAgent> agent = m_memoryAgents.key(cookie); - if (!agent.isNull()) { - QPointer<QObject> token = m_memoryAgentTokens.value(cookie); - QTC_ASSERT(!token.isNull(), return); - QByteArray ba = QByteArray::fromHex(data["contents"].data()); - agent->addLazyData(token.data(), addr, ba); - } -} - -void LldbEngine::refreshOutput(const GdbMi &output) +void LldbEngine::handleOutputNotification(const GdbMi &output) { QByteArray channel = output["channel"].data(); QByteArray data = QByteArray::fromHex(output["data"].data()); @@ -749,30 +678,6 @@ void LldbEngine::refreshOutput(const GdbMi &output) showMessage(QString::fromUtf8(data), ch); } -void LldbEngine::refreshAddedBreakpoint(const GdbMi &bkpt) -{ - BreakpointModelId id = BreakpointModelId(bkpt["modelid"].data()); - Breakpoint bp = breakHandler()->breakpointById(id); - QTC_CHECK(bp.state() == BreakpointInsertProceeding); - updateBreakpointData(bkpt, true); -} - -void LldbEngine::refreshChangedBreakpoint(const GdbMi &bkpt) -{ - BreakpointModelId id = BreakpointModelId(bkpt["modelid"].data()); - Breakpoint bp = breakHandler()->breakpointById(id); - QTC_CHECK(!bp.isValid() || bp.state() == BreakpointChangeProceeding); - updateBreakpointData(bkpt, false); -} - -void LldbEngine::refreshRemovedBreakpoint(const GdbMi &bkpt) -{ - BreakpointModelId id = BreakpointModelId(bkpt["modelid"].data()); - Breakpoint bp = breakHandler()->breakpointById(id); - QTC_CHECK(bp.state() == BreakpointRemoveProceeding); - bp.notifyBreakpointRemoveOk(); -} - void LldbEngine::loadSymbols(const QString &moduleName) { Q_UNUSED(moduleName) @@ -784,48 +689,47 @@ void LldbEngine::loadAllSymbols() void LldbEngine::reloadModules() { - runCommand("listModules"); -} - -void LldbEngine::refreshModules(const GdbMi &modules) -{ - ModulesHandler *handler = modulesHandler(); - handler->beginUpdateAll(); - foreach (const GdbMi &item, modules.children()) { - Module module; - module.modulePath = item["file"].toUtf8(); - module.moduleName = item["name"].toUtf8(); - module.symbolsRead = Module::UnknownReadState; - module.startAddress = item["loaded_addr"].toAddress(); - module.endAddress = 0; // FIXME: End address not easily available. - handler->updateModule(module); - } - handler->endUpdateAll(); + DebuggerCommand cmd("fetchModules"); + cmd.callback = [this](const DebuggerResponse &response) { + const GdbMi &modules = response.data["modules"]; + ModulesHandler *handler = modulesHandler(); + handler->beginUpdateAll(); + foreach (const GdbMi &item, modules.children()) { + Module module; + module.modulePath = item["file"].toUtf8(); + module.moduleName = item["name"].toUtf8(); + module.symbolsRead = Module::UnknownReadState; + module.startAddress = item["loaded_addr"].toAddress(); + module.endAddress = 0; // FIXME: End address not easily available. + handler->updateModule(module); + } + handler->endUpdateAll(); + }; + runCommand(cmd); } void LldbEngine::requestModuleSymbols(const QString &moduleName) { - DebuggerCommand cmd("listSymbols"); + DebuggerCommand cmd("fetchSymbols"); cmd.arg("module", moduleName); + cmd.callback = [this, moduleName](const DebuggerResponse &response) { + const GdbMi &symbols = response.data["symbols"]; + QString moduleName = response.data["module"].toUtf8(); + Symbols syms; + foreach (const GdbMi &item, symbols.children()) { + Symbol symbol; + symbol.address = item["address"].toUtf8(); + symbol.name = item["name"].toUtf8(); + symbol.state = item["state"].toUtf8(); + symbol.section = item["section"].toUtf8(); + symbol.demangled = item["demangled"].toUtf8(); + syms.append(symbol); + } + Internal::showModuleSymbols(moduleName, syms); + }; runCommand(cmd); } -void LldbEngine::refreshSymbols(const GdbMi &symbols) -{ - QString moduleName = symbols["module"].toUtf8(); - Symbols syms; - foreach (const GdbMi &item, symbols["symbols"].children()) { - Symbol symbol; - symbol.address = item["address"].toUtf8(); - symbol.name = item["name"].toUtf8(); - symbol.state = item["state"].toUtf8(); - symbol.section = item["section"].toUtf8(); - symbol.demangled = item["demangled"].toUtf8(); - syms.append(symbol); - } - Internal::showModuleSymbols(moduleName, syms); -} - ////////////////////////////////////////////////////////////////////// // @@ -840,27 +744,60 @@ bool LldbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const void LldbEngine::updateAll() { - DebuggerCommand cmd1("reportThreads"); - runCommand(cmd1); - - DebuggerCommand cmd2("reportCurrentThread"); - runCommand(cmd2); - - DebuggerCommand cmd("reportStack"); - cmd.arg("nativeMixed", isNativeMixedActive()); - cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt()); - cmd.arg("continuation", "updateLocals"); + DebuggerCommand cmd("fetchThreads"); + cmd.callback = [this](const DebuggerResponse &response) { + threadsHandler()->updateThreads(response.data); + fetchStack(action(MaximalStackDepth)->value().toInt()); + reloadRegisters(); + }; runCommand(cmd); } void LldbEngine::reloadFullStack() { - DebuggerCommand cmd("reportStack"); + fetchStack(-1); +} + +void LldbEngine::fetchStack(int limit) +{ + DebuggerCommand cmd("fetchStack"); cmd.arg("nativeMixed", isNativeMixedActive()); - cmd.arg("stacklimit", -1); + cmd.arg("stacklimit", limit); + cmd.callback = [this](const DebuggerResponse &response) { + const GdbMi &stack = response.data["stack"]; + StackHandler *handler = stackHandler(); + StackFrames frames; + foreach (const GdbMi &item, stack["frames"].children()) { + StackFrame frame; + frame.level = item["level"].toInt(); + frame.file = item["file"].toUtf8(); + frame.function = item["func"].toUtf8(); + frame.from = item["func"].toUtf8(); + frame.line = item["line"].toInt(); + frame.address = item["addr"].toAddress(); + GdbMi usable = item["usable"]; + if (usable.isValid()) + frame.usable = usable.data().toInt(); + else + frame.usable = QFileInfo(frame.file).isReadable(); + if (item["language"].data() == "js" + || frame.file.endsWith(QLatin1String(".js")) + || frame.file.endsWith(QLatin1String(".qml"))) { + frame.language = QmlLanguage; + frame.fixQmlFrame(runParameters()); + } + frames.append(frame); + } + bool canExpand = stack["hasmore"].toInt(); + action(ExpandStack)->setEnabled(canExpand); + handler->setFrames(frames, canExpand); + + updateLocals(); + }; runCommand(cmd); } + ////////////////////////////////////////////////////////////////////// // // Watch specific stuff @@ -873,6 +810,7 @@ void LldbEngine::assignValueInDebugger(WatchItem *, DebuggerCommand cmd("assignValue"); cmd.arg("exp", expression.toLatin1().toHex()); cmd.arg("value", value.toString().toLatin1().toHex()); + cmd.callback = [this](const DebuggerResponse &) { updateLocals(); }; runCommand(cmd); } @@ -885,7 +823,7 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms) watchHandler()->notifyUpdateStarted(params.partialVariables()); - DebuggerCommand cmd("updateData"); + DebuggerCommand cmd("fetchLocals"); cmd.arg("nativeMixed", isNativeMixedActive()); watchHandler()->appendFormatRequests(&cmd); @@ -925,9 +863,12 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms) m_lastDebuggableCommand = cmd; m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1"); - runCommand(cmd); + cmd.callback = [this](const DebuggerResponse &response) { + updateLocalsView(response.data); + watchHandler()->notifyUpdateFinished(); + }; - reloadRegisters(); + runCommand(cmd); } void LldbEngine::handleLldbError(QProcess::ProcessError error) @@ -991,7 +932,7 @@ void LldbEngine::readLldbStandardOutput() { QByteArray out = m_lldbProc.readAllStandardOutput(); out.replace("\r\n", "\n"); - showMessage(_(out)); + showMessage(_(out), LogOutput); m_inbuffer.append(out); while (true) { int pos = m_inbuffer.indexOf("@\n"); @@ -1006,86 +947,7 @@ void LldbEngine::readLldbStandardOutput() } } -void LldbEngine::refreshStack(const GdbMi &stack) -{ - StackHandler *handler = stackHandler(); - StackFrames frames; - foreach (const GdbMi &item, stack["frames"].children()) { - StackFrame frame; - frame.level = item["level"].toInt(); - frame.file = item["file"].toUtf8(); - frame.function = item["func"].toUtf8(); - frame.from = item["func"].toUtf8(); - frame.line = item["line"].toInt(); - frame.address = item["addr"].toAddress(); - GdbMi usable = item["usable"]; - if (usable.isValid()) - frame.usable = usable.data().toInt(); - else - frame.usable = QFileInfo(frame.file).isReadable(); - if (item["language"].data() == "js" - || frame.file.endsWith(QLatin1String(".js")) - || frame.file.endsWith(QLatin1String(".qml"))) { - frame.language = QmlLanguage; - frame.fixQmlFrame(runParameters()); - } - frames.append(frame); - } - bool canExpand = stack["hasmore"].toInt(); - action(ExpandStack)->setEnabled(canExpand); - handler->setFrames(frames, canExpand); -} - -void LldbEngine::refreshRegisters(const GdbMi ®isters) -{ - RegisterHandler *handler = registerHandler(); - foreach (const GdbMi &item, registers.children()) { - Register reg; - reg.name = item["name"].data(); - reg.value.fromByteArray(item["value"].data(), HexadecimalFormat); - reg.size = item["size"].data().toInt(); - reg.reportedType = item["type"].data(); - if (reg.reportedType.startsWith("unsigned")) - reg.kind = IntegerRegister; - handler->updateRegister(reg); - } - handler->commitUpdates(); -} - -void LldbEngine::refreshThreads(const GdbMi &threads) -{ - ThreadsHandler *handler = threadsHandler(); - handler->updateThreads(threads); - updateViews(); // Adjust Threads combobox. -} - -void LldbEngine::refreshCurrentThread(const GdbMi &data) -{ - ThreadsHandler *handler = threadsHandler(); - ThreadId id(data["id"].toInt()); - handler->setCurrentThread(id); - updateViews(); // Adjust Threads combobox. -} - -void LldbEngine::refreshTypeInfo(const GdbMi &typeInfo) -{ - if (typeInfo.type() == GdbMi::List) { -// foreach (const GdbMi &s, typeInfo.children()) { -// const GdbMi name = s["name"]; -// const GdbMi size = s["size"]; -// if (name.isValid() && size.isValid()) -// m_typeInfoCache.insert(QByteArray::fromBase64(name.data()), -// TypeInfo(size.data().toUInt())); -// } - } -// for (int i = 0; i != list.size(); ++i) { -// const TypeInfo ti = m_typeInfoCache.value(list.at(i).type); -// if (ti.size) -// list[i].size = ti.size; -// } -} - -void LldbEngine::refreshState(const GdbMi &reportedState) +void LldbEngine::handleStateNotification(const GdbMi &reportedState) { QByteArray newState = reportedState.data(); if (newState == "running") @@ -1113,10 +975,6 @@ void LldbEngine::refreshState(const GdbMi &reportedState) notifyEngineSetupFailed(); else if (newState == "enginerunfailed") notifyEngineRunFailed(); - else if (newState == "inferiorsetupok") - notifyInferiorSetupOk(); - else if (newState == "inferiorsetupfailed") - notifyInferiorSetupFailed(); else if (newState == "enginerunandinferiorrunok") { if (runParameters().continueAfterAttach) m_continueAtNextSpontaneousStop = true; @@ -1137,7 +995,7 @@ void LldbEngine::refreshState(const GdbMi &reportedState) notifyInferiorExited(); } -void LldbEngine::refreshLocation(const GdbMi &reportedLocation) +void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation) { qulonglong addr = reportedLocation["addr"].toAddress(); QString file = reportedLocation["file"].toUtf8(); @@ -1153,8 +1011,26 @@ void LldbEngine::refreshLocation(const GdbMi &reportedLocation) void LldbEngine::reloadRegisters() { - if (Internal::isDockVisible(QLatin1String(DOCKWIDGET_REGISTER))) - runCommand("reportRegisters"); + if (!Internal::isDockVisible(QLatin1String(DOCKWIDGET_REGISTER))) + return; + + DebuggerCommand cmd("fetchRegisters"); + cmd.callback = [this](const DebuggerResponse &response) { + RegisterHandler *handler = registerHandler(); + GdbMi regs = response.data["registers"]; + foreach (const GdbMi &item, regs.children()) { + Register reg; + reg.name = item["name"].data(); + reg.value.fromByteArray(item["value"].data(), HexadecimalFormat); + reg.size = item["size"].data().toInt(); + reg.reportedType = item["type"].data(); + if (reg.reportedType.startsWith("unsigned")) + reg.kind = IntegerRegister; + handler->updateRegister(reg); + } + handler->commitUpdates(); + }; + runCommand(cmd); } void LldbEngine::reloadDebuggingHelpers() @@ -1172,17 +1048,47 @@ void LldbEngine::fetchDisassembler(DisassemblerAgent *agent) m_disassemblerAgents.insert(p, id); } const Location &loc = agent->location(); - DebuggerCommand cmd("disassemble"); - cmd.arg("cookie", id); + DebuggerCommand cmd("fetchDisassembler"); cmd.arg("address", loc.address()); cmd.arg("function", loc.functionName()); cmd.arg("flavor", boolSetting(IntelFlavor) ? "intel" : "att"); + cmd.callback = [this, id](const DebuggerResponse &response) { + DisassemblerLines result; + QPointer<DisassemblerAgent> agent = m_disassemblerAgents.key(id); + if (!agent.isNull()) { + foreach (const GdbMi &line, response.data["lines"].children()) { + DisassemblerLine dl; + dl.address = line["address"].toAddress(); + //dl.data = line["data"].toUtf8(); + //dl.rawData = line["rawdata"].data(); + dl.data = line["rawdata"].toUtf8(); + if (!dl.data.isEmpty()) + dl.data += QString(30 - dl.data.size(), QLatin1Char(' ')); + dl.data += line["data"].toUtf8(); + dl.offset = line["offset"].toInt(); + dl.lineNumber = line["line"].toInt(); + dl.fileName = line["file"].toUtf8(); + dl.function = line["function"].toUtf8(); + dl.hunk = line["hunk"].toInt(); + QByteArray comment = line["comment"].data(); + if (!comment.isEmpty()) + dl.data += QString::fromUtf8(" # " + comment); + result.appendLine(dl); + } + agent->setContents(result); + } + }; runCommand(cmd); } -void LldbEngine::createFullBacktrace() +void LldbEngine::fetchFullBacktrace() { - runCommand("createFullBacktrace"); + DebuggerCommand cmd("fetchFullBacktrace"); + cmd.callback = [](const DebuggerResponse &response) { + Internal::openTextEditor(_("Backtrace $"), + QString::fromUtf8(QByteArray::fromHex(response.data.data()))); + }; + runCommand("fetchFullBacktrace"); } void LldbEngine::fetchMemory(MemoryAgent *agent, QObject *editorToken, @@ -1194,10 +1100,20 @@ void LldbEngine::fetchMemory(MemoryAgent *agent, QObject *editorToken, m_memoryAgents.insert(agent, id); } m_memoryAgentTokens.insert(id, editorToken); + DebuggerCommand cmd("fetchMemory"); cmd.arg("address", addr); cmd.arg("length", length); - cmd.arg("cookie", id); + cmd.callback = [this, id](const DebuggerResponse &response) { + qulonglong addr = response.data["address"].toAddress(); + QPointer<MemoryAgent> agent = m_memoryAgents.key(id); + if (!agent.isNull()) { + QPointer<QObject> token = m_memoryAgentTokens.value(id); + QTC_ASSERT(!token.isNull(), return); + QByteArray ba = QByteArray::fromHex(response.data["contents"].data()); + agent->addLazyData(token.data(), addr, ba); + } + }; runCommand(cmd); } @@ -1213,7 +1129,7 @@ void LldbEngine::changeMemory(MemoryAgent *agent, QObject *editorToken, DebuggerCommand cmd("writeMemory"); cmd.arg("address", addr); cmd.arg("data", data.toHex()); - cmd.arg("cookie", id); + cmd.callback = [this, id](const DebuggerResponse &response) { Q_UNUSED(response); }; runCommand(cmd); } diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index b924c1af9d..02c195aa8b 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -67,8 +67,10 @@ public: explicit LldbEngine(const DebuggerRunParameters &runParameters); ~LldbEngine(); +signals: + void outputReady(const QByteArray &data); + private: - // DebuggerEngine implementation DebuggerEngine *cppEngine() { return this; } void executeStep(); @@ -81,7 +83,6 @@ private: void startLldb(); void startLldbStage2(); void setupInferior(); - void setupInferiorStage2(); void runEngine(); void shutdownInferior(); void shutdownEngine(); @@ -98,6 +99,7 @@ private: void activateFrame(int index); void selectThread(ThreadId threadId); + void fetchFullBacktrace(); // This should be always the last call in a function. bool stateAcceptsBreakpointChanges() const; @@ -118,20 +120,13 @@ private: void reloadFullStack(); void reloadDebuggingHelpers(); void fetchDisassembler(Internal::DisassemblerAgent *); - void refreshDisassembly(const GdbMi &data); - bool supportsThreads() const { return true; } bool isSynchronous() const { return true; } void setRegisterValue(const QByteArray &name, const QString &value); void fetchMemory(Internal::MemoryAgent *, QObject *, quint64 addr, quint64 length); void changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data); - void refreshMemory(const GdbMi &data); -signals: - void outputReady(const QByteArray &data); - -private: QString errorMessage(QProcess::ProcessError error) const; bool hasCapability(unsigned cap) const; @@ -139,45 +134,23 @@ private: void handleLldbError(QProcess::ProcessError error); void readLldbStandardOutput(); void readLldbStandardError(); + + void handleStateNotification(const GdbMi &state); + void handleLocationNotification(const GdbMi &location); + void handleOutputNotification(const GdbMi &output); + void handleResponse(const QByteArray &data); void updateAll(); - void createFullBacktrace(); void doUpdateLocals(const UpdateParameters ¶ms); - void handleContinuation(const GdbMi &data); - - void refreshAll(const GdbMi &all); - void refreshThreads(const GdbMi &threads); - void refreshCurrentThread(const GdbMi &data); - void refreshStack(const GdbMi &stack); - void refreshRegisters(const GdbMi ®isters); - void refreshTypeInfo(const GdbMi &typeInfo); - void refreshState(const GdbMi &state); - void refreshLocation(const GdbMi &location); - void refreshModules(const GdbMi &modules); - void refreshSymbols(const GdbMi &symbols); - void refreshOutput(const GdbMi &output); - void refreshAddedBreakpoint(const GdbMi &bkpts); - void refreshChangedBreakpoint(const GdbMi &bkpts); - void refreshRemovedBreakpoint(const GdbMi &bkpts); - void showFullBacktrace(const GdbMi &data); - - typedef void (LldbEngine::*LldbCommandContinuation)(); - - void handleListLocals(const QByteArray &response); - void handleListModules(const QByteArray &response); - void handleListSymbols(const QByteArray &response); - void handleBreakpointsSynchronized(const QByteArray &response); - void updateBreakpointData(const GdbMi &bkpt, bool added); - void handleUpdateStack(const QByteArray &response); - void handleUpdateThreads(const QByteArray &response); + void updateBreakpointData(Breakpoint bp, const GdbMi &bkpt, bool added); + void fetchStack(int limit); void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result); - void handleChildren(const WatchData &data0, const GdbMi &item, - QList<WatchData> *list); - void runCommand(const DebuggerCommand &cmd); void debugLastCommand(); + +private: DebuggerCommand m_lastDebuggableCommand; QByteArray m_inbuffer; @@ -192,6 +165,8 @@ private: QMap<QPointer<MemoryAgent>, int> m_memoryAgents; QHash<int, QPointer<QObject> > m_memoryAgentTokens; + QHash<int, DebuggerCommand> m_commandForToken; + // Console handling. Q_SLOT void stubError(const QString &msg); Q_SLOT void stubExited(); |