diff options
Diffstat (limited to 'languages/cpp/debugger/framestackwidget.cpp')
-rw-r--r-- | languages/cpp/debugger/framestackwidget.cpp | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/languages/cpp/debugger/framestackwidget.cpp b/languages/cpp/debugger/framestackwidget.cpp new file mode 100644 index 00000000..e731a1ee --- /dev/null +++ b/languages/cpp/debugger/framestackwidget.cpp @@ -0,0 +1,645 @@ +/*************************************************************************** + begin : Sun Aug 8 1999 + copyright : (C) 1999 by John Birch + email : jbb@kdevelop.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "framestackwidget.h" +#include "gdbparser.h" +#include "gdbcommand.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kglobalsettings.h> + +#include <qheader.h> +#include <qlistbox.h> +#include <qregexp.h> +#include <qstrlist.h> +#include <qpainter.h> + + +#include <ctype.h> + + +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ + +namespace GDBDebugger +{ + +FramestackWidget::FramestackWidget(GDBController* controller, + QWidget *parent, + const char *name, WFlags f) + : QListView(parent, name, f), + viewedThread_(0), + controller_(controller), + mayUpdate_( false ) +{ + setRootIsDecorated(true); + setSorting(-1); + setSelectionMode(Single); + addColumn(QString::null); // Frame number + addColumn(QString::null); // function name/address + addColumn(QString::null); // source + header()->hide(); + + + // FIXME: maybe, all debugger components should derive from + // a base class that does this connect. + connect(controller, SIGNAL(event(GDBController::event_t)), + this, SLOT(slotEvent(GDBController::event_t))); + + connect( this, SIGNAL(clicked(QListViewItem*)), + this, SLOT(slotSelectionChanged(QListViewItem*)) ); +} + + +/***************************************************************************/ + +FramestackWidget::~FramestackWidget() +{} + +/***************************************************************************/ + +QListViewItem *FramestackWidget::lastChild() const +{ + QListViewItem* child = firstChild(); + if (child) + while (QListViewItem* nextChild = child->nextSibling()) + child = nextChild; + + return child; +} + +// ************************************************************************** + +void FramestackWidget::clear() +{ + viewedThread_ = 0; + + QListView::clear(); +} + +/***************************************************************************/ + +void FramestackWidget::slotSelectionChanged(QListViewItem *thisItem) +{ + ThreadStackItem *thread = dynamic_cast<ThreadStackItem*> (thisItem); + if (thread) + { + controller_->selectFrame(0, thread->threadNo()); + } + else + { + FrameStackItem *frame = dynamic_cast<FrameStackItem*> (thisItem); + if (frame) + { + if (frame->text(0) == "...") + { + // Switch to the target thread. + if (frame->threadNo() != -1) + controller_->addCommand( + new GDBCommand(QString("-thread-select %1") + .arg(frame->threadNo()).ascii())); + + viewedThread_ = findThread(frame->threadNo()); + getBacktrace(frame->frameNo(), frame->frameNo() + frameChunk_); + } + else + { + controller_-> + selectFrame(frame->frameNo(), frame->threadNo()); + } + } + } +} + +/***************************************************************************/ + +void FramestackWidget::slotEvent(GDBController::event_t e) +{ + switch(e) + { + case GDBController::program_state_changed: + + kdDebug(9012) << "Clearning framestack\n"; + clear(); + + if ( isVisible() ) + { + controller_->addCommand( + new GDBCommand("-thread-list-ids", + this, &FramestackWidget::handleThreadList)); + mayUpdate_ = false; + } + else mayUpdate_ = true; + + break; + + + case GDBController::thread_or_frame_changed: + + if (viewedThread_) + { + // For non-threaded programs frame switch is no-op + // as far as framestack is concerned. + // FIXME: but need to highlight the current frame. + + if (ThreadStackItem* item + = findThread(controller_->currentThread())) + { + viewedThread_ = item; + + if (!item->firstChild()) + { + // No backtrace for this thread yet. + getBacktrace(); + } + } + } + + break; + + case GDBController::program_exited: + case GDBController::debugger_exited: + { + clear(); + break; + } + case GDBController::debugger_busy: + case GDBController::debugger_ready: + case GDBController::shared_library_loaded: + case GDBController::program_running: + case GDBController::connected_to_program: + break; + } +} + +void FramestackWidget::showEvent(QShowEvent*) +{ + if (controller_->stateIsOn(s_appRunning|s_dbgBusy|s_dbgNotStarted|s_shuttingDown)) + return; + + if ( mayUpdate_ ) + { + clear(); + + controller_->addCommand( + new GDBCommand( "-thread-list-ids", this, &FramestackWidget::handleThreadList ) ); + + mayUpdate_ = false; + } +} + +void FramestackWidget::getBacktrace(int min_frame, int max_frame) +{ + minFrame_ = min_frame; + maxFrame_ = max_frame; + + controller_->addCommand( + new GDBCommand(QString("-stack-info-depth %1").arg(max_frame+1), + this, + &FramestackWidget::handleStackDepth)); +} + +void FramestackWidget::handleStackDepth(const GDBMI::ResultRecord& r) +{ + int existing_frames = r["depth"].literal().toInt(); + + has_more_frames = (existing_frames > maxFrame_); + + if (existing_frames < maxFrame_) + maxFrame_ = existing_frames; + //add the following command to the front, so noone switches threads in between + controller_->addCommandToFront( + new GDBCommand(QString("-stack-list-frames %1 %2") + .arg(minFrame_).arg(maxFrame_), + this, &FramestackWidget::parseGDBBacktraceList)); +} + +void FramestackWidget::getBacktraceForThread(int threadNo) +{ + unsigned currentThread = controller_->currentThread(); + if (viewedThread_) + { + // Switch to the target thread. + controller_->addCommand( + new GDBCommand(QString("-thread-select %1") + .arg(threadNo).ascii())); + + viewedThread_ = findThread(threadNo); + } + + getBacktrace(); + + if (viewedThread_) + { + // Switch back to the original thread. + controller_->addCommand( + new GDBCommand(QString("-thread-select %1") + .arg(currentThread).ascii())); + } +} + +void FramestackWidget::handleThreadList(const GDBMI::ResultRecord& r) +{ + // Gdb reply is: + // ^done,thread-ids={thread-id="3",thread-id="2",thread-id="1"}, + // which syntactically is a tuple, but one has to access it + // by index anyway. + const GDBMI::TupleValue& ids = + dynamic_cast<const GDBMI::TupleValue&>(r["thread-ids"]); + + if (ids.results.size() > 1) + { + // Need to iterate over all threads to figure out where each one stands. + // Note that this sequence of command will be executed in strict + // sequences, so no other view can add its command in between and + // get state for a wrong thread. + + // Really threaded program. + for(unsigned i = 0, e = ids.results.size(); i != e; ++i) + { + QString id = ids.results[i]->value->literal(); + + controller_->addCommand( + new GDBCommand(QString("-thread-select %1").arg(id).ascii(), + this, &FramestackWidget::handleThread)); + } + + controller_->addCommand( + new GDBCommand(QString("-thread-select %1") + .arg(controller_->currentThread()).ascii())); + } + + // Get backtrace for the current thread. We need to do this + // here, and not in event handler for program_state_changed, + // viewedThread_ is initialized by 'handleThread' before + // backtrace handler is called. + getBacktrace(); +} + +void FramestackWidget::handleThread(const GDBMI::ResultRecord& r) +{ + QString id = r["new-thread-id"].literal(); + int id_num = id.toInt(); + + QString name_column; + QString func_column; + QString args_column; + QString source_column; + + formatFrame(r["frame"], func_column, source_column); + + ThreadStackItem* thread = new ThreadStackItem(this, id_num); + thread->setText(1, func_column); + thread->setText(2, source_column); + + // The thread with a '*' is always the viewedthread + + if (id_num == controller_->currentThread()) + { + viewedThread_ = thread; + setSelected(viewedThread_, true); + } +} + + +void FramestackWidget::parseGDBBacktraceList(const GDBMI::ResultRecord& r) +{ + if (!r.hasField("stack")) + return; + + const GDBMI::Value& frames = r["stack"]; + + if (frames.empty()) + return; + + Q_ASSERT(dynamic_cast<const GDBMI::ListValue*>(&frames)); + + // Remove "..." item, if there's one. + QListViewItem* last; + if (viewedThread_) + { + last = viewedThread_->firstChild(); + if (last) + while(last->nextSibling()) + last = last->nextSibling(); + } + else + { + last = lastItem(); + } + if (last && last->text(0) == "...") + delete last; + + int lastLevel; + for(unsigned i = 0, e = frames.size(); i != e; ++i) + { + const GDBMI::Value& frame = frames[i]; + + // For now, just produce string simular to gdb + // console output. In future we might have a table, + // or something better. + QString frameDesc; + + QString name_column; + QString func_column; + QString source_column; + + QString level_s = frame["level"].literal(); + int level = level_s.toInt(); + + name_column = "#" + level_s; + + formatFrame(frame, func_column, source_column); + + FrameStackItem* item; + if (viewedThread_) + item = new FrameStackItem(viewedThread_, level, name_column); + else + item = new FrameStackItem(this, level, name_column); + lastLevel = level; + + item->setText(1, func_column); + item->setText(2, source_column); + } + if (has_more_frames) + { + QListViewItem* item; + if (viewedThread_) + item = new FrameStackItem(viewedThread_, lastLevel+1, "..."); + else + item = new FrameStackItem(this, lastLevel+1, "..."); + item->setText(1, "(click to get more frames)"); + } + + currentFrame_ = 0; + // Make sure the first frame in the stopped backtrace is selected + // and open + if (viewedThread_) + viewedThread_->setOpen(true); + else + { + if (FrameStackItem* frame = (FrameStackItem*) firstChild()) + { + frame->setOpen(true); + setSelected(frame, true); + } + } +} + +// ************************************************************************** + +ThreadStackItem *FramestackWidget::findThread(int threadNo) +{ + QListViewItem *sibling = firstChild(); + while (sibling) + { + ThreadStackItem *thread = dynamic_cast<ThreadStackItem*> (sibling); + if (thread && thread->threadNo() == threadNo) + { + return thread; + } + sibling = sibling->nextSibling(); + } + + return 0; +} + +// ************************************************************************** + +FrameStackItem *FramestackWidget::findFrame(int frameNo, int threadNo) +{ + QListViewItem* frameItem = 0; + + if (threadNo != -1) + { + ThreadStackItem *thread = findThread(threadNo); + if (thread == 0) + return 0; // no matching thread? + frameItem = thread->firstChild(); + } + else + frameItem = firstChild(); + + while (frameItem) + { + if (((FrameStackItem*)frameItem)->frameNo() == frameNo) + break; + + frameItem = frameItem->nextSibling(); + } + return (FrameStackItem*)frameItem; +} + +void FramestackWidget::formatFrame(const GDBMI::Value& frame, + QString& func_column, + QString& source_column) +{ + func_column = source_column = ""; + + if (frame.hasField("func")) + { + func_column += " " + frame["func"].literal(); + } + else + { + func_column += " " + frame["address"].literal(); + } + + + if (frame.hasField("file")) + { + source_column = frame["file"].literal(); + + if (frame.hasField("line")) + { + source_column += ":" + frame["line"].literal(); + } + } + else if (frame.hasField("from")) + { + source_column = frame["from"].literal(); + } +} + + +void FramestackWidget::drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ) +{ + QListView::drawContentsOffset(p, ox, oy, cx, cy, cw, ch); + + int s1_x = header()->sectionPos(1); + int s1_w = header()->sectionSize(1); + + QRect section1(s1_x, contentsHeight(), s1_w, viewport()->height()); + + p->fillRect(section1, KGlobalSettings::alternateBackgroundColor()); +} + +// ************************************************************************** +// ************************************************************************** +// ************************************************************************** + +FrameStackItem::FrameStackItem(FramestackWidget *parent, + unsigned frameNo, + const QString &name) + : QListViewItem(parent, parent->lastChild()), + frameNo_(frameNo), + threadNo_(-1) +{ + setText(0, name); +} + +// ************************************************************************** + +FrameStackItem::FrameStackItem(ThreadStackItem *parent, + unsigned frameNo, + const QString &name) + : QListViewItem(parent, parent->lastChild()), + frameNo_(frameNo), + threadNo_(parent->threadNo()) +{ + setText(0, name); +} + +// ************************************************************************** + +FrameStackItem::~FrameStackItem() +{} + +// ************************************************************************** + +QListViewItem *FrameStackItem::lastChild() const +{ + QListViewItem* child = firstChild(); + if (child) + while (QListViewItem* nextChild = child->nextSibling()) + child = nextChild; + + return child; +} + +// ************************************************************************** + +void FrameStackItem::setOpen(bool open) +{ +#if 0 + if (open) + { + FramestackWidget* owner = (FramestackWidget*)listView(); + if (this->threadNo() != owner->viewedThread() && + this->frameNo() != owner->currentFrame_) + { + ((FramestackWidget*)listView())->slotSelectFrame(0, threadNo()); + } + } +#endif + QListViewItem::setOpen(open); +} + +// ************************************************************************** +// ************************************************************************** +// ************************************************************************** + +ThreadStackItem::ThreadStackItem(FramestackWidget *parent, unsigned threadNo) +: QListViewItem(parent), + threadNo_(threadNo) +{ + setText(0, i18n("Thread %1").arg(threadNo_)); + setExpandable(true); +} + +// ************************************************************************** + +ThreadStackItem::~ThreadStackItem() +{} + +// ************************************************************************** + +QListViewItem *ThreadStackItem::lastChild() const +{ + QListViewItem* child = firstChild(); + if (child) + while (QListViewItem* nextChild = child->nextSibling()) + child = nextChild; + + return child; +} + +// ************************************************************************** + +void ThreadStackItem::setOpen(bool open) +{ + // If we're openining, and have no child yet, get backtrace from + // gdb. + if (open && !firstChild()) + { + // Not that this will not switch to another thread (and won't show + // position in that other thread). This will only get the frames. + + // Imagine you have 20 frames and you want to find one blocked on + // mutex. You don't want a new source file to be opened for each + // thread you open to find if that's the one you want to debug. + ((FramestackWidget*)listView())->getBacktraceForThread(threadNo()); + } + + if (open) + { + savedFunc_ = text(1); + setText(1, ""); + savedSource_ = text(2); + setText(2, ""); + } + else + { + setText(1, savedFunc_); + setText(2, savedSource_); + } + + QListViewItem::setOpen(open); +} + +void FrameStackItem::paintCell(QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + QColorGroup cg2(cg); + if (column % 2) + { + cg2.setColor(QColorGroup::Base, + KGlobalSettings::alternateBackgroundColor()); + } + QListViewItem::paintCell(p, cg2, column, width, align); +} + +void ThreadStackItem::paintCell(QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + QColorGroup cg2(cg); + if (column % 2) + { + cg2.setColor(QColorGroup::Base, + KGlobalSettings::alternateBackgroundColor()); + } + QListViewItem::paintCell(p, cg2, column, width, align); +} + + +} + +/***************************************************************************/ +/***************************************************************************/ + +#include "framestackwidget.moc" |