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  */
6 
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>
16 
17 
DebuggerDriver()18 DebuggerDriver::DebuggerDriver() :
19 	m_state(DSidle),
20 	m_activeCmd(0)
21 {
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()));
27 }
28 
~DebuggerDriver()29 DebuggerDriver::~DebuggerDriver()
30 {
31     flushHiPriQueue();
32     flushLoPriQueue();
33 }
34 
35 
startup(QString cmdStr)36 bool DebuggerDriver::startup(QString cmdStr)
37 {
38     // clear command queues
39     delete m_activeCmd;
40     m_activeCmd = 0;
41     flushHiPriQueue();
42     flushLoPriQueue();
43     m_state = DSidle;
44 
45     // debugger executable
46     if (cmdStr.isEmpty())
47 	cmdStr = defaultInvocation();
48 
49     QStringList cmd = cmdStr.split(' ', QString::SkipEmptyParts);
50     if (cmd.isEmpty())
51 	return false;
52     QString pgm = cmd.takeFirst();
53 
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
60 
61     setProcessChannelMode(MergedChannels);
62     start(pgm, cmd);
63     if (!waitForStarted(-1))
64 	return false;
65 
66     // open log file
67     if (!m_logFile.isOpen() && !m_logFileName.isEmpty()) {
68 	m_logFile.setFileName(m_logFileName);
69 	m_logFile.open(QIODevice::WriteOnly);
70     }
71 
72     return true;
73 }
74 
slotExited()75 void DebuggerDriver::slotExited()
76 {
77     static const char txt[] = "\n====== debugger exited ======\n";
78     if (m_logFile.isOpen()) {
79 	m_logFile.write(txt,sizeof(txt)-1);
80     }
81 
82     // reset state
83     m_state = DSidle;
84     // empty buffer
85     m_output.clear();
86 }
87 
88 
executeCmdString(DbgCommand cmd,QString cmdString,bool clearLow)89 CmdQueueItem* DebuggerDriver::executeCmdString(DbgCommand cmd,
90 					       QString cmdString, bool clearLow)
91 {
92     // place a new command into the high-priority queue
93     CmdQueueItem* cmdItem = new CmdQueueItem(cmd, cmdString);
94     m_hipriCmdQueue.push(cmdItem);
95 
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;
106 	}
107 	flushLoPriQueue();
108     }
109     // if gdb is idle, send it the command
110     if (m_state == DSidle) {
111 	ASSERT(m_activeCmd == 0);
112 	writeCommand();
113     }
114 
115     return cmdItem;
116 }
117 
operator ()(CmdQueueItem * cmd) const118 bool CmdQueueItem::IsEqualCmd::operator()(CmdQueueItem* cmd) const
119 {
120     return cmd->m_cmd == m_cmd && cmd->m_cmdString == m_str;
121 }
122 
queueCmdString(DbgCommand cmd,QString cmdString,QueueMode mode)123 CmdQueueItem* DebuggerDriver::queueCmdString(DbgCommand cmd,
124 					     QString cmdString, QueueMode mode)
125 {
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)
135 	{
136 	    return m_activeCmd;
137 	}
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);
148 	    }
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);
155     }
156 
157     // if gdb is idle, send it the command
158     if (m_state == DSidle) {
159 	ASSERT(m_activeCmd == 0);
160 	writeCommand();
161     }
162 
163     return cmdItem;
164 }
165 
166 // dequeue a pending command, make it the active one and send it to gdb
writeCommand()167 void DebuggerDriver::writeCommand()
168 {
169 //    ASSERT(m_activeCmd == 0);
170     assert(m_activeCmd == 0);
171 
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;
187     }
188 
189     m_activeCmd = cmd;
190     TRACE("in writeCommand: " + cmd->m_cmdString);
191 
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;
201     }
202 
203     // write also to log file
204     if (m_logFile.isOpen()) {
205 	m_logFile.write(str);
206 	m_logFile.flush();
207     }
208 
209     m_state = newState;
210 }
211 
flushLoPriQueue()212 void DebuggerDriver::flushLoPriQueue()
213 {
214     while (!m_lopriCmdQueue.empty()) {
215 	delete m_lopriCmdQueue.back();
216 	m_lopriCmdQueue.pop_back();
217     }
218 }
219 
flushHiPriQueue()220 void DebuggerDriver::flushHiPriQueue()
221 {
222     while (!m_hipriCmdQueue.empty()) {
223 	delete m_hipriCmdQueue.front();
224 	m_hipriCmdQueue.pop();
225     }
226 }
227 
flushCommands(bool hipriOnly)228 void DebuggerDriver::flushCommands(bool hipriOnly)
229 {
230     flushHiPriQueue();
231     if (!hipriOnly) {
232 	flushLoPriQueue();
233     }
234 }
235 
slotCommandRead()236 void DebuggerDriver::slotCommandRead()
237 {
238     TRACE(__PRETTY_FUNCTION__);
239 
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);
244 
245     // commit the command
246     m_activeCmd->m_committed = true;
247 
248     // now the debugger is officially working on the command
249     m_state = m_state == DScommandSent ? DSrunning : DSrunningLow;
250 
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;
258     }
259 
260     // process delayed output
261     while (!m_delayedOutput.empty()) {
262 	QByteArray delayed = m_delayedOutput.front();
263 	m_delayedOutput.pop();
264 	processOutput(delayed);
265     }
266 }
267 
slotReceiveOutput()268 void DebuggerDriver::slotReceiveOutput()
269 {
270     QByteArray data = readAllStandardOutput();
271 
272     /*
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.
277      */
278     if (m_state == DScommandSent || m_state == DScommandSentLow) {
279 	ASSERT(m_activeCmd != 0);
280 	ASSERT(!m_activeCmd->m_committed);
281 	/*
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.
285 	 */
286 	m_delayedOutput.push(data);
287 	return;
288     }
289     processOutput(data);
290 }
291 
processOutput(const QByteArray & data)292 void DebuggerDriver::processOutput(const QByteArray& data)
293 {
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();
298     }
299 
300     /*
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!
305      */
306     if (m_activeCmd == 0 && m_state != DSinterrupted) {
307 	// ignore the output
308 	TRACE("ignoring stray output: " + QString(data));
309 	return;
310     }
311     ASSERT(m_state == DSrunning || m_state == DSrunningLow || m_state == DSinterrupted);
312     ASSERT(m_activeCmd != 0 || m_state == DSinterrupted);
313 
314     // collect output until next prompt string is found
315 
316     // accumulate it
317     m_output += data;
318 
319     // check for a prompt
320     int promptStart = findPrompt(m_output);
321     if (promptStart >= 0)
322     {
323 	// found prompt!
324 
325 	// terminate output before the prompt
326 	m_output.resize(promptStart);
327 
328 	/*
329 	 * We've got output for the active command. But if it was
330 	 * interrupted, ignore it.
331 	 */
332 	if (m_state != DSinterrupted) {
333 	    /*
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.
337 	     */
338 	    ASSERT(m_state != DSidle);
339 	    CmdQueueItem* cmd = m_activeCmd;
340 	    m_activeCmd = 0;
341 	    commandFinished(cmd);
342 	    delete cmd;
343 	}
344 
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>();
350 	}
351 
352 	/*
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.
356 	 */
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();
364 	    }
365 	}
366     }
367 }
368 
dequeueCmdByVar(VarTree * var)369 void DebuggerDriver::dequeueCmdByVar(VarTree* var)
370 {
371     if (var == 0)
372 	return;
373 
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;
383     }
384 }
385 
386 
editableValue(VarTree * value)387 QString DebuggerDriver::editableValue(VarTree* value)
388 {
389     // by default, let the user edit what is visible
390     return value->value();
391 }
392 
393 
~StackFrame()394 StackFrame::~StackFrame()
395 {
396     delete var;
397 }
398 
399 
DbgAddr(const QString & aa)400 DbgAddr::DbgAddr(const QString& aa) :
401 	a(aa)
402 {
403     cleanAddr();
404 }
405 
406 /*
407  * We strip off the leading 0x and any leading zeros.
408  */
cleanAddr()409 void DbgAddr::cleanAddr()
410 {
411     if (a.isEmpty())
412 	return;
413 
414     while (a[0] == '0' || a[0] == 'x') {
415 	a.remove(0, 1);
416     }
417 }
418 
operator =(const QString & aa)419 void DbgAddr::operator=(const QString& aa)
420 {
421     a = aa;
422     fnoffs = QString();
423     cleanAddr();
424 }
425 
426 /* Re-attach 0x in front of the address */
asString() const427 QString DbgAddr::asString() const
428 {
429     if (a.isEmpty())
430 	return QString();
431     else
432 	return "0x" + a;
433 }
434 
operator ==(const DbgAddr & a1,const DbgAddr & a2)435 bool operator==(const DbgAddr& a1, const DbgAddr& a2)
436 {
437     return QString::compare(a1.a, a2.a) == 0;
438 }
439 
operator >(const DbgAddr & a1,const DbgAddr & a2)440 bool operator>(const DbgAddr& a1, const DbgAddr& a2)
441 {
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;
447 }
448 
449 
Breakpoint()450 Breakpoint::Breakpoint() :
451 	id(0),
452 	type(breakpoint),
453 	temporary(false),
454 	enabled(true),
455 	ignoreCount(0),
456 	hitCount(0),
457 	lineNo(0)
458 { }
459 
460 #include "dbgdriver.moc"
461