Update my email address.
[kdbg.git] / kdbg / dbgdriver.cpp
bloba3652c342d6ad2f4b333a53bf1920778b5613d81
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include "dbgdriver.h"
7 #include "exprwnd.h"
8 #include "valarray.h"
9 #include <ctype.h>
10 #include <stdlib.h> /* strtol, atoi */
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14 #include "mydebug.h"
15 #include <assert.h>
18 DebuggerDriver::DebuggerDriver() :
19 KProcess(),
20 m_state(DSidle),
21 m_output(0),
22 m_outputLen(0),
23 m_outputAlloc(0),
24 m_activeCmd(0),
25 m_promptMinLen(0),
26 m_promptLastChar('\0')
28 m_outputAlloc = 4000;
29 m_output = new char[m_outputAlloc];
31 m_prompt[0] = '\0';
32 // Derived classes can set either m_prompt or m_promptRE.
33 // If m_promptRE is set, it must include the '$' at the end.
34 // m_promptLastChar and m_promptMinLen must also be set.
36 m_hipriCmdQueue.setAutoDelete(true);
38 // debugger process
39 connect(this, SIGNAL(receivedStdout(KProcess*,char*,int)),
40 SLOT(slotReceiveOutput(KProcess*,char*,int)));
41 connect(this, SIGNAL(wroteStdin(KProcess*)), SLOT(slotCommandRead(KProcess*)));
42 connect(this, SIGNAL(processExited(KProcess*)), SLOT(slotExited(KProcess*)));
45 DebuggerDriver::~DebuggerDriver()
47 delete[] m_output;
50 int DebuggerDriver::commSetupDoneC()
52 TRACE(__PRETTY_FUNCTION__);
54 if (!KProcess::commSetupDoneC())
55 return 0;
57 close(STDERR_FILENO);
58 return dup2(STDOUT_FILENO, STDERR_FILENO) != -1;
61 static void splitCmdStr(const QString& cmd, ValArray<QString>& parts)
63 QString str = cmd.simplifyWhiteSpace();
64 int start = 0;
65 int end;
66 while ((end = str.find(' ', start)) >= 0) {
67 parts.append(str.mid(start, end-start));
68 start = end+1;
70 parts.append(str.mid(start, str.length()-start));
74 bool DebuggerDriver::startup(QString cmdStr)
76 // clear command queues
77 delete m_activeCmd;
78 m_activeCmd = 0;
79 m_hipriCmdQueue.clear();
80 bool autodel = m_lopriCmdQueue.autoDelete();
81 m_lopriCmdQueue.setAutoDelete(true);
82 m_lopriCmdQueue.clear();
83 m_lopriCmdQueue.setAutoDelete(autodel);
84 m_state = DSidle;
86 // debugger executable
87 if (cmdStr.isEmpty())
88 cmdStr = defaultInvocation();
90 ValArray<QString> cmd;
91 splitCmdStr(cmdStr, cmd);
92 clearArguments();
93 for (int i = 0; i < cmd.size(); i++) {
94 *this << cmd[i];
97 if (!start(KProcess::NotifyOnExit,
98 KProcess::Communication(KProcess::Stdin|KProcess::Stdout))) {
99 return false;
102 // open log file
103 if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
104 m_logFile.setName(m_logFileName);
105 m_logFile.open(IO_WriteOnly);
108 // these must be set by derived classes in their constructor
109 assert(m_promptMinLen > 0); // _must_ have a prompt
110 assert(m_promptLastChar != '\0'); // last char _must_ be fixed
111 assert(m_prompt[0] == '\0' || strlen(m_prompt) == m_promptMinLen);
112 // either a prompt or a regexp
113 assert(m_prompt[0] != '\0' && m_promptMinLen < sizeof(m_prompt) ||
114 !m_promptRE.isEmpty() && m_promptRE.isValid());
116 return true;
119 void DebuggerDriver::slotExited(KProcess*)
121 static const char txt[] = "\n====== debugger exited ======\n";
122 if (m_logFile.isOpen()) {
123 m_logFile.writeBlock(txt,sizeof(txt)-1);
126 // reset state
127 m_state = DSidle;
128 // empty buffer
129 m_outputLen = 0;
130 *m_output = '\0';
134 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
135 QString cmdString, bool clearLow)
137 // place a new command into the high-priority queue
138 CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
139 m_hipriCmdQueue.enqueue(cmdItem);
141 if (clearLow) {
142 if (m_state == DSrunningLow) {
143 // take the liberty to interrupt the running command
144 m_state = DSinterrupted;
145 kill(SIGINT);
146 ASSERT(m_activeCmd != 0);
147 TRACE(QString().sprintf("interrupted the command %d",
148 (m_activeCmd ? m_activeCmd->m_cmd : -1)));
149 delete m_activeCmd;
150 m_activeCmd = 0;
152 flushLoPriQueue();
154 // if gdb is idle, send it the command
155 if (m_state == DSidle) {
156 ASSERT(m_activeCmd == 0);
157 writeCommand();
160 return cmdItem;
163 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
164 QString cmdString, QueueMode mode)
166 // place a new command into the low-priority queue
167 CmdQueueItem* cmdItem = 0;
168 switch (mode) {
169 case QMoverrideMoreEqual:
170 case QMoverride:
171 // check whether gdb is currently processing this command
172 if (m_activeCmd != 0 &&
173 m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
175 return m_activeCmd;
177 // check whether there is already the same command in the queue
178 for (cmdItem = m_lopriCmdQueue.first(); cmdItem != 0; cmdItem = m_lopriCmdQueue.next()) {
179 if (cmdItem->m_cmd == cmd && cmdItem->m_cmdString == cmdString)
180 break;
182 if (cmdItem != 0) {
183 // found one
184 if (mode == QMoverrideMoreEqual) {
185 // All commands are equal, but some are more equal than others...
186 // put this command in front of all others
187 m_lopriCmdQueue.take();
188 m_lopriCmdQueue.insert(0, cmdItem);
190 break;
191 } // else none found, so add it
192 // drop through
193 case QMnormal:
194 cmdItem = new CmdQueueItem(cmd, cmdString);
195 m_lopriCmdQueue.append(cmdItem);
198 // if gdb is idle, send it the command
199 if (m_state == DSidle) {
200 ASSERT(m_activeCmd == 0);
201 writeCommand();
204 return cmdItem;
207 // dequeue a pending command, make it the active one and send it to gdb
208 void DebuggerDriver::writeCommand()
210 // ASSERT(m_activeCmd == 0);
211 assert(m_activeCmd == 0);
213 // first check the high-priority queue - only if it is empty
214 // use a low-priority command.
215 CmdQueueItem* cmd = m_hipriCmdQueue.dequeue();
216 DebuggerState newState = DScommandSent;
217 if (cmd == 0) {
218 cmd = m_lopriCmdQueue.first();
219 m_lopriCmdQueue.removeFirst();
220 newState = DScommandSentLow;
222 if (cmd == 0) {
223 // nothing to do
224 m_state = DSidle; /* is necessary if command was interrupted earlier */
225 return;
228 m_activeCmd = cmd;
229 TRACE("in writeCommand: " + cmd->m_cmdString);
231 const char* str = cmd->m_cmdString;
232 writeStdin(const_cast<char*>(str), cmd->m_cmdString.length());
234 // write also to log file
235 if (m_logFile.isOpen()) {
236 m_logFile.writeBlock(str, cmd->m_cmdString.length());
237 m_logFile.flush();
240 m_state = newState;
243 void DebuggerDriver::flushLoPriQueue()
245 while (!m_lopriCmdQueue.isEmpty()) {
246 delete m_lopriCmdQueue.take(0);
250 void DebuggerDriver::flushHiPriQueue()
252 CmdQueueItem* cmd;
253 while ((cmd = m_hipriCmdQueue.dequeue()) != 0) {
254 delete cmd;
258 void DebuggerDriver::flushCommands(bool hipriOnly)
260 flushHiPriQueue();
261 if (!hipriOnly) {
262 flushLoPriQueue();
266 void DebuggerDriver::slotCommandRead(KProcess*)
268 TRACE(__PRETTY_FUNCTION__);
270 // there must be an active command which is not yet commited
271 ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
272 ASSERT(m_activeCmd != 0);
273 ASSERT(!m_activeCmd->m_committed);
275 // commit the command
276 m_activeCmd->m_committed = true;
278 // now the debugger is officially working on the command
279 m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
281 // set the flag that reflects whether the program is really running
282 switch (m_activeCmd->m_cmd) {
283 case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
284 emit inferiorRunning();
285 break;
286 default:
287 break;
290 // re-receive delayed output
291 if (m_delayedOutput.current() != 0) {
292 DelayedStr* delayed;
293 while ((delayed = m_delayedOutput.dequeue()) != 0) {
294 const char* str = delayed->data();
295 slotReceiveOutput(0, const_cast<char*>(str), delayed->length());
296 delete delayed;
301 void DebuggerDriver::slotReceiveOutput(KProcess*, char* buffer, int buflen)
304 * The debugger should be running (processing a command) at this point.
305 * If it is not, it is still idle because we haven't received the
306 * wroteStdin signal yet, in which case there must be an active command
307 * which is not commited.
309 if (m_state == DScommandSent || m_state == DScommandSentLow) {
310 ASSERT(m_activeCmd != 0);
311 ASSERT(!m_activeCmd->m_committed);
313 * We received output before we got signal wroteStdin. Collect this
314 * output, it will be re-sent by commandRead when it gets the
315 * acknowledgment for the uncommitted command.
317 m_delayedOutput.enqueue(new DelayedStr(buffer, buflen+1));
318 return;
320 // write to log file (do not log delayed output - it would appear twice)
321 if (m_logFile.isOpen()) {
322 m_logFile.writeBlock(buffer, buflen);
323 m_logFile.flush();
327 * gdb sometimes produces stray output while it's idle. This happens if
328 * it receives a signal, most prominently a SIGCONT after a SIGTSTP:
329 * The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
330 * also continues gdb, which repeats the prompt!
332 if (m_activeCmd == 0 && m_state != DSinterrupted) {
333 // ignore the output
334 TRACE("ignoring stray output: " + DelayedStr(buffer, buflen+1));
335 return;
337 ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
338 ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
340 // collect output until next prompt string is found
342 // accumulate it
343 if (m_outputLen + buflen >= m_outputAlloc) {
345 * Must enlarge m_output: double it. Note: That particular
346 * sequence of commandes here ensures exception safety.
348 int newSize = m_outputAlloc * 2;
349 char* newBuf = new char[newSize];
350 memcpy(newBuf, m_output, m_outputLen);
351 delete[] m_output;
352 m_output = newBuf;
353 m_outputAlloc = newSize;
355 memcpy(m_output+m_outputLen, buffer, buflen);
356 m_outputLen += buflen;
357 m_output[m_outputLen] = '\0';
360 * If there's a prompt string in the collected output, it must be at
361 * the very end. In order to quickly find out whether there is a prompt
362 * string, we check whether the last character of m_output is identical
363 * to the last character of the prompt string. Only if it is, we check
364 * for the full prompt string.
366 * Note: Using the regular expression here is most expensive, because
367 * it translates m_output to a QString each time.
369 * Note: It could nevertheless happen that a character sequence that is
370 * equal to the prompt string appears at the end of the output,
371 * although it is very, very unlikely (namely as part of a string that
372 * lingered in gdb's output buffer due to some timing/heavy load
373 * conditions for a very long time such that that buffer overflowed
374 * exactly at the end of the prompt string look-a-like).
376 int promptStart = -1;
377 if (m_output[m_outputLen-1] == m_promptLastChar &&
378 m_outputLen >= m_promptMinLen)
380 // this is a candidate for a prompt at the end,
381 // now see if there really is
382 if (m_prompt[0] != '\0') {
383 if (strncmp(m_output+m_outputLen-m_promptMinLen,
384 m_prompt, m_promptMinLen) == 0)
386 promptStart = m_outputLen-m_promptMinLen;
388 } else {
389 QString output = QString::fromLatin1(m_output, m_outputLen);
390 #if QT_VERSION >= 300
391 promptStart = m_promptRE.search(output);
392 #else
393 promptStart = m_promptRE.match(output);
394 #endif
397 if (promptStart >= 0)
399 // found prompt!
401 // terminate output before the prompt
402 m_output[promptStart] = '\0';
405 * We've got output for the active command. But if it was
406 * interrupted, ignore it.
408 if (m_state != DSinterrupted) {
410 * m_state shouldn't be DSidle while we are parsing the output
411 * so that all commands produced by parse() go into the queue
412 * instead of being written to gdb immediately.
414 ASSERT(m_state != DSidle);
415 CmdQueueItem* cmd = m_activeCmd;
416 m_activeCmd = 0;
417 commandFinished(cmd);
418 delete cmd;
421 // empty buffer
422 m_outputLen = 0;
423 *m_output = '\0';
424 // also clear delayed output if interrupted
425 if (m_state == DSinterrupted) {
426 DelayedStr* delayed;
427 while ((delayed = m_delayedOutput.dequeue()) != 0) {
428 delete delayed;
433 * We parsed some output successfully. Unless there's more delayed
434 * output, the debugger must be idle now, so send down the next
435 * command.
437 if (m_delayedOutput.current() == 0) {
438 if (m_hipriCmdQueue.isEmpty() && m_lopriCmdQueue.isEmpty()) {
439 // no pending commands
440 m_state = DSidle;
441 emit enterIdleState();
442 } else {
443 writeCommand();
449 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
451 if (var == 0)
452 return;
455 * Check the low-priority queue: We start at the back end, but skip the
456 * last element for now. The reason is that if we delete an element the
457 * current element is stepped to the next one - except if it's on the
458 * last: then it's stepped to the previous element. By checking the
459 * last element separately we avoid that special case.
461 CmdQueueItem* cmd = m_lopriCmdQueue.last();
462 while ((cmd = m_lopriCmdQueue.prev()) != 0) {
463 if (cmd->m_expr != 0 && var->isAncestorEq(cmd->m_expr)) {
464 // this is indeed a critical command; delete it
465 TRACE("removing critical lopri-cmd: " + cmd->m_cmdString);
466 m_lopriCmdQueue.remove(); /* steps to next element */
469 cmd = m_lopriCmdQueue.last();
470 if (cmd != 0) {
471 if (cmd->m_expr != 0 && var->isAncestorEq(cmd->m_expr)) {
472 TRACE("removing critical lopri-cmd: " + cmd->m_cmdString);
473 m_lopriCmdQueue.remove(); /* steps to next element */
479 QString DebuggerDriver::editableValue(VarTree* value)
481 // by default, let the user edit what is visible
482 return value->value();
486 StackFrame::~StackFrame()
488 delete var;
492 DbgAddr::DbgAddr(const QString& aa) :
493 a(aa)
495 cleanAddr();
499 * We strip off the leading 0x and any leading zeros.
501 void DbgAddr::cleanAddr()
503 if (a.isEmpty())
504 return;
506 while (a[0] == '0' || a[0] == 'x') {
507 a.remove(0, 1);
511 void DbgAddr::operator=(const QString& aa)
513 a = aa;
514 fnoffs = QString();
515 cleanAddr();
518 /* Re-attach 0x in front of the address */
519 QString DbgAddr::asString() const
521 if (a.isEmpty())
522 return QString();
523 else
524 return "0x" + a;
527 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
529 return QString::compare(a1.a, a2.a) == 0;
532 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
534 if (a1.a.length() > a2.a.length())
535 return true;
536 if (a1.a.length() < a2.a.length())
537 return false;
538 return QString::compare(a1.a, a2.a) > 0;
542 Breakpoint::Breakpoint() :
543 id(0),
544 type(breakpoint),
545 temporary(false),
546 enabled(true),
547 ignoreCount(0),
548 hitCount(0),
549 lineNo(0)
552 #include "dbgdriver.moc"