Extend copyright to 2018.
[kdbg.git] / kdbg / dbgdriver.cpp
bloba615aef28f679ffbdd38d992afd17d5163a6d80b
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>
10 #include <ctype.h>
11 #include <signal.h>
12 #include <stdlib.h> /* strtol, atoi */
13 #include <algorithm>
14 #include "mydebug.h"
15 #include <assert.h>
18 DebuggerDriver::DebuggerDriver() :
19 m_state(DSidle),
20 m_activeCmd(0)
22 // debugger process
23 connect(this, SIGNAL(readyReadStandardOutput()), SLOT(slotReceiveOutput()));
24 connect(this, SIGNAL(bytesWritten(qint64)), SLOT(slotCommandRead()));
25 connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
26 SLOT(slotExited()));
29 DebuggerDriver::~DebuggerDriver()
31 flushHiPriQueue();
32 flushLoPriQueue();
36 bool DebuggerDriver::startup(QString cmdStr)
38 // clear command queues
39 delete m_activeCmd;
40 m_activeCmd = 0;
41 flushHiPriQueue();
42 flushLoPriQueue();
43 m_state = DSidle;
45 // debugger executable
46 if (cmdStr.isEmpty())
47 cmdStr = defaultInvocation();
49 QStringList cmd = cmdStr.split(' ', QString::SkipEmptyParts);
50 if (cmd.isEmpty())
51 return false;
52 QString pgm = cmd.takeFirst();
54 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
55 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
56 env.insert(QLatin1String("LC_ALL"), QLatin1String("C"));
57 env.remove(QLatin1String("LANG"));
58 setProcessEnvironment(env);
59 #endif
61 setProcessChannelMode(MergedChannels);
62 start(pgm, cmd);
63 if (!waitForStarted(-1))
64 return false;
66 // open log file
67 if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
68 m_logFile.setFileName(m_logFileName);
69 m_logFile.open(QIODevice::WriteOnly);
72 return true;
75 void DebuggerDriver::slotExited()
77 static const char txt[] = "\n====== debugger exited ======\n";
78 if (m_logFile.isOpen()) {
79 m_logFile.write(txt,sizeof(txt)-1);
82 // reset state
83 m_state = DSidle;
84 // empty buffer
85 m_output.clear();
89 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
90 QString cmdString, bool clearLow)
92 // place a new command into the high-priority queue
93 CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
94 m_hipriCmdQueue.push(cmdItem);
96 if (clearLow) {
97 if (m_state == DSrunningLow) {
98 // take the liberty to interrupt the running command
99 m_state = DSinterrupted;
100 ::kill(pid(), SIGINT);
101 ASSERT(m_activeCmd != 0);
102 TRACE(QString().sprintf("interrupted the command %d",
103 (m_activeCmd ? m_activeCmd->m_cmd : -1)));
104 delete m_activeCmd;
105 m_activeCmd = 0;
107 flushLoPriQueue();
109 // if gdb is idle, send it the command
110 if (m_state == DSidle) {
111 ASSERT(m_activeCmd == 0);
112 writeCommand();
115 return cmdItem;
118 bool CmdQueueItem::IsEqualCmd::operator()(CmdQueueItem* cmd) const
120 return cmd->m_cmd == m_cmd && cmd->m_cmdString == m_str;
123 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
124 QString cmdString, QueueMode mode)
126 // place a new command into the low-priority queue
127 std::list<CmdQueueItem*>::iterator i;
128 CmdQueueItem* cmdItem = 0;
129 switch (mode) {
130 case QMoverrideMoreEqual:
131 case QMoverride:
132 // check whether gdb is currently processing this command
133 if (m_activeCmd != 0 &&
134 m_activeCmd->m_cmd == cmd && m_activeCmd->m_cmdString == cmdString)
136 return m_activeCmd;
138 // check whether there is already the same command in the queue
139 i = find_if(m_lopriCmdQueue.begin(), m_lopriCmdQueue.end(), CmdQueueItem::IsEqualCmd(cmd, cmdString));
140 if (i != m_lopriCmdQueue.end()) {
141 // found one
142 cmdItem = *i;
143 if (mode == QMoverrideMoreEqual) {
144 // All commands are equal, but some are more equal than others...
145 // put this command in front of all others
146 m_lopriCmdQueue.erase(i);
147 m_lopriCmdQueue.push_front(cmdItem);
149 break;
150 } // else none found, so add it
151 // drop through
152 case QMnormal:
153 cmdItem = new CmdQueueItem(cmd, cmdString);
154 m_lopriCmdQueue.push_back(cmdItem);
157 // if gdb is idle, send it the command
158 if (m_state == DSidle) {
159 ASSERT(m_activeCmd == 0);
160 writeCommand();
163 return cmdItem;
166 // dequeue a pending command, make it the active one and send it to gdb
167 void DebuggerDriver::writeCommand()
169 // ASSERT(m_activeCmd == 0);
170 assert(m_activeCmd == 0);
172 // first check the high-priority queue - only if it is empty
173 // use a low-priority command.
174 CmdQueueItem* cmd;
175 DebuggerState newState = DScommandSent;
176 if (!m_hipriCmdQueue.empty()) {
177 cmd = m_hipriCmdQueue.front();
178 m_hipriCmdQueue.pop();
179 } else if (!m_lopriCmdQueue.empty()) {
180 cmd = m_lopriCmdQueue.front();
181 m_lopriCmdQueue.pop_front();
182 newState = DScommandSentLow;
183 } else {
184 // nothing to do
185 m_state = DSidle; /* is necessary if command was interrupted earlier */
186 return;
189 m_activeCmd = cmd;
190 TRACE("in writeCommand: " + cmd->m_cmdString);
192 QByteArray str = cmd->m_cmdString.toLocal8Bit();
193 const char* data = str.data();
194 qint64 len = str.length();
195 while (len > 0) {
196 qint64 n = write(data, len);
197 if (n <= 0)
198 break; // ignore error
199 len -= n;
200 data += n;
203 // write also to log file
204 if (m_logFile.isOpen()) {
205 m_logFile.write(str);
206 m_logFile.flush();
209 m_state = newState;
212 void DebuggerDriver::flushLoPriQueue()
214 while (!m_lopriCmdQueue.empty()) {
215 delete m_lopriCmdQueue.back();
216 m_lopriCmdQueue.pop_back();
220 void DebuggerDriver::flushHiPriQueue()
222 while (!m_hipriCmdQueue.empty()) {
223 delete m_hipriCmdQueue.front();
224 m_hipriCmdQueue.pop();
228 void DebuggerDriver::flushCommands(bool hipriOnly)
230 flushHiPriQueue();
231 if (!hipriOnly) {
232 flushLoPriQueue();
236 void DebuggerDriver::slotCommandRead()
238 TRACE(__PRETTY_FUNCTION__);
240 // there must be an active command which is not yet commited
241 ASSERT(m_state == DScommandSent || m_state == DScommandSentLow);
242 ASSERT(m_activeCmd != 0);
243 ASSERT(!m_activeCmd->m_committed);
245 // commit the command
246 m_activeCmd->m_committed = true;
248 // now the debugger is officially working on the command
249 m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
251 // set the flag that reflects whether the program is really running
252 switch (m_activeCmd->m_cmd) {
253 case DCrun: case DCcont: case DCnext: case DCstep: case DCfinish: case DCuntil:
254 emit inferiorRunning();
255 break;
256 default:
257 break;
260 // process delayed output
261 while (!m_delayedOutput.empty()) {
262 QByteArray delayed = m_delayedOutput.front();
263 m_delayedOutput.pop();
264 processOutput(delayed);
268 void DebuggerDriver::slotReceiveOutput()
270 QByteArray data = readAllStandardOutput();
273 * The debugger should be running (processing a command) at this point.
274 * If it is not, it is still idle because we haven't received the
275 * bytesWritten signal yet, in which case there must be an active command
276 * which is not commited.
278 if (m_state == DScommandSent || m_state == DScommandSentLow) {
279 ASSERT(m_activeCmd != 0);
280 ASSERT(!m_activeCmd->m_committed);
282 * We received output before we got signal bytesWritten. Collect this
283 * output, it will be processed by commandRead when it gets the
284 * acknowledgment for the uncommitted command.
286 m_delayedOutput.push(data);
287 return;
289 processOutput(data);
292 void DebuggerDriver::processOutput(const QByteArray& data)
294 // write to log file (do not log delayed output - it would appear twice)
295 if (m_logFile.isOpen()) {
296 m_logFile.write(data);
297 m_logFile.flush();
301 * gdb sometimes produces stray output while it's idle. This happens if
302 * it receives a signal, most prominently a SIGCONT after a SIGTSTP:
303 * The user haltet kdbg with Ctrl-Z, then continues it with "fg", which
304 * also continues gdb, which repeats the prompt!
306 if (m_activeCmd == 0 && m_state != DSinterrupted) {
307 // ignore the output
308 TRACE("ignoring stray output: " + QString(data));
309 return;
311 ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
312 ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
314 // collect output until next prompt string is found
316 // accumulate it
317 m_output += data;
319 // check for a prompt
320 int promptStart = findPrompt(m_output);
321 if (promptStart >= 0)
323 // found prompt!
325 // terminate output before the prompt
326 m_output.resize(promptStart);
329 * We've got output for the active command. But if it was
330 * interrupted, ignore it.
332 if (m_state != DSinterrupted) {
334 * m_state shouldn't be DSidle while we are parsing the output
335 * so that all commands produced by parse() go into the queue
336 * instead of being written to gdb immediately.
338 ASSERT(m_state != DSidle);
339 CmdQueueItem* cmd = m_activeCmd;
340 m_activeCmd = 0;
341 commandFinished(cmd);
342 delete cmd;
345 // empty buffer
346 m_output.clear();
347 // also clear delayed output if interrupted
348 if (m_state == DSinterrupted) {
349 m_delayedOutput = std::queue<QByteArray>();
353 * We parsed some output successfully. Unless there's more delayed
354 * output, the debugger must be idle now, so send down the next
355 * command.
357 if (m_delayedOutput.empty()) {
358 if (m_hipriCmdQueue.empty() && m_lopriCmdQueue.empty()) {
359 // no pending commands
360 m_state = DSidle;
361 emit enterIdleState();
362 } else {
363 writeCommand();
369 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
371 if (var == 0)
372 return;
374 std::list<CmdQueueItem*>::iterator i = m_lopriCmdQueue.begin();
375 while (i != m_lopriCmdQueue.end()) {
376 if ((*i)->m_expr != 0 && var->isAncestorEq((*i)->m_expr)) {
377 // this is indeed a critical command; delete it
378 TRACE("removing critical lopri-cmd: " + (*i)->m_cmdString);
379 delete *i;
380 m_lopriCmdQueue.erase(i++);
381 } else
382 ++i;
387 QString DebuggerDriver::editableValue(VarTree* value)
389 // by default, let the user edit what is visible
390 return value->value();
394 StackFrame::~StackFrame()
396 delete var;
400 DbgAddr::DbgAddr(const QString& aa) :
401 a(aa)
403 cleanAddr();
407 * We strip off the leading 0x and any leading zeros.
409 void DbgAddr::cleanAddr()
411 if (a.isEmpty())
412 return;
414 while (a[0] == '0' || a[0] == 'x') {
415 a.remove(0, 1);
419 void DbgAddr::operator=(const QString& aa)
421 a = aa;
422 fnoffs = QString();
423 cleanAddr();
426 /* Re-attach 0x in front of the address */
427 QString DbgAddr::asString() const
429 if (a.isEmpty())
430 return QString();
431 else
432 return "0x" + a;
435 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
437 return QString::compare(a1.a, a2.a) == 0;
440 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
442 if (a1.a.length() > a2.a.length())
443 return true;
444 if (a1.a.length() < a2.a.length())
445 return false;
446 return QString::compare(a1.a, a2.a) > 0;
450 Breakpoint::Breakpoint() :
451 id(0),
452 type(breakpoint),
453 temporary(false),
454 enabled(true),
455 ignoreCount(0),
456 hitCount(0),
457 lineNo(0)
460 #include "dbgdriver.moc"