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
|
TeamWindow.cpp
|
||||||
ThreadListView.cpp
|
ThreadListView.cpp
|
||||||
VariablesView.cpp
|
VariablesView.cpp
|
||||||
|
WatchPromptWindow.cpp
|
||||||
|
|
||||||
# user_interface/gui/util
|
# user_interface/gui/util
|
||||||
ActionMenuItem.cpp
|
ActionMenuItem.cpp
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ enum {
|
|||||||
MSG_INSPECTOR_WINDOW_CLOSED = 'irwc',
|
MSG_INSPECTOR_WINDOW_CLOSED = 'irwc',
|
||||||
MSG_INSPECT_ADDRESS = 'isad',
|
MSG_INSPECT_ADDRESS = 'isad',
|
||||||
MSG_SHOW_TYPECAST_NODE_PROMPT = 'stnp',
|
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 "TypeComponentPath.h"
|
||||||
#include "UserInterface.h"
|
#include "UserInterface.h"
|
||||||
#include "Variable.h"
|
#include "Variable.h"
|
||||||
|
#include "WatchPromptWindow.h"
|
||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -247,6 +248,27 @@ TeamWindow::MessageReceived(BMessage* message)
|
|||||||
break;
|
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:
|
case B_REFS_RECEIVED:
|
||||||
{
|
{
|
||||||
entry_ref locatedPath;
|
entry_ref locatedPath;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
|
#include <debugger.h>
|
||||||
|
|
||||||
#include <Looper.h>
|
#include <Looper.h>
|
||||||
#include <PopUpMenu.h>
|
#include <PopUpMenu.h>
|
||||||
#include <ToolTip.h>
|
#include <ToolTip.h>
|
||||||
@@ -1684,6 +1686,22 @@ VariablesView::MessageReceived(BMessage* message)
|
|||||||
node->NodeChild()->SetNode(valueNode);
|
node->NodeChild()->SetNode(valueNode);
|
||||||
break;
|
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:
|
case MSG_VALUE_NODE_CHANGED:
|
||||||
{
|
{
|
||||||
ValueNodeChild* nodeChild;
|
ValueNodeChild* nodeChild;
|
||||||
@@ -1977,34 +1995,45 @@ VariablesView::_GetContextActionsForNode(ModelNode* node,
|
|||||||
if (location->PieceAt(0).type != VALUE_PIECE_LOCATION_MEMORY)
|
if (location->PieceAt(0).type != VALUE_PIECE_LOCATION_MEMORY)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
BMessage* message = new BMessage(MSG_SHOW_INSPECTOR_WINDOW);
|
BMessage* message = NULL;
|
||||||
if (message == NULL)
|
status_t result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW,
|
||||||
return B_NO_MEMORY;
|
actions, message);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
ObjectDeleter<BMessage> messageDeleter(message);
|
|
||||||
message->AddUInt64("address", location->PieceAt(0).address);
|
message->AddUInt64("address", location->PieceAt(0).address);
|
||||||
|
|
||||||
ActionMenuItem* item = new(std::nothrow) ActionMenuItem("Inspect",
|
result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS,
|
||||||
message);
|
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)
|
if (item == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
messageDeleter.Detach();
|
messageDeleter.Detach();
|
||||||
ObjectDeleter<ActionMenuItem> actionDeleter(item);
|
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))
|
if (!actions->AddItem(item))
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ private:
|
|||||||
void _RequestNodeValue(ModelNode* node);
|
void _RequestNodeValue(ModelNode* node);
|
||||||
status_t _GetContextActionsForNode(ModelNode* node,
|
status_t _GetContextActionsForNode(ModelNode* node,
|
||||||
ContextActionList* actions);
|
ContextActionList* actions);
|
||||||
|
status_t _AddContextAction(const char* action,
|
||||||
|
uint32 what, ContextActionList* actions,
|
||||||
|
BMessage*& _message);
|
||||||
void _FinishContextMenu(bool force);
|
void _FinishContextMenu(bool force);
|
||||||
void _SaveViewState() const;
|
void _SaveViewState() const;
|
||||||
void _RestoreViewState();
|
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