Upgraded the build system for KDE2.
[kdbg.git] / kdbg / dbgdriver.cpp
blob868659938a5f1a86fc224f62d79f6e47129c205b
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"
17 DebuggerDriver::DebuggerDriver() :
18 KProcess(),
19 m_state(DSidle),
20 m_output(0),
21 m_outputLen(0),
22 m_outputAlloc(0),
23 m_activeCmd(0),
24 m_promptLen(1),
25 m_promptLastChar(' ')
27 m_outputAlloc = 4000;
28 m_output = new char[m_outputAlloc];
30 m_prompt[0] = ' ';
31 m_prompt[1] = '\0';
33 m_hipriCmdQueue.setAutoDelete(true);
35 // debugger process
36 connect(this, SIGNAL(receivedStdout(KProcess*,char*,int)),
37 SLOT(slotReceiveOutput(KProcess*,char*,int)));
38 connect(this, SIGNAL(wroteStdin(KProcess*)), SLOT(slotCommandRead(KProcess*)));
39 connect(this, SIGNAL(processExited(KProcess*)), SLOT(slotExited(KProcess*)));
42 DebuggerDriver::~DebuggerDriver()
44 delete[] m_output;
47 int DebuggerDriver::commSetupDoneC()
49 TRACE(__PRETTY_FUNCTION__);
51 if (!KProcess::commSetupDoneC())
52 return 0;
54 close(STDERR_FILENO);
55 return dup2(STDOUT_FILENO, STDERR_FILENO) != -1;
58 static void splitCmdStr(const QString& cmd, ValArray<QString>& parts)
60 QString str = cmd.simplifyWhiteSpace();
61 int start = 0;
62 int end;
63 while ((end = str.find(' ', start)) >= 0) {
64 parts.append(str.mid(start, end-start));
65 start = end+1;
67 parts.append(str.mid(start, str.length()-start));
71 bool DebuggerDriver::startup(QString cmdStr)
73 // clear command queues
74 delete m_activeCmd;
75 m_activeCmd = 0;
76 m_hipriCmdQueue.clear();
77 bool autodel = m_lopriCmdQueue.autoDelete();
78 m_lopriCmdQueue.setAutoDelete(true);
79 m_lopriCmdQueue.clear();
80 m_lopriCmdQueue.setAutoDelete(autodel);
81 m_state = DSidle;
83 // debugger executable
84 if (cmdStr.isEmpty())
85 cmdStr = defaultInvocation();
87 ValArray<QString> cmd;
88 splitCmdStr(cmdStr, cmd);
89 clearArguments();
90 for (int i = 0; i < cmd.size(); i++) {
91 *this << cmd[i];
94 if (!start(KProcess::NotifyOnExit,
95 KProcess::Communication(KProcess::Stdin|KProcess::Stdout))) {
96 return false;
99 // open log file
100 if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
101 m_logFile.setName(m_logFileName);
102 m_logFile.open(IO_WriteOnly);
105 return true;
108 void DebuggerDriver::slotExited(KProcess*)
110 static const char txt[] = "\n====== debugger exited ======\n";
111 if (m_logFile.isOpen()) {
112 m_logFile.writeBlock(txt,sizeof(txt)-1);
115 // reset state
116 m_state = DSidle;
117 // empty buffer
118 m_outputLen = 0;
119 *m_output = '\0';
123 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
124 QString cmdString, bool clearLow)
126 // place a new command into the high-priority queue
127 CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
128 m_hipriCmdQueue.enqueue(cmdItem);
130 if (clearLow) {
131 if (m_state == DSrunningLow) {
132 // take the liberty to interrupt the running command
133 m_state = DSinterrupted;
134 kill(SIGINT);
135 ASSERT(m_activeCmd != 0);
136 TRACE(QString().sprintf("interrupted the command %d",
137 (m_activeCmd ? m_activeCmd->m_cmd : -1)));
138 delete m_activeCmd;
139 m_activeCmd = 0;
141 flushLoPriQueue();
143 // if gdb is idle, send it the command
144 if (m_state == DSidle) {
145 ASSERT(m_activeCmd == 0);
146 writeCommand();
149 return cmdItem;
152 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
153 QString cmdString, QueueMode mode)
155 // place a new command into the low-priority queue
156 CmdQueueItem* cmdItem = 0;
157 switch (mode) {
158 case QMoverrideMoreEqual:
159 case QMoverride:
160 // check whether gdb is currently processing this command
161 if (m_activeCmd != 0 &&
162 m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
164 return m_activeCmd;
166 // check whether there is already the same command in the queue
167 for (cmdItem = m_lopriCmdQueue.first(); cmdItem != 0; cmdItem = m_lopriCmdQueue.next()) {
168 if (cmdItem->m_cmd == cmd && cmdItem->m_cmdString == cmdString)
169 break;
171 if (cmdItem != 0) {
172 // found one
173 if (mode == QMoverrideMoreEqual) {
174 // All commands are equal, but some are more equal than others...
175 // put this command in front of all others
176 m_lopriCmdQueue.take();
177 m_lopriCmdQueue.insert(0, cmdItem);
179 break;
180 } // else none found, so add it
181 // drop through
182 case QMnormal:
183 cmdItem = new CmdQueueItem(cmd, cmdString);
184 m_lopriCmdQueue.append(cmdItem);
187 // if gdb is idle, send it the command
188 if (m_state == DSidle) {
189 ASSERT(m_activeCmd == 0);
190 writeCommand();
193 return cmdItem;
196 // dequeue a pending command, make it the active one and send it to gdb
197 void DebuggerDriver::writeCommand()
199 // ASSERT(m_activeCmd == 0);
200 assert(m_activeCmd == 0);
202 // first check the high-priority queue - only if it is empty
203 // use a low-priority command.
204 CmdQueueItem* cmd = m_hipriCmdQueue.dequeue();
205 DebuggerState newState = DScommandSent;
206 if (cmd == 0) {
207 cmd = m_lopriCmdQueue.first();
208 m_lopriCmdQueue.removeFirst();
209 newState = DScommandSentLow;
211 if (cmd == 0) {
212 // nothing to do
213 m_state = DSidle; /* is necessary if command was interrupted earlier */
214 return;
217 m_activeCmd = cmd;
218 TRACE("in writeCommand: " + cmd->m_cmdString);
220 const char* str = cmd->m_cmdString;
221 writeStdin(const_cast<char*>(str), cmd->m_cmdString.length());
223 // write also to log file
224 if (m_logFile.isOpen()) {
225 m_logFile.writeBlock(str, cmd->m_cmdString.length());
226 m_logFile.flush();
229 m_state = newState;
232 void DebuggerDriver::flushLoPriQueue()
234 while (!m_lopriCmdQueue.isEmpty()) {
235 delete m_lopriCmdQueue.take(0);
239 void DebuggerDriver::flushHiPriQueue()
241 CmdQueueItem* cmd;
242 while ((cmd = m_hipriCmdQueue.dequeue()) != 0) {
243 delete cmd;
247 void DebuggerDriver::flushCommands(bool hipriOnly)
249 flushHiPriQueue();
250 if (!hipriOnly) {
251 flushLoPriQueue();
255 void DebuggerDriver::slotCommandRead(KProcess*)
257 TRACE(__PRETTY_FUNCTION__);
259 // there must be an active command which is not yet commited
260 ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
261 ASSERT(m_activeCmd != 0);
262 ASSERT(!m_activeCmd->m_committed);
264 // commit the command
265 m_activeCmd->m_committed = true;
267 // now the debugger is officially working on the command
268 m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
270 // set the flag that reflects whether the program is really running
271 switch (m_activeCmd->m_cmd) {
272 case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
273 emit inferiorRunning();
274 break;
275 default:
276 break;
279 // re-receive delayed output
280 if (m_delayedOutput.current() != 0) {
281 DelayedStr* delayed;
282 while ((delayed = m_delayedOutput.dequeue()) != 0) {
283 const char* str = delayed->data();
284 slotReceiveOutput(0, const_cast<char*>(str), delayed->length());
285 delete delayed;
290 void DebuggerDriver::slotReceiveOutput(KProcess*, char* buffer, int buflen)
293 * The debugger should be running (processing a command) at this point.
294 * If it is not, it is still idle because we haven't received the
295 * wroteStdin signal yet, in which case there must be an active command
296 * which is not commited.
298 if (m_state == DScommandSent || m_state == DScommandSentLow) {
299 ASSERT(m_activeCmd != 0);
300 ASSERT(!m_activeCmd->m_committed);
302 * We received output before we got signal wroteStdin. Collect this
303 * output, it will be re-sent by commandRead when it gets the
304 * acknowledgment for the uncommitted command.
306 m_delayedOutput.enqueue(new DelayedStr(buffer, buflen+1));
307 return;
309 // write to log file (do not log delayed output - it would appear twice)
310 if (m_logFile.isOpen()) {
311 m_logFile.writeBlock(buffer, buflen);
312 m_logFile.flush();
316 * gdb sometimes produces stray output while it's idle. This happens if
317 * it receives a signal, most prominently a SIGCONT after a SIGTSTP:
318 * The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
319 * also continues gdb, which repeats the prompt!
321 if (m_activeCmd == 0 && m_state != DSinterrupted) {
322 // ignore the output
323 TRACE("ignoring stray output: " + DelayedStr(buffer, buflen+1));
324 return;
326 ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
327 ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
329 // collect output until next prompt string is found
331 // accumulate it
332 if (m_outputLen + buflen >= m_outputAlloc) {
334 * Must enlarge m_output: double it. Note: That particular
335 * sequence of commandes here ensures exception safety.
337 int newSize = m_outputAlloc * 2;
338 char* newBuf = new char[newSize];
339 memcpy(newBuf, m_output, m_outputLen);
340 delete[] m_output;
341 m_output = newBuf;
342 m_outputAlloc = newSize;
344 memcpy(m_output+m_outputLen, buffer, buflen);
345 m_outputLen += buflen;
346 m_output[m_outputLen] = '\0';
349 * If there's a prompt string in the collected output, it must be at
350 * the very end.
352 * Note: It could nevertheless happen that a character sequence that is
353 * equal to the prompt string appears at the end of the output,
354 * although it is very, very unlikely (namely as part of a string that
355 * lingered in gdb's output buffer due to some timing/heavy load
356 * conditions for a very long time such that that buffer overflowed
357 * exactly at the end of the prompt string look-a-like).
359 if (m_output[m_outputLen-1] == m_promptLastChar &&
360 m_outputLen >= m_promptLen &&
361 strncmp(m_output+m_outputLen-m_promptLen, m_prompt, m_promptLen) == 0)
363 // found prompt!
365 // terminate output before the prompt
366 m_output[m_outputLen-m_promptLen] = '\0';
369 * We've got output for the active command. But if it was
370 * interrupted, ignore it.
372 if (m_state != DSinterrupted) {
374 * m_state shouldn't be DSidle while we are parsing the output
375 * so that all commands produced by parse() go into the queue
376 * instead of being written to gdb immediately.
378 ASSERT(m_state != DSidle);
379 CmdQueueItem* cmd = m_activeCmd;
380 m_activeCmd = 0;
381 commandFinished(cmd);
382 delete cmd;
385 // empty buffer
386 m_outputLen = 0;
387 *m_output = '\0';
388 // also clear delayed output if interrupted
389 if (m_state == DSinterrupted) {
390 DelayedStr* delayed;
391 while ((delayed = m_delayedOutput.dequeue()) != 0) {
392 delete delayed;
397 * We parsed some output successfully. Unless there's more delayed
398 * output, the debugger must be idle now, so send down the next
399 * command.
401 if (m_delayedOutput.current() == 0) {
402 if (m_hipriCmdQueue.isEmpty() && m_lopriCmdQueue.isEmpty()) {
403 // no pending commands
404 m_state = DSidle;
405 emit enterIdleState();
406 } else {
407 writeCommand();
413 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
415 if (var == 0)
416 return;
419 * Check the low-priority queue: We start at the back end, but skip the
420 * last element for now. The reason is that if we delete an element the
421 * current element is stepped to the next one - except if it's on the
422 * last: then it's stepped to the previous element. By checking the
423 * last element separately we avoid that special case.
425 CmdQueueItem* cmd = m_lopriCmdQueue.last();
426 while ((cmd = m_lopriCmdQueue.prev()) != 0) {
427 if (cmd->m_expr != 0 && var->isAncestorEq(cmd->m_expr)) {
428 // this is indeed a critical command; delete it
429 TRACE("removing critical lopri-cmd: " + cmd->m_cmdString);
430 m_lopriCmdQueue.remove(); /* steps to next element */
433 cmd = m_lopriCmdQueue.last();
434 if (cmd != 0) {
435 if (cmd->m_expr != 0 && var->isAncestorEq(cmd->m_expr)) {
436 TRACE("removing critical lopri-cmd: " + cmd->m_cmdString);
437 m_lopriCmdQueue.remove(); /* steps to next element */
442 StackFrame::~StackFrame()
444 delete var;
448 DbgAddr::DbgAddr(const QString& aa) :
449 a(aa)
451 cleanAddr();
455 * We strip off the leading 0x and any leading zeros.
457 void DbgAddr::cleanAddr()
459 if (a.isEmpty())
460 return;
462 while (a[0] == '0' || a[0] == 'x') {
463 a.remove(0, 1);
467 void DbgAddr::operator=(const QString& aa)
469 a = aa;
470 fnoffs = QString();
471 cleanAddr();
474 /* Re-attach 0x in front of the address */
475 QString DbgAddr::asString() const
477 if (a.isEmpty())
478 return QString();
479 else
480 return "0x" + a;
483 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
485 #if QT_VERSION < 200
486 return strcmp(a1.a, a2.a) == 0;
487 #else
488 return QString::compare(a1.a, a2.a) == 0;
489 #endif
492 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
494 if (a1.a.length() > a2.a.length())
495 return true;
496 if (a1.a.length() < a2.a.length())
497 return false;
498 #if QT_VERSION < 200
499 return strcmp(a1.a, a2.a) > 0;
500 #else
501 return QString::compare(a1.a, a2.a) > 0;
502 #endif
506 #include "dbgdriver.moc"