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.
12 #include <stdlib.h> /* strtol, atoi */
18 DebuggerDriver::DebuggerDriver() :
23 connect(this, SIGNAL(readyReadStandardOutput()), SLOT(slotReceiveOutput()));
24 connect(this, SIGNAL(bytesWritten(qint64
)), SLOT(slotCommandRead()));
25 connect(this, SIGNAL(finished(int, QProcess::ExitStatus
)),
29 DebuggerDriver::~DebuggerDriver()
36 bool DebuggerDriver::startup(QString cmdStr
)
38 // clear command queues
45 // debugger executable
47 cmdStr
= defaultInvocation();
49 QStringList cmd
= cmdStr
.split(' ', QString::SkipEmptyParts
);
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
);
61 setProcessChannelMode(MergedChannels
);
63 if (!waitForStarted(-1))
67 if (!m_logFile
.isOpen() && !m_logFileName
.isEmpty()) {
68 m_logFile
.setFileName(m_logFileName
);
69 m_logFile
.open(QIODevice::WriteOnly
);
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);
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
);
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)));
109 // if gdb is idle, send it the command
110 if (m_state
== DSidle
) {
111 ASSERT(m_activeCmd
== 0);
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;
130 case QMoverrideMoreEqual
:
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
)
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()) {
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
);
150 } // else none found, so add it
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);
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.
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
;
185 m_state
= DSidle
; /* is necessary if command was interrupted earlier */
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();
196 qint64 n
= write(data
, len
);
198 break; // ignore error
203 // write also to log file
204 if (m_logFile
.isOpen()) {
205 m_logFile
.write(str
);
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
)
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();
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
);
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
);
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
) {
308 TRACE("ignoring stray output: " + QString(data
));
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
319 // check for a prompt
320 int promptStart
= findPrompt(m_output
);
321 if (promptStart
>= 0)
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
;
341 commandFinished(cmd
);
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
357 if (m_delayedOutput
.empty()) {
358 if (m_hipriCmdQueue
.empty() && m_lopriCmdQueue
.empty()) {
359 // no pending commands
361 emit
enterIdleState();
369 void DebuggerDriver::dequeueCmdByVar(VarTree
* var
)
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
);
380 m_lopriCmdQueue
.erase(i
++);
387 QString
DebuggerDriver::editableValue(VarTree
* value
)
389 // by default, let the user edit what is visible
390 return value
->value();
394 StackFrame::~StackFrame()
400 DbgAddr::DbgAddr(const QString
& aa
) :
407 * We strip off the leading 0x and any leading zeros.
409 void DbgAddr::cleanAddr()
414 while (a
[0] == '0' || a
[0] == 'x') {
419 void DbgAddr::operator=(const QString
& aa
)
426 /* Re-attach 0x in front of the address */
427 QString
DbgAddr::asString() const
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())
444 if (a1
.a
.length() < a2
.a
.length())
446 return QString::compare(a1
.a
, a2
.a
) > 0;
450 Breakpoint::Breakpoint() :
460 #include "dbgdriver.moc"