1 | // |
2 | // debugview.cpp |
3 | // |
4 | // Description: Manages the interaction with GDB |
5 | // |
6 | // |
7 | // Copyright (c) 2008-2010 Ian Wakeling <ian.wakeling@ntlworld.com> |
8 | // Copyright (c) 2011 Kåre Särs <kare.sars@iki.fi> |
9 | // |
10 | // This library is free software; you can redistribute it and/or |
11 | // modify it under the terms of the GNU Library General Public |
12 | // License version 2 as published by the Free Software Foundation. |
13 | // |
14 | // This library is distributed in the hope that it will be useful, |
15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | // Library General Public License for more details. |
18 | // |
19 | // You should have received a copy of the GNU Library General Public License |
20 | // along with this library; see the file COPYING.LIB. If not, write to |
21 | // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
22 | // Boston, MA 02110-1301, USA. |
23 | |
24 | #include "debugview.h" |
25 | #include "debugview.moc" |
26 | |
27 | #include <QtCore/QRegExp> |
28 | #include <QtCore/QFile> |
29 | #include <QtCore/QTimer> |
30 | |
31 | #include <kmessagebox.h> |
32 | #include <kurlrequester.h> |
33 | #include <kurl.h> |
34 | #include <kdebug.h> |
35 | #include <klocale.h> |
36 | |
37 | #include <signal.h> |
38 | #include <stdlib.h> |
39 | |
40 | static const QString PromptStr = "(prompt)" ; |
41 | |
42 | DebugView::DebugView( QObject* parent ) |
43 | : QObject( parent ), |
44 | m_debugProcess(0), |
45 | m_state( none ), |
46 | m_subState( normal ), |
47 | m_debugLocationChanged( true ), |
48 | m_queryLocals( false ) |
49 | { |
50 | } |
51 | |
52 | |
53 | DebugView::~DebugView() |
54 | { |
55 | if ( m_debugProcess.state() != QProcess::NotRunning ) |
56 | { |
57 | m_debugProcess.kill(); |
58 | m_debugProcess.blockSignals( true ); |
59 | m_debugProcess.waitForFinished(); |
60 | } |
61 | } |
62 | |
63 | void DebugView::runDebugger(const GDBTargetConf &conf, const QStringList &ioFifos) |
64 | { |
65 | if (conf.executable.isEmpty()) { |
66 | return; |
67 | } |
68 | m_targetConf = conf; |
69 | if (ioFifos.size() == 3) { |
70 | m_ioPipeString = QString("< %1 1> %2 2> %3" ) |
71 | .arg(ioFifos[0]) |
72 | .arg(ioFifos[1]) |
73 | .arg(ioFifos[2]); |
74 | } |
75 | |
76 | if (m_state == none) { |
77 | m_outBuffer.clear(); |
78 | m_errBuffer.clear(); |
79 | m_errorList.clear(); |
80 | |
81 | //create a process to control GDB |
82 | m_debugProcess.setWorkingDirectory(m_targetConf.workDir); |
83 | |
84 | connect( &m_debugProcess, SIGNAL(error(QProcess::ProcessError)), |
85 | this, SLOT(slotError()) ); |
86 | |
87 | connect( &m_debugProcess, SIGNAL(readyReadStandardError()), |
88 | this, SLOT(slotReadDebugStdErr()) ); |
89 | |
90 | connect( &m_debugProcess, SIGNAL(readyReadStandardOutput()), |
91 | this, SLOT(slotReadDebugStdOut()) ); |
92 | |
93 | connect( &m_debugProcess, SIGNAL(finished(int,QProcess::ExitStatus)), |
94 | this, SLOT(slotDebugFinished(int,QProcess::ExitStatus)) ); |
95 | |
96 | m_debugProcess.setShellCommand(m_targetConf.gdbCmd); |
97 | m_debugProcess.setOutputChannelMode(KProcess::SeparateChannels); |
98 | m_debugProcess.start(); |
99 | |
100 | m_nextCommands << "set pagination off" ; |
101 | m_state = ready; |
102 | } |
103 | else |
104 | { |
105 | // On startup the gdb prompt will trigger the "nextCommands", |
106 | // here we have to trigger it manually. |
107 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
108 | } |
109 | m_nextCommands << QString("file %1" ).arg(m_targetConf.executable); |
110 | m_nextCommands << QString("set args %1 %2" ).arg(m_targetConf.arguments).arg(m_ioPipeString); |
111 | m_nextCommands << QString("set inferior-tty /dev/null" ); |
112 | m_nextCommands << m_targetConf.customInit; |
113 | m_nextCommands << QString("(Q) info breakpoints" ); |
114 | } |
115 | |
116 | bool DebugView::debuggerRunning() const |
117 | { |
118 | return( m_state != none ); |
119 | } |
120 | |
121 | bool DebugView::debuggerBusy() const |
122 | { |
123 | return( m_state == executingCmd ); |
124 | } |
125 | |
126 | bool DebugView::hasBreakpoint( const KUrl& url, int line ) |
127 | { |
128 | for (int i = 0; i<m_breakPointList.size(); i++) { |
129 | if ( (url == m_breakPointList[i].file) && (line == m_breakPointList[i].line) ) { |
130 | return true; |
131 | } |
132 | } |
133 | return false; |
134 | } |
135 | |
136 | void DebugView::toggleBreakpoint( KUrl const& url, int line ) |
137 | { |
138 | if( m_state == ready ) |
139 | { |
140 | QString cmd; |
141 | if (hasBreakpoint( url, line )) |
142 | { |
143 | cmd = QString("clear %1:%2" ).arg(url.path()).arg(line); |
144 | } |
145 | else { |
146 | cmd = QString("break %1:%2" ).arg(url.path()).arg(line); |
147 | } |
148 | issueCommand( cmd ); |
149 | } |
150 | } |
151 | |
152 | void DebugView::slotError() |
153 | { |
154 | KMessageBox::sorry( NULL, i18n("Could not start debugger process" ) ); |
155 | } |
156 | |
157 | void DebugView::slotReadDebugStdOut() |
158 | { |
159 | m_outBuffer += QString::fromLocal8Bit( m_debugProcess.readAllStandardOutput().data() ); |
160 | int end=0; |
161 | // handle one line at a time |
162 | do { |
163 | end = m_outBuffer.indexOf('\n'); |
164 | if (end < 0) break; |
165 | processLine( m_outBuffer.mid(0, end) ); |
166 | m_outBuffer.remove(0,end+1); |
167 | } while (1); |
168 | |
169 | if (m_outBuffer == "(gdb) " || m_outBuffer == ">" ) |
170 | { |
171 | m_outBuffer.clear(); |
172 | processLine( PromptStr ); |
173 | } |
174 | } |
175 | |
176 | void DebugView::slotReadDebugStdErr() |
177 | { |
178 | m_errBuffer += QString::fromLocal8Bit( m_debugProcess.readAllStandardError().data() ); |
179 | int end=0; |
180 | // add whole lines at a time to the error list |
181 | do { |
182 | end = m_errBuffer.indexOf('\n'); |
183 | if (end < 0) break; |
184 | m_errorList << m_errBuffer.mid(0, end); |
185 | m_errBuffer.remove(0,end+1); |
186 | } while (1); |
187 | |
188 | processErrors(); |
189 | } |
190 | |
191 | void DebugView::slotDebugFinished( int /*exitCode*/, QProcess::ExitStatus status ) |
192 | { |
193 | if( status != QProcess::NormalExit ) |
194 | { |
195 | emit outputText( i18n("*** gdb exited normally ***" ) + '\n' ); |
196 | } |
197 | |
198 | m_state = none; |
199 | emit readyForInput( false ); |
200 | |
201 | // remove all old breakpoints |
202 | BreakPoint bPoint; |
203 | while ( m_breakPointList.size() > 0 ) |
204 | { |
205 | bPoint = m_breakPointList.takeFirst(); |
206 | emit breakPointCleared( bPoint.file, bPoint.line -1 ); |
207 | } |
208 | |
209 | emit gdbEnded(); |
210 | } |
211 | |
212 | void DebugView::movePC( KUrl const& url, int line ) |
213 | { |
214 | if( m_state == ready ) |
215 | { |
216 | QString cmd = QString("tbreak %1:%2" ).arg(url.path()).arg(line); |
217 | m_nextCommands << QString("jump %1:%2" ).arg(url.path()).arg(line); |
218 | issueCommand( cmd ); |
219 | } |
220 | } |
221 | |
222 | void DebugView::runToCursor( KUrl const& url, int line ) |
223 | { |
224 | if( m_state == ready ) |
225 | { |
226 | QString cmd = QString("tbreak %1:%2" ).arg(url.path()).arg(line); |
227 | m_nextCommands << "continue" ; |
228 | issueCommand( cmd ); |
229 | } |
230 | } |
231 | |
232 | void DebugView::slotInterrupt() |
233 | { |
234 | if (m_state == executingCmd) { |
235 | m_debugLocationChanged = true; |
236 | } |
237 | int pid = m_debugProcess.pid(); |
238 | if (pid != 0) { |
239 | ::kill(pid, SIGINT); |
240 | } |
241 | } |
242 | |
243 | void DebugView::slotKill() |
244 | { |
245 | if( m_state != ready ) |
246 | { |
247 | slotInterrupt(); |
248 | m_state = ready; |
249 | } |
250 | issueCommand( "kill" ); |
251 | } |
252 | |
253 | void DebugView::slotReRun() |
254 | { |
255 | slotKill(); |
256 | m_nextCommands << QString("file %1" ).arg(m_targetConf.executable); |
257 | m_nextCommands << QString("set args %1 %2" ).arg(m_targetConf.arguments).arg(m_ioPipeString); |
258 | m_nextCommands << QString("set inferior-tty /dev/null" ); |
259 | m_nextCommands << m_targetConf.customInit; |
260 | m_nextCommands << QString("(Q) info breakpoints" ); |
261 | |
262 | m_nextCommands << QString("tbreak main" ); |
263 | m_nextCommands << QString("run" ); |
264 | m_nextCommands << QString("p setvbuf(stdout, 0, %1, 1024)" ).arg(_IOLBF); |
265 | m_nextCommands << QString("continue" ); |
266 | } |
267 | |
268 | void DebugView::slotStepInto() |
269 | { |
270 | issueCommand( "step" ); |
271 | } |
272 | |
273 | void DebugView::slotStepOver() |
274 | { |
275 | issueCommand( "next" ); |
276 | } |
277 | |
278 | void DebugView::slotStepOut() |
279 | { |
280 | issueCommand( "finish" ); |
281 | } |
282 | |
283 | void DebugView::slotContinue() |
284 | { |
285 | issueCommand( "continue" ); |
286 | } |
287 | |
288 | static QRegExp breakpointList( "Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What.*" ); |
289 | static QRegExp breakpointListed( "(\\d)\\s+breakpoint\\s+keep\\sy\\s+0x[\\da-f]+\\sin\\s.+\\sat\\s([^:]+):(\\d+).*" ); |
290 | static QRegExp stackFrameAny( "#(\\d+)\\s(.*)" ); |
291 | static QRegExp stackFrameFile( "#(\\d+)\\s+(?:0x[\\da-f]+\\s*in\\s)*(\\S+)(\\s\\(.*\\)) at ([^:]+):(\\d+).*" ); |
292 | static QRegExp changeFile( "(?:(?:Temporary\\sbreakpoint|Breakpoint)\\s*\\d+,\\s*|0x[\\da-f]+\\s*in\\s*)?[^\\s]+\\s*\\([^)]*\\)\\s*at\\s*([^:]+):(\\d+).*" ); |
293 | static QRegExp changeLine( "(\\d+)\\s+.*" ); |
294 | static QRegExp breakPointReg( "Breakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+file\\s+([^\\,]+)\\,\\s+line\\s+(\\d+).*" ); |
295 | static QRegExp breakPointMultiReg( "Breakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+([^\\,]+):(\\d+).*" ); |
296 | static QRegExp breakPointDel( "Deleted\\s+breakpoint.*" ); |
297 | static QRegExp exitProgram( "(?:Program|.*Inferior.*)\\s+exited.*" ); |
298 | static QRegExp threadLine( "\\**\\s+(\\d+)\\s+Thread.*" ); |
299 | |
300 | void DebugView::processLine( QString line ) |
301 | { |
302 | if (line.isEmpty()) return; |
303 | |
304 | switch( m_state ) |
305 | { |
306 | case none: |
307 | case ready: |
308 | if( PromptStr == line ) |
309 | { |
310 | // we get here after initialization |
311 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
312 | } |
313 | break; |
314 | |
315 | case executingCmd: |
316 | if( breakpointList.exactMatch( line ) ) |
317 | { |
318 | m_state = listingBreakpoints; |
319 | emit clearBreakpointMarks(); |
320 | m_breakPointList.clear(); |
321 | } |
322 | else if ( line.contains( "No breakpoints or watchpoints." ) ) |
323 | { |
324 | emit clearBreakpointMarks(); |
325 | m_breakPointList.clear(); |
326 | } |
327 | else if ( stackFrameAny.exactMatch( line ) ) |
328 | { |
329 | if ( m_lastCommand.contains( "info stack" ) ) |
330 | { |
331 | emit stackFrameInfo( stackFrameAny.cap(1), stackFrameAny.cap(2)); |
332 | } |
333 | else |
334 | { |
335 | m_subState = ( m_subState == normal ) ? stackFrameSeen : stackTraceSeen; |
336 | |
337 | m_newFrameLevel = stackFrameAny.cap( 1 ).toInt(); |
338 | |
339 | if ( stackFrameFile.exactMatch( line ) ) |
340 | { |
341 | m_newFrameFile = stackFrameFile.cap( 4 ); |
342 | } |
343 | } |
344 | } |
345 | else if( changeFile.exactMatch( line ) ) |
346 | { |
347 | m_currentFile = changeFile.cap( 1 ).trimmed(); |
348 | int lineNum = changeFile.cap( 2 ).toInt(); |
349 | |
350 | if ( !m_nextCommands.contains("continue" ) ) { |
351 | // GDB uses 1 based line numbers, kate uses 0 based... |
352 | emit debugLocationChanged( resolveFileName(m_currentFile), lineNum - 1 ); |
353 | } |
354 | m_debugLocationChanged = true; |
355 | } |
356 | else if( changeLine.exactMatch( line ) ) |
357 | { |
358 | int lineNum = changeLine.cap( 1 ).toInt(); |
359 | |
360 | if( m_subState == stackFrameSeen ) |
361 | { |
362 | m_currentFile = m_newFrameFile; |
363 | } |
364 | if ( !m_nextCommands.contains("continue" ) ) { |
365 | // GDB uses 1 based line numbers, kate uses 0 based... |
366 | emit debugLocationChanged( resolveFileName(m_currentFile), lineNum - 1 ); |
367 | } |
368 | m_debugLocationChanged = true; |
369 | } |
370 | else if (breakPointReg.exactMatch(line)) |
371 | { |
372 | BreakPoint breakPoint; |
373 | breakPoint.number = breakPointReg.cap( 1 ).toInt(); |
374 | breakPoint.file = resolveFileName( breakPointReg.cap( 2 ) ); |
375 | breakPoint.line = breakPointReg.cap( 3 ).toInt(); |
376 | m_breakPointList << breakPoint; |
377 | emit breakPointSet( breakPoint.file, breakPoint.line -1 ); |
378 | } |
379 | else if (breakPointMultiReg.exactMatch(line)) |
380 | { |
381 | BreakPoint breakPoint; |
382 | breakPoint.number = breakPointMultiReg.cap( 1 ).toInt(); |
383 | breakPoint.file = resolveFileName( breakPointMultiReg.cap( 2 ) ); |
384 | breakPoint.line = breakPointMultiReg.cap( 3 ).toInt(); |
385 | m_breakPointList << breakPoint; |
386 | emit breakPointSet( breakPoint.file, breakPoint.line -1 ); |
387 | } |
388 | else if (breakPointDel.exactMatch(line)) |
389 | { |
390 | line.remove("Deleted breakpoint" ); |
391 | line.remove('s'); // in case of multiple breakpoints |
392 | QStringList numbers = line.split(' ', QString::SkipEmptyParts); |
393 | for (int i=0; i<numbers.size(); i++) |
394 | { |
395 | for (int j = 0; j<m_breakPointList.size(); j++) |
396 | { |
397 | if ( numbers[i].toInt() == m_breakPointList[j].number ) |
398 | { |
399 | emit breakPointCleared( m_breakPointList[j].file, m_breakPointList[j].line -1 ); |
400 | m_breakPointList.removeAt(j); |
401 | break; |
402 | } |
403 | } |
404 | } |
405 | } |
406 | else if ( exitProgram.exactMatch( line ) || |
407 | line.contains( "The program no longer exists" ) || |
408 | line.contains( "Kill the program being debugged" ) ) |
409 | { |
410 | // if there are still commands to execute remove them to remove unneeded output |
411 | // except if the "kill was for "re-run" |
412 | if ( ( m_nextCommands.size() > 0 ) && !m_nextCommands[0].contains("file" ) ) |
413 | { |
414 | m_nextCommands.clear(); |
415 | } |
416 | m_debugLocationChanged = false; // do not insert (Q) commands |
417 | emit programEnded(); |
418 | } |
419 | else if( PromptStr == line ) |
420 | { |
421 | if( m_subState == stackFrameSeen ) |
422 | { |
423 | emit stackFrameChanged( m_newFrameLevel ); |
424 | } |
425 | m_state = ready; |
426 | |
427 | // Give the error a possibility get noticed since stderr and stdout are not in sync |
428 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
429 | } |
430 | break; |
431 | |
432 | case listingBreakpoints: |
433 | if (breakpointListed.exactMatch( line ) ) |
434 | { |
435 | BreakPoint breakPoint; |
436 | breakPoint.number = breakpointListed.cap( 1 ).toInt(); |
437 | breakPoint.file = resolveFileName( breakpointListed.cap( 2 ) ); |
438 | breakPoint.line = breakpointListed.cap( 3 ).toInt(); |
439 | m_breakPointList << breakPoint; |
440 | emit breakPointSet( breakPoint.file, breakPoint.line -1 ); |
441 | } |
442 | else if( PromptStr == line ) |
443 | { |
444 | m_state = ready; |
445 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
446 | } |
447 | break; |
448 | case infoArgs: |
449 | if( PromptStr == line ) |
450 | { |
451 | m_state = ready; |
452 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
453 | } |
454 | else { |
455 | emit infoLocal( line ); |
456 | } |
457 | break; |
458 | case printThis: |
459 | if( PromptStr == line ) |
460 | { |
461 | m_state = ready; |
462 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
463 | } |
464 | else { |
465 | emit infoLocal( line ); |
466 | } |
467 | break; |
468 | case infoLocals: |
469 | if( PromptStr == line ) |
470 | { |
471 | m_state = ready; |
472 | emit infoLocal( QString() ); |
473 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
474 | } |
475 | else { |
476 | emit infoLocal( line ); |
477 | } |
478 | break; |
479 | case infoStack: |
480 | if( PromptStr == line ) |
481 | { |
482 | m_state = ready; |
483 | emit stackFrameInfo( QString(), QString() ); |
484 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
485 | } |
486 | else if ( stackFrameAny.exactMatch( line ) ) |
487 | { |
488 | emit stackFrameInfo( stackFrameAny.cap(1), stackFrameAny.cap(2)); |
489 | } |
490 | break; |
491 | case infoThreads: |
492 | if( PromptStr == line ) |
493 | { |
494 | m_state = ready; |
495 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
496 | } |
497 | else if ( threadLine.exactMatch( line ) ) |
498 | { |
499 | emit threadInfo( threadLine.cap(1).toInt(), (line[0] == '*')); |
500 | } |
501 | break; |
502 | } |
503 | outputTextMaybe( line ); |
504 | } |
505 | |
506 | void DebugView::processErrors() |
507 | { |
508 | QString error; |
509 | while (m_errorList.size() > 0) { |
510 | error = m_errorList.takeFirst(); |
511 | //kDebug() << error; |
512 | if( error == "The program is not being run." ) |
513 | { |
514 | if ( m_lastCommand == "continue" ) |
515 | { |
516 | m_nextCommands.clear(); |
517 | m_nextCommands << QString("tbreak main" ); |
518 | m_nextCommands << QString("run" ); |
519 | m_nextCommands << QString("p setvbuf(stdout, 0, %1, 1024)" ).arg(_IOLBF); |
520 | m_nextCommands << QString("continue" ); |
521 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
522 | } |
523 | else if ( ( m_lastCommand == "step" ) || |
524 | ( m_lastCommand == "next" ) || |
525 | ( m_lastCommand == "finish" ) ) |
526 | { |
527 | m_nextCommands.clear(); |
528 | m_nextCommands << "tbreak main" ; |
529 | m_nextCommands << "run" ; |
530 | m_nextCommands << QString("p setvbuf(stdout, 0, %1, 1024)" ).arg(_IOLBF); |
531 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
532 | } |
533 | else if ((m_lastCommand == "kill" )) |
534 | { |
535 | if ( m_nextCommands.size() > 0 ) |
536 | { |
537 | if ( !m_nextCommands[0].contains("file" ) ) |
538 | { |
539 | m_nextCommands.clear(); |
540 | m_nextCommands << "quit" ; |
541 | } |
542 | // else continue with "ReRun" |
543 | } |
544 | else |
545 | { |
546 | m_nextCommands << "quit" ; |
547 | } |
548 | m_state = ready; |
549 | QTimer::singleShot(0, this, SLOT(issueNextCommand())); |
550 | } |
551 | // else do nothing |
552 | } |
553 | else if ( error.contains( "No line " ) || |
554 | error.contains( "No source file named" ) ) |
555 | { |
556 | // setting a breakpoint failed. Do not continue. |
557 | m_nextCommands.clear(); |
558 | emit readyForInput( true ); |
559 | } |
560 | else if ( error.contains( "No stack" ) ) |
561 | { |
562 | m_nextCommands.clear(); |
563 | emit programEnded(); |
564 | } |
565 | |
566 | if ((m_lastCommand == "(Q)print *this" ) && error.contains("No symbol \"this\" in current context." )) { |
567 | continue; |
568 | } |
569 | emit outputError( error + '\n' ); |
570 | } |
571 | } |
572 | |
573 | void DebugView::issueCommand( QString const& cmd ) |
574 | { |
575 | if( m_state == ready ) |
576 | { |
577 | emit readyForInput( false ); |
578 | m_state = executingCmd; |
579 | if (cmd == "(Q)info locals" ) { |
580 | m_state = infoLocals; |
581 | } |
582 | else if (cmd == "(Q)info args" ) { |
583 | m_state = infoArgs; |
584 | } |
585 | else if (cmd == "(Q)print *this" ) { |
586 | m_state = printThis; |
587 | } |
588 | else if (cmd == "(Q)info stack" ) { |
589 | m_state = infoStack; |
590 | } |
591 | else if (cmd == "(Q)info thread" ) { |
592 | emit threadInfo( -1 , false ); |
593 | m_state = infoThreads; |
594 | } |
595 | m_subState = normal; |
596 | m_lastCommand = cmd; |
597 | |
598 | if ( cmd.startsWith("(Q)" ) ) |
599 | { |
600 | m_debugProcess.write( cmd.mid(3).toLocal8Bit() + '\n' ); |
601 | } |
602 | else { |
603 | emit outputText( "(gdb) " + cmd + '\n' ); |
604 | m_debugProcess.write( cmd.toLocal8Bit() + '\n' ); |
605 | } |
606 | } |
607 | } |
608 | |
609 | void DebugView::issueNextCommand() |
610 | { |
611 | if( m_state == ready ) |
612 | { |
613 | if( m_nextCommands.size() > 0) |
614 | { |
615 | QString cmd = m_nextCommands.takeFirst(); |
616 | //kDebug() << "Next command" << cmd; |
617 | issueCommand( cmd ); |
618 | } |
619 | else |
620 | { |
621 | // FIXME "thread" needs a better generic solution |
622 | if (m_debugLocationChanged || m_lastCommand.startsWith("thread" )) { |
623 | m_debugLocationChanged = false; |
624 | if (m_queryLocals && !m_lastCommand.startsWith("(Q)" )) { |
625 | m_nextCommands << "(Q)info stack" ; |
626 | m_nextCommands << "(Q)frame" ; |
627 | m_nextCommands << "(Q)info args" ; |
628 | m_nextCommands << "(Q)print *this" ; |
629 | m_nextCommands << "(Q)info locals" ; |
630 | m_nextCommands << "(Q)info thread" ; |
631 | issueNextCommand(); |
632 | return; |
633 | } |
634 | } |
635 | emit readyForInput( true ); |
636 | } |
637 | } |
638 | } |
639 | |
640 | KUrl DebugView::resolveFileName( const QString &fileName ) |
641 | { |
642 | KUrl url; |
643 | |
644 | //did we end up with an absolute path or a relative one? |
645 | if ( QFileInfo(fileName).isAbsolute() ) { |
646 | url.setPath( fileName ); |
647 | url.cleanPath(); |
648 | } |
649 | else { |
650 | url.setPath(m_targetConf.workDir); |
651 | url.addPath(fileName); |
652 | url.cleanPath(); |
653 | |
654 | if (!QFileInfo(url.path()).exists()) { |
655 | url.setPath(m_targetConf.executable); |
656 | url.upUrl(); // get path |
657 | url.addPath(fileName); |
658 | url.cleanPath(); |
659 | } |
660 | // Now, if not found just give up ;) |
661 | } |
662 | |
663 | return url; |
664 | } |
665 | |
666 | void DebugView::outputTextMaybe( const QString &text ) |
667 | { |
668 | if ( !m_lastCommand.startsWith( "(Q)" ) && !text.contains( PromptStr ) ) |
669 | { |
670 | emit outputText( text + '\n' ); |
671 | } |
672 | } |
673 | |
674 | |
675 | void DebugView::slotQueryLocals(bool query) |
676 | { |
677 | m_queryLocals = query; |
678 | if ( query && ( m_state == ready ) && ( m_nextCommands.size() == 0 ) ) |
679 | { |
680 | m_nextCommands << "(Q)info stack" ; |
681 | m_nextCommands << "(Q)frame" ; |
682 | m_nextCommands << "(Q)info args" ; |
683 | m_nextCommands << "(Q)print *this" ; |
684 | m_nextCommands << "(Q)info locals" ; |
685 | m_nextCommands << "(Q)info thread" ; |
686 | issueNextCommand(); |
687 | } |
688 | } |
689 | |