|
|
|
/***************************************************************************
|
|
|
|
begin : Tue Oct 5 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 "memviewdlg.h"
|
|
|
|
#include "gdbcontroller.h"
|
|
|
|
#include "gdbcommand.h"
|
|
|
|
|
|
|
|
#include <kbuttonbox.h>
|
|
|
|
#include <klineedit.h>
|
|
|
|
#include <tdeglobalsettings.h>
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kstdguiitem.h>
|
|
|
|
#include <tdeversion.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqmultilineedit.h>
|
|
|
|
#include <tqpushbutton.h>
|
|
|
|
#include <tqvariant.h>
|
|
|
|
#include <tqpopupmenu.h>
|
|
|
|
#include <tqhbox.h>
|
|
|
|
#include <tqtoolbox.h>
|
|
|
|
#include <tqtextedit.h>
|
|
|
|
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
|
|
|
|
#include <khexedit/byteseditinterface.h>
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
// **************************************************************************
|
|
|
|
//
|
|
|
|
// Dialog allows the user to enter
|
|
|
|
// - A starting address
|
|
|
|
// - An ending address
|
|
|
|
//
|
|
|
|
// this can be in the form
|
|
|
|
// functiom/method name
|
|
|
|
// variable address (ie &Var, str)
|
|
|
|
// Memory address 0x8040abc
|
|
|
|
//
|
|
|
|
// When disassembling and you enter a method name without an
|
|
|
|
// ending address then the whole method is disassembled.
|
|
|
|
// No data means disassemble the method we're curently in.(from the
|
|
|
|
// start of the method)
|
|
|
|
//
|
|
|
|
// click ok buton to send the request to gdb
|
|
|
|
// the output is returned (some time later) in the raw data slot
|
|
|
|
// and displayed as is, so it's rather crude, but it works!
|
|
|
|
// **************************************************************************
|
|
|
|
|
|
|
|
namespace GDBDebugger
|
|
|
|
{
|
|
|
|
/** Container for controls that select memory range.
|
|
|
|
|
|
|
|
The memory range selection is embedded into memory view widget,
|
|
|
|
it's not a standalone dialog. However, we want to have easy way
|
|
|
|
to hide/show all controls, so we group them in this class.
|
|
|
|
*/
|
|
|
|
class MemoryRangeSelector : public TQWidget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KLineEdit* startAddressLineEdit;
|
|
|
|
KLineEdit* amountLineEdit;
|
|
|
|
TQPushButton* okButton;
|
|
|
|
TQPushButton* cancelButton;
|
|
|
|
|
|
|
|
MemoryRangeSelector(TQWidget* parent)
|
|
|
|
: TQWidget(parent)
|
|
|
|
{
|
|
|
|
TQVBoxLayout* l = new TQVBoxLayout(this);
|
|
|
|
|
|
|
|
// Grid layout: labels + address field
|
|
|
|
TQGridLayout* gl = new TQGridLayout(l);
|
|
|
|
|
|
|
|
gl->setColSpacing(0, 2);
|
|
|
|
gl->setColSpacing(1, 4);
|
|
|
|
gl->setRowSpacing(1, 2);
|
|
|
|
|
|
|
|
TQLabel* l1 = new TQLabel(i18n("Start"), this);
|
|
|
|
gl->addWidget(l1, 0, 1);
|
|
|
|
|
|
|
|
startAddressLineEdit = new KLineEdit(this);
|
|
|
|
gl->addWidget(startAddressLineEdit, 0, 3);
|
|
|
|
|
|
|
|
TQLabel* l2 = new TQLabel(i18n("Amount"), this);
|
|
|
|
gl->addWidget(l2, 2, 1);
|
|
|
|
|
|
|
|
amountLineEdit = new KLineEdit(this);
|
|
|
|
gl->addWidget(amountLineEdit, 2, 3);
|
|
|
|
|
|
|
|
l->addSpacing(2);
|
|
|
|
|
|
|
|
TQHBoxLayout* hb = new TQHBoxLayout(l);
|
|
|
|
hb->addStretch();
|
|
|
|
|
|
|
|
okButton = new TQPushButton(i18n("OK"), this);
|
|
|
|
hb->addWidget(okButton);
|
|
|
|
|
|
|
|
cancelButton = new TQPushButton(i18n("Cancel"), this);
|
|
|
|
hb->addWidget(cancelButton);
|
|
|
|
|
|
|
|
l->addSpacing(2);
|
|
|
|
|
|
|
|
connect(startAddressLineEdit, TQ_SIGNAL(returnPressed()),
|
|
|
|
okButton, TQ_SLOT(animateClick()));
|
|
|
|
|
|
|
|
connect(amountLineEdit, TQ_SIGNAL(returnPressed()),
|
|
|
|
okButton, TQ_SLOT(animateClick()));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MemoryView::MemoryView(GDBController* controller,
|
|
|
|
TQWidget* parent, const char* name)
|
|
|
|
: TQWidget(parent, name),
|
|
|
|
controller_(controller),
|
|
|
|
// New memory view can be created only when debugger is active,
|
|
|
|
// so don't set s_appNotStarted here.
|
|
|
|
khexedit2_real_widget(0),
|
|
|
|
amount_(0), data_(0),
|
|
|
|
debuggerState_(0)
|
|
|
|
{
|
|
|
|
setCaption(i18n("Memory view"));
|
|
|
|
emit captionChanged(caption());
|
|
|
|
|
|
|
|
initWidget();
|
|
|
|
|
|
|
|
if (isOk())
|
|
|
|
slotEnableOrDisable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::initWidget()
|
|
|
|
{
|
|
|
|
TQVBoxLayout *l = new TQVBoxLayout(this, 0, 0);
|
|
|
|
|
|
|
|
khexedit2_widget = KHE::createBytesEditWidget(this);
|
|
|
|
|
|
|
|
bool ok_ = false;
|
|
|
|
|
|
|
|
if (khexedit2_widget)
|
|
|
|
{
|
|
|
|
TQWidget* real_widget = (TQWidget*)
|
|
|
|
khexedit2_widget->child("BytesEdit");
|
|
|
|
|
|
|
|
if (real_widget)
|
|
|
|
{
|
|
|
|
ok_ = true;
|
|
|
|
|
|
|
|
connect(real_widget, TQ_SIGNAL(bufferChanged(int, int)),
|
|
|
|
this, TQ_SLOT(memoryEdited(int, int)));
|
|
|
|
|
|
|
|
khexedit2_real_widget = real_widget;
|
|
|
|
|
|
|
|
TQVariant resize_style(2); // full size usage.
|
|
|
|
real_widget->setProperty("ResizeStyle", resize_style);
|
|
|
|
|
|
|
|
//TQVariant group(8);
|
|
|
|
//real_widget->setProperty("StartOffset", start);
|
|
|
|
//real_widget->setProperty("NoOfBytesPerLine", group);
|
|
|
|
|
|
|
|
// HACK: use hardcoded constant taht should match
|
|
|
|
// khexedit2
|
|
|
|
// 3 -- binary
|
|
|
|
// 1 -- decimal
|
|
|
|
// 0 -- hex
|
|
|
|
//TQVariant coding(3);
|
|
|
|
//real_widget->setProperty("Coding", coding);
|
|
|
|
|
|
|
|
//TQVariant gap(32);
|
|
|
|
//real_widget->setProperty("BinaryGapWidth", gap);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete khexedit2_widget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ok_) {
|
|
|
|
|
|
|
|
rangeSelector_ = new MemoryRangeSelector(this);
|
|
|
|
l->addWidget(rangeSelector_);
|
|
|
|
|
|
|
|
connect(rangeSelector_->okButton, TQ_SIGNAL(clicked()),
|
|
|
|
this, TQ_SLOT(slotChangeMemoryRange()));
|
|
|
|
|
|
|
|
|
|
|
|
connect(rangeSelector_->cancelButton, TQ_SIGNAL(clicked()),
|
|
|
|
this, TQ_SLOT(slotHideRangeDialog()));
|
|
|
|
|
|
|
|
connect(rangeSelector_->startAddressLineEdit,
|
|
|
|
TQ_SIGNAL(textChanged(const TQString&)),
|
|
|
|
this,
|
|
|
|
TQ_SLOT(slotEnableOrDisable()));
|
|
|
|
|
|
|
|
connect(rangeSelector_->amountLineEdit,
|
|
|
|
TQ_SIGNAL(textChanged(const TQString&)),
|
|
|
|
this,
|
|
|
|
TQ_SLOT(slotEnableOrDisable()));
|
|
|
|
|
|
|
|
l->addWidget(khexedit2_widget);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
TQTextEdit* edit = new TQTextEdit(this);
|
|
|
|
l->addWidget(edit);
|
|
|
|
|
|
|
|
edit->setText(
|
|
|
|
"<h1>Not available</h1>"
|
|
|
|
"<p>Could not open the khexedit2 library. "
|
|
|
|
"Make sure that the KHexEdit package (part of tdeutils) is installed. "
|
|
|
|
"Specifically, check for the following files:"
|
|
|
|
"<ul><li>libkhexeditcommon.so.0.0.0\n"
|
|
|
|
"<li>libkbyteseditwidget.so\n"
|
|
|
|
"<li>kbyteseditwidget.desktop\n"
|
|
|
|
"</ul>");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::debuggerStateChanged(int state)
|
|
|
|
{
|
|
|
|
if (isOk())
|
|
|
|
{
|
|
|
|
debuggerState_ = state;
|
|
|
|
slotEnableOrDisable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MemoryView::slotHideRangeDialog()
|
|
|
|
{
|
|
|
|
rangeSelector_->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::slotChangeMemoryRange()
|
|
|
|
{
|
|
|
|
controller_->addCommand(
|
|
|
|
new ExpressionValueCommand(
|
|
|
|
rangeSelector_->amountLineEdit->text(),
|
|
|
|
this, &MemoryView::sizeComputed));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::sizeComputed(const TQString& size)
|
|
|
|
{
|
|
|
|
controller_->addCommand(
|
|
|
|
new
|
|
|
|
GDBCommand(
|
|
|
|
TQString("-data-read-memory %1 x 1 1 %2")
|
|
|
|
.arg(rangeSelector_->startAddressLineEdit->text())
|
|
|
|
.arg(size).ascii(),
|
|
|
|
this,
|
|
|
|
&MemoryView::memoryRead));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::memoryRead(const GDBMI::ResultRecord& r)
|
|
|
|
{
|
|
|
|
const GDBMI::Value& content = r["memory"][0]["data"];
|
|
|
|
|
|
|
|
amount_ = content.size();
|
|
|
|
|
|
|
|
startAsString_ = rangeSelector_->startAddressLineEdit->text();
|
|
|
|
amountAsString_ = rangeSelector_->amountLineEdit->text();
|
|
|
|
start_ = startAsString_.toUInt(0, 0);
|
|
|
|
|
|
|
|
setCaption(TQString("%1 (%2 bytes)")
|
|
|
|
.arg(startAsString_).arg(amount_));
|
|
|
|
emit captionChanged(caption());
|
|
|
|
|
|
|
|
KHE::BytesEditInterface* bytesEditor
|
|
|
|
= KHE::bytesEditInterface(khexedit2_widget);
|
|
|
|
|
|
|
|
delete[] this->data_;
|
|
|
|
this->data_ = new char[amount_];
|
|
|
|
for(unsigned i = 0; i < content.size(); ++i)
|
|
|
|
{
|
|
|
|
this->data_[i] = content[i].literal().toInt(0, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bytesEditor->setData( this->data_, amount_ );
|
|
|
|
bytesEditor->setReadOnly(false);
|
|
|
|
// Overwrite data, not insert new
|
|
|
|
bytesEditor->setOverwriteMode( true );
|
|
|
|
// Not sure this is needed, but prevent
|
|
|
|
// inserting new data.
|
|
|
|
bytesEditor->setOverwriteOnly( true );
|
|
|
|
|
|
|
|
TQVariant start_v(start_);
|
|
|
|
khexedit2_real_widget->setProperty("FirstLineOffset", start_v);
|
|
|
|
|
|
|
|
//TQVariant bsw(0);
|
|
|
|
//khexedit2_real_widget->setProperty("ByteSpacingWidth", bsw);
|
|
|
|
|
|
|
|
// HACK: use hardcoded constant taht should match
|
|
|
|
// khexedit2
|
|
|
|
// 3 -- binary
|
|
|
|
// 1 -- decimal
|
|
|
|
// 0 -- hex
|
|
|
|
//TQVariant coding(1);
|
|
|
|
//khexedit2_real_widget->setProperty("Coding", coding);
|
|
|
|
|
|
|
|
|
|
|
|
slotHideRangeDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MemoryView::memoryEdited(int start, int end)
|
|
|
|
{
|
|
|
|
for(int i = start; i <= end; ++i)
|
|
|
|
{
|
|
|
|
controller_->addCommand(
|
|
|
|
new GDBCommand(
|
|
|
|
TQString("set *(char*)(%1 + %2) = %3")
|
|
|
|
.arg(start_)
|
|
|
|
.arg(i)
|
|
|
|
.arg(TQString::number(data_[i]))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::contextMenuEvent ( TQContextMenuEvent * e )
|
|
|
|
{
|
|
|
|
if (!isOk())
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQPopupMenu menu;
|
|
|
|
|
|
|
|
bool app_running = !(debuggerState_ & s_appNotStarted);
|
|
|
|
|
|
|
|
int idRange = menu.insertItem(i18n("Change memory range"));
|
|
|
|
// If address selector is show, 'set memory range' can't
|
|
|
|
// do anything more.
|
|
|
|
menu.setItemEnabled(idRange,
|
|
|
|
app_running && !rangeSelector_->isShown());
|
|
|
|
int idReload = menu.insertItem(i18n("Reload"));
|
|
|
|
// If amount is zero, it means there's not data yet, so
|
|
|
|
// reloading does not make sense.
|
|
|
|
menu.setItemEnabled(idReload, app_running && amount_ != 0);
|
|
|
|
int idClose = menu.insertItem(i18n("Close this view"));
|
|
|
|
|
|
|
|
int result = menu.exec(e->globalPos());
|
|
|
|
|
|
|
|
if (result == idRange)
|
|
|
|
{
|
|
|
|
rangeSelector_->startAddressLineEdit->setText(startAsString_);
|
|
|
|
rangeSelector_->amountLineEdit->setText(amountAsString_);
|
|
|
|
|
|
|
|
rangeSelector_->show();
|
|
|
|
rangeSelector_->startAddressLineEdit->setFocus();
|
|
|
|
}
|
|
|
|
if (result == idReload)
|
|
|
|
{
|
|
|
|
// We use numeric start_ and amount_ stored in this,
|
|
|
|
// not textual startAsString_ and amountAsString_,
|
|
|
|
// because program position might have changes and expressions
|
|
|
|
// are no longer valid.
|
|
|
|
controller_->addCommand(
|
|
|
|
new
|
|
|
|
GDBCommand(
|
|
|
|
TQString("-data-read-memory %1 x 1 1 %2")
|
|
|
|
.arg(start_).arg(amount_).ascii(),
|
|
|
|
this,
|
|
|
|
&MemoryView::memoryRead));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == idClose)
|
|
|
|
delete this;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MemoryView::isOk() const
|
|
|
|
{
|
|
|
|
return khexedit2_real_widget;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryView::slotEnableOrDisable()
|
|
|
|
{
|
|
|
|
bool app_started = !(debuggerState_ & s_appNotStarted);
|
|
|
|
|
|
|
|
bool enabled_ = app_started &&
|
|
|
|
!rangeSelector_->startAddressLineEdit->text().isEmpty() &&
|
|
|
|
!rangeSelector_->amountLineEdit->text().isEmpty();
|
|
|
|
|
|
|
|
rangeSelector_->okButton->setEnabled(enabled_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ViewerWidget::ViewerWidget(GDBController* controller,
|
|
|
|
TQWidget* parent,
|
|
|
|
const char* name)
|
|
|
|
: TQWidget(parent, name),
|
|
|
|
controller_(controller)
|
|
|
|
{
|
|
|
|
setIcon(SmallIcon("math_brace"));
|
|
|
|
|
|
|
|
TQVBoxLayout *l = new TQVBoxLayout(this, 0, 0);
|
|
|
|
|
|
|
|
toolBox_ = new TQToolBox(this);
|
|
|
|
l->addWidget(toolBox_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewerWidget::slotAddMemoryView()
|
|
|
|
{
|
|
|
|
// For unclear reasons, this call, that indirectly
|
|
|
|
// does
|
|
|
|
//
|
|
|
|
// mainWindow()->setViewAvailable(this)
|
|
|
|
// mainWindow()->raiseView(this)
|
|
|
|
//
|
|
|
|
// should be done before creating the child widget.
|
|
|
|
// Otherwise, the child widget won't be freely resizable --
|
|
|
|
// there will be not-so-small minimum size.
|
|
|
|
// Problem exists both with KMDI and S/IDEAL.
|
|
|
|
|
|
|
|
setViewShown(true);
|
|
|
|
|
|
|
|
MemoryView* widget = new MemoryView(controller_, this);
|
|
|
|
toolBox_->addItem(widget, widget->caption());
|
|
|
|
toolBox_->setCurrentItem(widget);
|
|
|
|
memoryViews_.push_back(widget);
|
|
|
|
|
|
|
|
connect(widget, TQ_SIGNAL(captionChanged(const TQString&)),
|
|
|
|
this, TQ_SLOT(slotChildCaptionChanged(const TQString&)));
|
|
|
|
|
|
|
|
connect(widget, TQ_SIGNAL(destroyed(TQObject*)),
|
|
|
|
this, TQ_SLOT(slotChildDestroyed(TQObject*)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewerWidget::slotDebuggerState(const TQString&, int state)
|
|
|
|
{
|
|
|
|
for(unsigned i = 0; i < memoryViews_.size(); ++i)
|
|
|
|
{
|
|
|
|
memoryViews_[i]->debuggerStateChanged(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewerWidget::slotChildCaptionChanged(const TQString& caption)
|
|
|
|
{
|
|
|
|
const TQWidget* s = static_cast<const TQWidget*>(sender());
|
|
|
|
TQWidget* ncs = const_cast<TQWidget*>(s);
|
|
|
|
TQString cap = caption;
|
|
|
|
// Prevent intepreting '&' as accelerator specifier.
|
|
|
|
cap.replace("&", "&&");
|
|
|
|
toolBox_->setItemLabel(toolBox_->indexOf(ncs), cap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewerWidget::slotChildDestroyed(TQObject* child)
|
|
|
|
{
|
|
|
|
TQValueVector<MemoryView*>::iterator i, e;
|
|
|
|
for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i)
|
|
|
|
{
|
|
|
|
if (*i == child)
|
|
|
|
{
|
|
|
|
memoryViews_.erase(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toolBox_->count() == 0)
|
|
|
|
setViewShown(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// **************************************************************************
|
|
|
|
// **************************************************************************
|
|
|
|
// **************************************************************************
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "memviewdlg.moc"
|