Add UI hooks for watchpoints.
This gets basic watchpoint support working. Right clicking on a variable and picking Watch now opens a prompt with the inferred address, size and watch type for the user to adjust. Still needs some work to get them to show/be modifiable in the breakpoints tab and to get them to respect architectural restrictions (i.e. on x86 we can realistically only do 2 hardware watchpoints and those are restricted to write watch), at least until we support software emulated watchpoints.
This commit is contained in:
@@ -211,6 +211,7 @@ Application Debugger :
|
||||
TeamWindow.cpp
|
||||
ThreadListView.cpp
|
||||
VariablesView.cpp
|
||||
WatchPromptWindow.cpp
|
||||
|
||||
# user_interface/gui/util
|
||||
ActionMenuItem.cpp
|
||||
|
||||
@@ -53,7 +53,8 @@ enum {
|
||||
MSG_INSPECTOR_WINDOW_CLOSED = 'irwc',
|
||||
MSG_INSPECT_ADDRESS = 'isad',
|
||||
MSG_SHOW_TYPECAST_NODE_PROMPT = 'stnp',
|
||||
MSG_TYPECAST_NODE = 'tyno'
|
||||
MSG_TYPECAST_NODE = 'tyno',
|
||||
MSG_SHOW_WATCH_VARIABLE_PROMPT = 'swvp'
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "TypeComponentPath.h"
|
||||
#include "UserInterface.h"
|
||||
#include "Variable.h"
|
||||
#include "WatchPromptWindow.h"
|
||||
|
||||
|
||||
enum {
|
||||
@@ -247,6 +248,27 @@ TeamWindow::MessageReceived(BMessage* message)
|
||||
break;
|
||||
|
||||
}
|
||||
case MSG_SHOW_WATCH_VARIABLE_PROMPT:
|
||||
{
|
||||
target_addr_t address;
|
||||
uint32 type;
|
||||
int32 length;
|
||||
|
||||
if (message->FindUInt64("address", &address) != B_OK
|
||||
|| message->FindUInt32("type", &type) != B_OK
|
||||
|| message->FindInt32("length", &length) != B_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
WatchPromptWindow* window = WatchPromptWindow::Create(address,
|
||||
type, length, fListener);
|
||||
window->Show();
|
||||
} catch (...) {
|
||||
// TODO: notify user
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_REFS_RECEIVED:
|
||||
{
|
||||
entry_ref locatedPath;
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <debugger.h>
|
||||
|
||||
#include <Looper.h>
|
||||
#include <PopUpMenu.h>
|
||||
#include <ToolTip.h>
|
||||
@@ -1684,6 +1686,22 @@ VariablesView::MessageReceived(BMessage* message)
|
||||
node->NodeChild()->SetNode(valueNode);
|
||||
break;
|
||||
}
|
||||
case MSG_SHOW_WATCH_VARIABLE_PROMPT:
|
||||
{
|
||||
ModelNode* node = reinterpret_cast<ModelNode*>(
|
||||
fVariableTable->SelectionModel()->NodeAt(0));
|
||||
ValueLocation* location = node->NodeChild()->Location();
|
||||
ValuePieceLocation piece = location->PieceAt(0);
|
||||
if (piece.type != VALUE_PIECE_LOCATION_MEMORY)
|
||||
break;
|
||||
|
||||
BMessage looperMessage(*message);
|
||||
looperMessage.AddUInt64("address", piece.address);
|
||||
looperMessage.AddInt32("length", piece.size);
|
||||
looperMessage.AddUInt32("type", B_DATA_READ_WRITE_WATCHPOINT);
|
||||
Looper()->PostMessage(&looperMessage);
|
||||
break;
|
||||
}
|
||||
case MSG_VALUE_NODE_CHANGED:
|
||||
{
|
||||
ValueNodeChild* nodeChild;
|
||||
@@ -1977,34 +1995,45 @@ VariablesView::_GetContextActionsForNode(ModelNode* node,
|
||||
if (location->PieceAt(0).type != VALUE_PIECE_LOCATION_MEMORY)
|
||||
return B_OK;
|
||||
|
||||
BMessage* message = new BMessage(MSG_SHOW_INSPECTOR_WINDOW);
|
||||
if (message == NULL)
|
||||
return B_NO_MEMORY;
|
||||
BMessage* message = NULL;
|
||||
status_t result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW,
|
||||
actions, message);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ObjectDeleter<BMessage> messageDeleter(message);
|
||||
message->AddUInt64("address", location->PieceAt(0).address);
|
||||
|
||||
ActionMenuItem* item = new(std::nothrow) ActionMenuItem("Inspect",
|
||||
message);
|
||||
result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS,
|
||||
MSG_SHOW_TYPECAST_NODE_PROMPT, actions, message);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
result = _AddContextAction("Watch" B_UTF8_ELLIPSIS,
|
||||
MSG_SHOW_WATCH_VARIABLE_PROMPT, actions, message);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VariablesView::_AddContextAction(const char* action, uint32 what,
|
||||
ContextActionList* actions, BMessage*& _message)
|
||||
{
|
||||
_message = new BMessage(what);
|
||||
if (_message == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
ObjectDeleter<BMessage> messageDeleter(_message);
|
||||
|
||||
ActionMenuItem* item = new(std::nothrow) ActionMenuItem(action,
|
||||
_message);
|
||||
if (item == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
messageDeleter.Detach();
|
||||
ObjectDeleter<ActionMenuItem> actionDeleter(item);
|
||||
if (!actions->AddItem(item))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
message = new(std::nothrow)BMessage(MSG_SHOW_TYPECAST_NODE_PROMPT);
|
||||
if (message == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
item = new(std::nothrow) ActionMenuItem("Cast as" B_UTF8_ELLIPSIS,
|
||||
message);
|
||||
if (item == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
messageDeleter.Detach();
|
||||
|
||||
if (!actions->AddItem(item))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ private:
|
||||
void _RequestNodeValue(ModelNode* node);
|
||||
status_t _GetContextActionsForNode(ModelNode* node,
|
||||
ContextActionList* actions);
|
||||
status_t _AddContextAction(const char* action,
|
||||
uint32 what, ContextActionList* actions,
|
||||
BMessage*& _message);
|
||||
void _FinishContextMenu(bool force);
|
||||
void _SaveViewState() const;
|
||||
void _RestoreViewState();
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2012, Rene Gollent, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "WatchPromptWindow.h"
|
||||
|
||||
#include <Alert.h>
|
||||
#include <Button.h>
|
||||
#include <LayoutBuilder.h>
|
||||
#include <Menu.h>
|
||||
#include <MenuField.h>
|
||||
#include <MenuItem.h>
|
||||
#include <String.h>
|
||||
#include <TextControl.h>
|
||||
|
||||
#include <ExpressionParser.h>
|
||||
|
||||
#include "MessageCodes.h"
|
||||
#include "UserInterface.h"
|
||||
#include "Watchpoint.h"
|
||||
|
||||
|
||||
WatchPromptWindow::WatchPromptWindow(target_addr_t address, uint32 type,
|
||||
int32 length, UserInterfaceListener* listener)
|
||||
:
|
||||
BWindow(BRect(), "Edit Watchpoint", B_FLOATING_WINDOW,
|
||||
B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
|
||||
fInitialAddress(address),
|
||||
fInitialType(type),
|
||||
fInitialLength(length),
|
||||
fAddressInput(NULL),
|
||||
fLengthInput(NULL),
|
||||
fTypeField(NULL),
|
||||
fListener(listener)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WatchPromptWindow::~WatchPromptWindow()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WatchPromptWindow*
|
||||
WatchPromptWindow::Create(target_addr_t address, uint32 type, int32 length,
|
||||
UserInterfaceListener* listener)
|
||||
{
|
||||
WatchPromptWindow* self = new WatchPromptWindow(address, type, length,
|
||||
listener);
|
||||
|
||||
try {
|
||||
self->_Init();
|
||||
} catch (...) {
|
||||
delete self;
|
||||
throw;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
WatchPromptWindow::_Init()
|
||||
{
|
||||
BString text;
|
||||
text.SetToFormat("0x%" B_PRIx64, fInitialAddress);
|
||||
fAddressInput = new BTextControl("Address:", text, NULL);
|
||||
|
||||
text.SetToFormat("%" B_PRId32, fInitialLength);
|
||||
fLengthInput = new BTextControl("Length:", text, NULL);
|
||||
|
||||
BMenu* typeMenu = new BMenu("Watch Type");
|
||||
typeMenu->AddItem(new BMenuItem("Read", NULL));
|
||||
typeMenu->AddItem(new BMenuItem("Write", NULL));
|
||||
typeMenu->AddItem(new BMenuItem("Read/Write", NULL));
|
||||
fTypeField = new BMenuField("Type:", typeMenu);
|
||||
BLayoutItem* labelItem = fTypeField->CreateLabelLayoutItem();
|
||||
labelItem->View()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
BLayoutBuilder::Group<>(this, B_VERTICAL)
|
||||
.SetInsets(4.0f, 4.0f, 4.0f, 4.0f)
|
||||
.AddGroup(B_HORIZONTAL, 4.0f)
|
||||
.Add(fAddressInput)
|
||||
.End()
|
||||
.AddGroup(B_HORIZONTAL, 4.0f)
|
||||
.Add(fLengthInput)
|
||||
.Add(labelItem)
|
||||
.Add(fTypeField->CreateMenuBarLayoutItem())
|
||||
.End()
|
||||
.AddGroup(B_HORIZONTAL, 4.0f)
|
||||
.AddGlue()
|
||||
.Add((fWatchButton = new BButton("Set",
|
||||
new BMessage(MSG_SET_WATCHPOINT))))
|
||||
.Add((fCancelButton = new BButton("Cancel",
|
||||
new BMessage(B_QUIT_REQUESTED))))
|
||||
.End();
|
||||
|
||||
fWatchButton->SetTarget(this);
|
||||
fCancelButton->SetTarget(this);
|
||||
|
||||
fTypeField->Menu()->SetLabelFromMarked(true);
|
||||
fTypeField->Menu()->ItemAt(fInitialType)->SetMarked(true);
|
||||
}
|
||||
|
||||
void
|
||||
WatchPromptWindow::Show()
|
||||
{
|
||||
CenterOnScreen();
|
||||
BWindow::Show();
|
||||
}
|
||||
|
||||
void
|
||||
WatchPromptWindow::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case MSG_SET_WATCHPOINT:
|
||||
{
|
||||
target_addr_t address;
|
||||
int32 length;
|
||||
ExpressionParser parser;
|
||||
parser.SetSupportHexInput(true);
|
||||
BString errorMessage;
|
||||
try {
|
||||
address = parser.EvaluateToInt64(fAddressInput->Text());
|
||||
length = (int32)parser.EvaluateToInt64(fLengthInput->Text());
|
||||
} catch(ParseException parseError) {
|
||||
errorMessage.SetToFormat("Failed to parse data: %s",
|
||||
parseError.message.String());
|
||||
} catch(...) {
|
||||
errorMessage.SetToFormat(
|
||||
"Unknown error while parsing address");
|
||||
}
|
||||
|
||||
if (!errorMessage.IsEmpty()) {
|
||||
BAlert* alert = new(std::nothrow) BAlert("Edit Watchpoint",
|
||||
errorMessage.String(), "Close");
|
||||
if (alert != NULL)
|
||||
alert->Go();
|
||||
break;
|
||||
}
|
||||
|
||||
fListener->ClearWatchpointRequested(fInitialAddress);
|
||||
fListener->SetWatchpointRequested(address, fTypeField->Menu()
|
||||
->IndexOf(fTypeField->Menu()->FindMarked()), length, true);
|
||||
|
||||
PostMessage(B_QUIT_REQUESTED);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012, Rene Gollent, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef WATCH_PROMPT_WINDOW_H
|
||||
#define WATCH_PROMPT_WINDOW_H
|
||||
|
||||
|
||||
#include <Window.h>
|
||||
|
||||
#include "types/Types.h"
|
||||
|
||||
|
||||
class BTextControl;
|
||||
class Watchpoint;
|
||||
class BMenuField;
|
||||
class UserInterfaceListener;
|
||||
|
||||
|
||||
class WatchPromptWindow : public BWindow
|
||||
{
|
||||
public:
|
||||
// edit existing watchpoint
|
||||
WatchPromptWindow(target_addr_t address,
|
||||
uint32 type, int32 length,
|
||||
UserInterfaceListener* listener);
|
||||
|
||||
~WatchPromptWindow();
|
||||
|
||||
static WatchPromptWindow* Create(target_addr_t address, uint32 type,
|
||||
int32 length,
|
||||
UserInterfaceListener* listener);
|
||||
// throws
|
||||
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
virtual void Show();
|
||||
|
||||
private:
|
||||
void _Init();
|
||||
|
||||
|
||||
private:
|
||||
target_addr_t fInitialAddress;
|
||||
uint32 fInitialType;
|
||||
int32 fInitialLength;
|
||||
BTextControl* fAddressInput;
|
||||
BTextControl* fLengthInput;
|
||||
BMenuField* fTypeField;
|
||||
UserInterfaceListener* fListener;
|
||||
BButton* fWatchButton;
|
||||
BButton* fCancelButton;
|
||||
};
|
||||
|
||||
#endif // WATCH_PROMPT_WINDOW_H
|
||||
Reference in New Issue
Block a user