Switch KProcess to K3Process.
[kdbg.git] / kdbg / dbgdriver.cpp
blob690a53ce780cacf0a840bda6351c90d30b14cf77
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "dbgdriver.h"
8 #include "exprwnd.h"
9 #include <qstringlist.h>
10 #include <ctype.h>
11 #include <stdlib.h> /* strtol, atoi */
12 #include <algorithm>
13 #include "mydebug.h"
14 #include <assert.h>
17 DebuggerDriver::DebuggerDriver() :
18 K3Process(),
19 m_state(DSidle),
20 m_output(0),
21 m_outputLen(0),
22 m_outputAlloc(0),
23 m_activeCmd(0)
25 m_outputAlloc = 4000;
26 m_output = new char[m_outputAlloc];
28 // debugger process
29 connect(this, SIGNAL(receivedStdout(K3Process*,char*,int)),
30 SLOT(slotReceiveOutput(K3Process*,char*,int)));
31 connect(this, SIGNAL(wroteStdin(K3Process*)), SLOT(slotCommandRead(K3Process*)));
32 connect(this, SIGNAL(processExited(K3Process*)), SLOT(slotExited(K3Process*)));
35 DebuggerDriver::~DebuggerDriver()
37 delete[] m_output;
38 flushHiPriQueue();
39 flushLoPriQueue();
42 int DebuggerDriver::commSetupDoneC()
44 TRACE(__PRETTY_FUNCTION__);
46 if (!K3Process::commSetupDoneC())
47 return 0;
49 close(STDERR_FILENO);
50 return dup2(STDOUT_FILENO, STDERR_FILENO) != -1;
54 bool DebuggerDriver::startup(QString cmdStr)
56 // clear command queues
57 delete m_activeCmd;
58 m_activeCmd = 0;
59 flushHiPriQueue();
60 flushLoPriQueue();
61 m_state = DSidle;
63 // debugger executable
64 if (cmdStr.isEmpty())
65 cmdStr = defaultInvocation();
67 QStringList cmd = QStringList::split(' ', cmdStr);
68 clearArguments();
69 for (QStringList::iterator i = cmd.begin(); i != cmd.end(); ++i) {
70 *this << *i;
73 if (!start(K3Process::NotifyOnExit,
74 K3Process::Communication(K3Process::Stdin|K3Process::Stdout))) {
75 return false;
78 // open log file
79 if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
80 m_logFile.setName(m_logFileName);
81 m_logFile.open(QIODevice::WriteOnly);
84 return true;
87 void DebuggerDriver::slotExited(K3Process*)
89 static const char txt[] = "\n====== debugger exited ======\n";
90 if (m_logFile.isOpen()) {
91 m_logFile.writeBlock(txt,sizeof(txt)-1);
94 // reset state
95 m_state = DSidle;
96 // empty buffer
97 m_outputLen = 0;
98 *m_output = '\0';
102 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
103 QString cmdString, bool clearLow)
105 // place a new command into the high-priority queue
106 CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
107 m_hipriCmdQueue.push(cmdItem);
109 if (clearLow) {
110 if (m_state == DSrunningLow) {
111 // take the liberty to interrupt the running command
112 m_state = DSinterrupted;
113 kill(SIGINT);
114 ASSERT(m_activeCmd != 0);
115 TRACE(QString().sprintf("interrupted the command %d",
116 (m_activeCmd ? m_activeCmd->m_cmd : -1)));
117 delete m_activeCmd;
118 m_activeCmd = 0;
120 flushLoPriQueue();
122 // if gdb is idle, send it the command
123 if (m_state == DSidle) {
124 ASSERT(m_activeCmd == 0);
125 writeCommand();
128 return cmdItem;
131 bool CmdQueueItem::IsEqualCmd::operator()(CmdQueueItem* cmd) const
133 return cmd->m_cmd == m_cmd && cmd->m_cmdString == m_str;
136 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
137 QString cmdString, QueueMode mode)
139 // place a new command into the low-priority queue
140 std::list<CmdQueueItem*>::iterator i;
141 CmdQueueItem* cmdItem = 0;
142 switch (mode) {
143 case QMoverrideMoreEqual:
144 case QMoverride:
145 // check whether gdb is currently processing this command
146 if (m_activeCmd != 0 &&
147 m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
149 return m_activeCmd;
151 // check whether there is already the same command in the queue
152 i = find_if(m_lopriCmdQueue.begin(), m_lopriCmdQueue.end(), CmdQueueItem::IsEqualCmd(cmd, cmdString));
153 if (i != m_lopriCmdQueue.end()) {
154 // found one
155 cmdItem = *i;
156 if (mode == QMoverrideMoreEqual) {
157 // All commands are equal, but some are more equal than others...
158 // put this command in front of all others
159 m_lopriCmdQueue.erase(i);
160 m_lopriCmdQueue.push_front(cmdItem);
162 break;
163 } // else none found, so add it
164 // drop through
165 case QMnormal:
166 cmdItem = new CmdQueueItem(cmd, cmdString);
167 m_lopriCmdQueue.push_back(cmdItem);
170 // if gdb is idle, send it the command
171 if (m_state == DSidle) {
172 ASSERT(m_activeCmd == 0);
173 writeCommand();
176 return cmdItem;
179 // dequeue a pending command, make it the active one and send it to gdb
180 void DebuggerDriver::writeCommand()
182 // ASSERT(m_activeCmd == 0);
183 assert(m_activeCmd == 0);
185 // first check the high-priority queue - only if it is empty
186 // use a low-priority command.
187 CmdQueueItem* cmd;
188 DebuggerState newState = DScommandSent;
189 if (!m_hipriCmdQueue.empty()) {
190 cmd = m_hipriCmdQueue.front();
191 m_hipriCmdQueue.pop();
192 } else if (!m_lopriCmdQueue.empty()) {
193 cmd = m_lopriCmdQueue.front();
194 m_lopriCmdQueue.pop_front();
195 newState = DScommandSentLow;
196 } else {
197 // nothing to do
198 m_state = DSidle; /* is necessary if command was interrupted earlier */
199 return;
202 m_activeCmd = cmd;
203 TRACE("in writeCommand: " + cmd->m_cmdString);
205 const char* str = cmd->m_cmdString;
206 writeStdin(const_cast<char*>(str), cmd->m_cmdString.length());
208 // write also to log file
209 if (m_logFile.isOpen()) {
210 m_logFile.writeBlock(str, cmd->m_cmdString.length());
211 m_logFile.flush();
214 m_state = newState;
217 void DebuggerDriver::flushLoPriQueue()
219 while (!m_lopriCmdQueue.empty()) {
220 delete m_lopriCmdQueue.back();
221 m_lopriCmdQueue.pop_back();
225 void DebuggerDriver::flushHiPriQueue()
227 while (!m_hipriCmdQueue.empty()) {
228 delete m_hipriCmdQueue.front();
229 m_hipriCmdQueue.pop();
233 void DebuggerDriver::flushCommands(bool hipriOnly)
235 flushHiPriQueue();
236 if (!hipriOnly) {
237 flushLoPriQueue();
241 void DebuggerDriver::slotCommandRead(K3Process*)
243 TRACE(__PRETTY_FUNCTION__);
245 // there must be an active command which is not yet commited
246 ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
247 ASSERT(m_activeCmd != 0);
248 ASSERT(!m_activeCmd->m_committed);
250 // commit the command
251 m_activeCmd->m_committed = true;
253 // now the debugger is officially working on the command
254 m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
256 // set the flag that reflects whether the program is really running
257 switch (m_activeCmd->m_cmd) {
258 case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
259 emit inferiorRunning();
260 break;
261 default:
262 break;
265 // re-receive delayed output
266 while (!m_delayedOutput.empty()) {
267 QByteArray delayed = m_delayedOutput.front();
268 m_delayedOutput.pop();
269 slotReceiveOutput(0, const_cast<char*>(delayed.data()), delayed.size());
273 void DebuggerDriver::slotReceiveOutput(K3Process*, char* buffer, int buflen)
276 * The debugger should be running (processing a command) at this point.
277 * If it is not, it is still idle because we haven't received the
278 * wroteStdin signal yet, in which case there must be an active command
279 * which is not commited.
281 if (m_state == DScommandSent || m_state == DScommandSentLow) {
282 ASSERT(m_activeCmd != 0);
283 ASSERT(!m_activeCmd->m_committed);
285 * We received output before we got signal wroteStdin. Collect this
286 * output, it will be re-sent by commandRead when it gets the
287 * acknowledgment for the uncommitted command.
289 m_delayedOutput.push(QByteArray().duplicate(buffer, buflen));
290 return;
292 // write to log file (do not log delayed output - it would appear twice)
293 if (m_logFile.isOpen()) {
294 m_logFile.writeBlock(buffer, buflen);
295 m_logFile.flush();
299 * gdb sometimes produces stray output while it's idle. This happens if
300 * it receives a signal, most prominently a SIGCONT after a SIGTSTP:
301 * The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
302 * also continues gdb, which repeats the prompt!
304 if (m_activeCmd == 0 && m_state != DSinterrupted) {
305 // ignore the output
306 TRACE("ignoring stray output: " + QString::fromLatin1(buffer, buflen));
307 return;
309 ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
310 ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
312 // collect output until next prompt string is found
314 // accumulate it
315 if (m_outputLen + buflen >= m_outputAlloc) {
317 * Must enlarge m_output: double it. Note: That particular
318 * sequence of commandes here ensures exception safety.
320 int newSize = m_outputAlloc * 2;
321 char* newBuf = new char[newSize];
322 memcpy(newBuf, m_output, m_outputLen);
323 delete[] m_output;
324 m_output = newBuf;
325 m_outputAlloc = newSize;
327 memcpy(m_output+m_outputLen, buffer, buflen);
328 m_outputLen += buflen;
329 m_output[m_outputLen] = '\0';
331 // check for a prompt
332 int promptStart = findPrompt(m_output, m_outputLen);
333 if (promptStart >= 0)
335 // found prompt!
337 // terminate output before the prompt
338 m_output[promptStart] = '\0';
341 * We've got output for the active command. But if it was
342 * interrupted, ignore it.
344 if (m_state != DSinterrupted) {
346 * m_state shouldn't be DSidle while we are parsing the output
347 * so that all commands produced by parse() go into the queue
348 * instead of being written to gdb immediately.
350 ASSERT(m_state != DSidle);
351 CmdQueueItem* cmd = m_activeCmd;
352 m_activeCmd = 0;
353 commandFinished(cmd);
354 delete cmd;
357 // empty buffer
358 m_outputLen = 0;
359 *m_output = '\0';
360 // also clear delayed output if interrupted
361 if (m_state == DSinterrupted) {
362 m_delayedOutput = std::queue<QByteArray>();
366 * We parsed some output successfully. Unless there's more delayed
367 * output, the debugger must be idle now, so send down the next
368 * command.
370 if (m_delayedOutput.empty()) {
371 if (m_hipriCmdQueue.empty() && m_lopriCmdQueue.empty()) {
372 // no pending commands
373 m_state = DSidle;
374 emit enterIdleState();
375 } else {
376 writeCommand();
382 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
384 if (var == 0)
385 return;
387 std::list<CmdQueueItem*>::iterator i = m_lopriCmdQueue.begin();
388 while (i != m_lopriCmdQueue.end()) {
389 if ((*i)->m_expr != 0 && var->isAncestorEq((*i)->m_expr)) {
390 // this is indeed a critical command; delete it
391 TRACE("removing critical lopri-cmd: " + (*i)->m_cmdString);
392 delete *i;
393 m_lopriCmdQueue.erase(i++);
394 } else
395 ++i;
400 QString DebuggerDriver::editableValue(VarTree* value)
402 // by default, let the user edit what is visible
403 return value->value();
407 StackFrame::~StackFrame()
409 delete var;
413 DbgAddr::DbgAddr(const QString& aa) :
414 a(aa)
416 cleanAddr();
420 * We strip off the leading 0x and any leading zeros.
422 void DbgAddr::cleanAddr()
424 if (a.isEmpty())
425 return;
427 while (a[0] == '0' || a[0] == 'x') {
428 a.remove(0, 1);
432 void DbgAddr::operator=(const QString& aa)
434 a = aa;
435 fnoffs = QString();
436 cleanAddr();
439 /* Re-attach 0x in front of the address */
440 QString DbgAddr::asString() const
442 if (a.isEmpty())
443 return QString();
444 else
445 return "0x" + a;
448 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
450 return QString::compare(a1.a, a2.a) == 0;
453 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
455 if (a1.a.length() > a2.a.length())
456 return true;
457 if (a1.a.length() < a2.a.length())
458 return false;
459 return QString::compare(a1.a, a2.a) > 0;
463 Breakpoint::Breakpoint() :
464 id(0),
465 type(breakpoint),
466 temporary(false),
467 enabled(true),
468 ignoreCount(0),
469 hitCount(0),
470 lineNo(0)
473 #include "dbgdriver.moc"