Remove Help button from the program arguments dialog.
[kdbg.git] / kdbg / dbgdriver.cpp
blob2f4bcf656868c4e7ecd04fc3740047378aab11f4
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 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16 #include "mydebug.h"
17 #include <assert.h>
20 DebuggerDriver::DebuggerDriver() :
21 KProcess(),
22 m_state(DSidle),
23 m_output(0),
24 m_outputLen(0),
25 m_outputAlloc(0),
26 m_activeCmd(0)
28 m_outputAlloc = 4000;
29 m_output = new char[m_outputAlloc];
31 // debugger process
32 connect(this, SIGNAL(receivedStdout(KProcess*,char*,int)),
33 SLOT(slotReceiveOutput(KProcess*,char*,int)));
34 connect(this, SIGNAL(wroteStdin(KProcess*)), SLOT(slotCommandRead(KProcess*)));
35 connect(this, SIGNAL(processExited(KProcess*)), SLOT(slotExited(KProcess*)));
38 DebuggerDriver::~DebuggerDriver()
40 delete[] m_output;
41 flushHiPriQueue();
42 flushLoPriQueue();
45 int DebuggerDriver::commSetupDoneC()
47 TRACE(__PRETTY_FUNCTION__);
49 if (!KProcess::commSetupDoneC())
50 return 0;
52 close(STDERR_FILENO);
53 return dup2(STDOUT_FILENO, STDERR_FILENO) != -1;
57 bool DebuggerDriver::startup(QString cmdStr)
59 // clear command queues
60 delete m_activeCmd;
61 m_activeCmd = 0;
62 flushHiPriQueue();
63 flushLoPriQueue();
64 m_state = DSidle;
66 // debugger executable
67 if (cmdStr.isEmpty())
68 cmdStr = defaultInvocation();
70 QStringList cmd = QStringList::split(' ', cmdStr);
71 clearArguments();
72 for (QStringList::iterator i = cmd.begin(); i != cmd.end(); ++i) {
73 *this << *i;
76 if (!start(KProcess::NotifyOnExit,
77 KProcess::Communication(KProcess::Stdin|KProcess::Stdout))) {
78 return false;
81 // open log file
82 if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
83 m_logFile.setName(m_logFileName);
84 m_logFile.open(IO_WriteOnly);
87 return true;
90 void DebuggerDriver::slotExited(KProcess*)
92 static const char txt[] = "\n====== debugger exited ======\n";
93 if (m_logFile.isOpen()) {
94 m_logFile.writeBlock(txt,sizeof(txt)-1);
97 // reset state
98 m_state = DSidle;
99 // empty buffer
100 m_outputLen = 0;
101 *m_output = '\0';
105 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
106 QString cmdString, bool clearLow)
108 // place a new command into the high-priority queue
109 CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
110 m_hipriCmdQueue.push(cmdItem);
112 if (clearLow) {
113 if (m_state == DSrunningLow) {
114 // take the liberty to interrupt the running command
115 m_state = DSinterrupted;
116 kill(SIGINT);
117 ASSERT(m_activeCmd != 0);
118 TRACE(QString().sprintf("interrupted the command %d",
119 (m_activeCmd ? m_activeCmd->m_cmd : -1)));
120 delete m_activeCmd;
121 m_activeCmd = 0;
123 flushLoPriQueue();
125 // if gdb is idle, send it the command
126 if (m_state == DSidle) {
127 ASSERT(m_activeCmd == 0);
128 writeCommand();
131 return cmdItem;
134 bool CmdQueueItem::IsEqualCmd::operator()(CmdQueueItem* cmd) const
136 return cmd->m_cmd == m_cmd && cmd->m_cmdString == m_str;
139 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
140 QString cmdString, QueueMode mode)
142 // place a new command into the low-priority queue
143 std::list<CmdQueueItem*>::iterator i;
144 CmdQueueItem* cmdItem = 0;
145 switch (mode) {
146 case QMoverrideMoreEqual:
147 case QMoverride:
148 // check whether gdb is currently processing this command
149 if (m_activeCmd != 0 &&
150 m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
152 return m_activeCmd;
154 // check whether there is already the same command in the queue
155 i = find_if(m_lopriCmdQueue.begin(), m_lopriCmdQueue.end(), CmdQueueItem::IsEqualCmd(cmd, cmdString));
156 if (i != m_lopriCmdQueue.end()) {
157 // found one
158 cmdItem = *i;
159 if (mode == QMoverrideMoreEqual) {
160 // All commands are equal, but some are more equal than others...
161 // put this command in front of all others
162 m_lopriCmdQueue.erase(i);
163 m_lopriCmdQueue.push_front(cmdItem);
165 break;
166 } // else none found, so add it
167 // drop through
168 case QMnormal:
169 cmdItem = new CmdQueueItem(cmd, cmdString);
170 m_lopriCmdQueue.push_back(cmdItem);
173 // if gdb is idle, send it the command
174 if (m_state == DSidle) {
175 ASSERT(m_activeCmd == 0);
176 writeCommand();
179 return cmdItem;
182 // dequeue a pending command, make it the active one and send it to gdb
183 void DebuggerDriver::writeCommand()
185 // ASSERT(m_activeCmd == 0);
186 assert(m_activeCmd == 0);
188 // first check the high-priority queue - only if it is empty
189 // use a low-priority command.
190 CmdQueueItem* cmd;
191 DebuggerState newState = DScommandSent;
192 if (!m_hipriCmdQueue.empty()) {
193 cmd = m_hipriCmdQueue.front();
194 m_hipriCmdQueue.pop();
195 } else if (!m_lopriCmdQueue.empty()) {
196 cmd = m_lopriCmdQueue.front();
197 m_lopriCmdQueue.pop_front();
198 newState = DScommandSentLow;
199 } else {
200 // nothing to do
201 m_state = DSidle; /* is necessary if command was interrupted earlier */
202 return;
205 m_activeCmd = cmd;
206 TRACE("in writeCommand: " + cmd->m_cmdString);
208 const char* str = cmd->m_cmdString;
209 writeStdin(const_cast<char*>(str), cmd->m_cmdString.length());
211 // write also to log file
212 if (m_logFile.isOpen()) {
213 m_logFile.writeBlock(str, cmd->m_cmdString.length());
214 m_logFile.flush();
217 m_state = newState;
220 void DebuggerDriver::flushLoPriQueue()
222 while (!m_lopriCmdQueue.empty()) {
223 delete m_lopriCmdQueue.back();
224 m_lopriCmdQueue.pop_back();
228 void DebuggerDriver::flushHiPriQueue()
230 while (!m_hipriCmdQueue.empty()) {
231 delete m_hipriCmdQueue.front();
232 m_hipriCmdQueue.pop();
236 void DebuggerDriver::flushCommands(bool hipriOnly)
238 flushHiPriQueue();
239 if (!hipriOnly) {
240 flushLoPriQueue();
244 void DebuggerDriver::slotCommandRead(KProcess*)
246 TRACE(__PRETTY_FUNCTION__);
248 // there must be an active command which is not yet commited
249 ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
250 ASSERT(m_activeCmd != 0);
251 ASSERT(!m_activeCmd->m_committed);
253 // commit the command
254 m_activeCmd->m_committed = true;
256 // now the debugger is officially working on the command
257 m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
259 // set the flag that reflects whether the program is really running
260 switch (m_activeCmd->m_cmd) {
261 case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
262 emit inferiorRunning();
263 break;
264 default:
265 break;
268 // re-receive delayed output
269 while (!m_delayedOutput.empty()) {
270 QByteArray delayed = m_delayedOutput.front();
271 m_delayedOutput.pop();
272 slotReceiveOutput(0, const_cast<char*>(delayed.data()), delayed.size());
276 void DebuggerDriver::slotReceiveOutput(KProcess*, char* buffer, int buflen)
279 * The debugger should be running (processing a command) at this point.
280 * If it is not, it is still idle because we haven't received the
281 * wroteStdin signal yet, in which case there must be an active command
282 * which is not commited.
284 if (m_state == DScommandSent || m_state == DScommandSentLow) {
285 ASSERT(m_activeCmd != 0);
286 ASSERT(!m_activeCmd->m_committed);
288 * We received output before we got signal wroteStdin. Collect this
289 * output, it will be re-sent by commandRead when it gets the
290 * acknowledgment for the uncommitted command.
292 m_delayedOutput.push(QByteArray().duplicate(buffer, buflen));
293 return;
295 // write to log file (do not log delayed output - it would appear twice)
296 if (m_logFile.isOpen()) {
297 m_logFile.writeBlock(buffer, buflen);
298 m_logFile.flush();
302 * gdb sometimes produces stray output while it's idle. This happens if
303 * it receives a signal, most prominently a SIGCONT after a SIGTSTP:
304 * The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
305 * also continues gdb, which repeats the prompt!
307 if (m_activeCmd == 0 && m_state != DSinterrupted) {
308 // ignore the output
309 TRACE("ignoring stray output: " + QString::fromLatin1(buffer, buflen));
310 return;
312 ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
313 ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
315 // collect output until next prompt string is found
317 // accumulate it
318 if (m_outputLen + buflen >= m_outputAlloc) {
320 * Must enlarge m_output: double it. Note: That particular
321 * sequence of commandes here ensures exception safety.
323 int newSize = m_outputAlloc * 2;
324 char* newBuf = new char[newSize];
325 memcpy(newBuf, m_output, m_outputLen);
326 delete[] m_output;
327 m_output = newBuf;
328 m_outputAlloc = newSize;
330 memcpy(m_output+m_outputLen, buffer, buflen);
331 m_outputLen += buflen;
332 m_output[m_outputLen] = '\0';
334 // check for a prompt
335 int promptStart = findPrompt(m_output, m_outputLen);
336 if (promptStart >= 0)
338 // found prompt!
340 // terminate output before the prompt
341 m_output[promptStart] = '\0';
344 * We've got output for the active command. But if it was
345 * interrupted, ignore it.
347 if (m_state != DSinterrupted) {
349 * m_state shouldn't be DSidle while we are parsing the output
350 * so that all commands produced by parse() go into the queue
351 * instead of being written to gdb immediately.
353 ASSERT(m_state != DSidle);
354 CmdQueueItem* cmd = m_activeCmd;
355 m_activeCmd = 0;
356 commandFinished(cmd);
357 delete cmd;
360 // empty buffer
361 m_outputLen = 0;
362 *m_output = '\0';
363 // also clear delayed output if interrupted
364 if (m_state == DSinterrupted) {
365 m_delayedOutput = std::queue<QByteArray>();
369 * We parsed some output successfully. Unless there's more delayed
370 * output, the debugger must be idle now, so send down the next
371 * command.
373 if (m_delayedOutput.empty()) {
374 if (m_hipriCmdQueue.empty() && m_lopriCmdQueue.empty()) {
375 // no pending commands
376 m_state = DSidle;
377 emit enterIdleState();
378 } else {
379 writeCommand();
385 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
387 if (var == 0)
388 return;
390 std::list<CmdQueueItem*>::iterator i = m_lopriCmdQueue.begin();
391 while (i != m_lopriCmdQueue.end()) {
392 if ((*i)->m_expr != 0 && var->isAncestorEq((*i)->m_expr)) {
393 // this is indeed a critical command; delete it
394 TRACE("removing critical lopri-cmd: " + (*i)->m_cmdString);
395 delete *i;
396 m_lopriCmdQueue.erase(i++);
397 } else
398 ++i;
403 QString DebuggerDriver::editableValue(VarTree* value)
405 // by default, let the user edit what is visible
406 return value->value();
410 StackFrame::~StackFrame()
412 delete var;
416 DbgAddr::DbgAddr(const QString& aa) :
417 a(aa)
419 cleanAddr();
423 * We strip off the leading 0x and any leading zeros.
425 void DbgAddr::cleanAddr()
427 if (a.isEmpty())
428 return;
430 while (a[0] == '0' || a[0] == 'x') {
431 a.remove(0, 1);
435 void DbgAddr::operator=(const QString& aa)
437 a = aa;
438 fnoffs = QString();
439 cleanAddr();
442 /* Re-attach 0x in front of the address */
443 QString DbgAddr::asString() const
445 if (a.isEmpty())
446 return QString();
447 else
448 return "0x" + a;
451 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
453 return QString::compare(a1.a, a2.a) == 0;
456 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
458 if (a1.a.length() > a2.a.length())
459 return true;
460 if (a1.a.length() < a2.a.length())
461 return false;
462 return QString::compare(a1.a, a2.a) > 0;
466 Breakpoint::Breakpoint() :
467 id(0),
468 type(breakpoint),
469 temporary(false),
470 enabled(true),
471 ignoreCount(0),
472 hitCount(0),
473 lineNo(0)
476 #include "dbgdriver.moc"