1/*
2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5#include "WatchPromptWindow.h"
6
7#include <Alert.h>
8#include <Button.h>
9#include <LayoutBuilder.h>
10#include <Menu.h>
11#include <MenuField.h>
12#include <MenuItem.h>
13#include <String.h>
14#include <TextControl.h>
15
16#include "AutoLocker.h"
17
18#include "AppMessageCodes.h"
19#include "Architecture.h"
20#include "CppLanguage.h"
21#include "IntegerValue.h"
22#include "MessageCodes.h"
23#include "SyntheticPrimitiveType.h"
24#include "UserInterface.h"
25#include "Watchpoint.h"
26
27
28WatchPromptWindow::WatchPromptWindow(Architecture* architecture,
29	target_addr_t address, uint32 type, int32 length,
30	UserInterfaceListener* listener)
31	:
32	BWindow(BRect(), "Edit Watchpoint", B_FLOATING_WINDOW,
33		B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
34	fInitialAddress(address),
35	fInitialType(type),
36	fInitialLength(length),
37	fArchitecture(architecture),
38	fRequestedAddress(0),
39	fRequestedLength(0),
40	fAddressInput(NULL),
41	fLengthInput(NULL),
42	fAddressExpressionInfo(NULL),
43	fLengthExpressionInfo(NULL),
44	fTypeField(NULL),
45	fListener(listener),
46	fLanguage(NULL)
47{
48	fArchitecture->AcquireReference();
49}
50
51
52WatchPromptWindow::~WatchPromptWindow()
53{
54	fArchitecture->ReleaseReference();
55
56	if (fLanguage != NULL)
57		fLanguage->ReleaseReference();
58
59	if (fAddressExpressionInfo != NULL) {
60		fAddressExpressionInfo->RemoveListener(this);
61		fAddressExpressionInfo->ReleaseReference();
62	}
63
64	if (fLengthExpressionInfo != NULL) {
65		fLengthExpressionInfo->RemoveListener(this);
66		fLengthExpressionInfo->ReleaseReference();
67	}
68}
69
70
71WatchPromptWindow*
72WatchPromptWindow::Create(Architecture* architecture, target_addr_t address,
73	uint32 type, int32 length, UserInterfaceListener* listener)
74{
75	WatchPromptWindow* self = new WatchPromptWindow(architecture, address,
76		type, length, listener);
77
78	try {
79		self->_Init();
80	} catch (...) {
81		delete self;
82		throw;
83	}
84
85	return self;
86
87}
88
89
90void
91WatchPromptWindow::_Init()
92{
93	fLanguage = new CppLanguage();
94
95	BString text;
96	text.SetToFormat("0x%" B_PRIx64, fInitialAddress);
97	fAddressInput = new BTextControl("Address:", text, NULL);
98	fAddressExpressionInfo = new ExpressionInfo(text);
99	fAddressExpressionInfo->AddListener(this);
100
101	text.SetToFormat("%" B_PRId32, fInitialLength);
102	fLengthInput = new BTextControl("Length:", text, NULL);
103	fLengthExpressionInfo = new ExpressionInfo(text);
104	fLengthExpressionInfo->AddListener(this);
105
106	int32 maxDebugRegisters = 0;
107	int32 maxBytesPerRegister = 0;
108	uint8 debugCapabilityFlags = 0;
109	fArchitecture->GetWatchpointDebugCapabilities(maxDebugRegisters,
110		maxBytesPerRegister, debugCapabilityFlags);
111
112	BMenu* typeMenu = new BMenu("Watch type");
113
114	BMenuItem* watchTypeItem = new BMenuItem("Read", NULL);
115	watchTypeItem->SetEnabled(
116		(debugCapabilityFlags & WATCHPOINT_CAPABILITY_FLAG_READ) != 0);
117	typeMenu->AddItem(watchTypeItem);
118
119	watchTypeItem = new BMenuItem("Write", NULL);
120	watchTypeItem->SetEnabled(
121		(debugCapabilityFlags & WATCHPOINT_CAPABILITY_FLAG_WRITE) != 0);
122	typeMenu->AddItem(watchTypeItem);
123
124	watchTypeItem = new BMenuItem("Read/Write", NULL);
125	watchTypeItem->SetEnabled(
126		(debugCapabilityFlags & WATCHPOINT_CAPABILITY_FLAG_READ_WRITE) != 0);
127	typeMenu->AddItem(watchTypeItem);
128
129	fTypeField = new BMenuField("Type:", typeMenu);
130	BLayoutItem* labelItem = fTypeField->CreateLabelLayoutItem();
131	labelItem->View()->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
132	BLayoutBuilder::Group<>(this, B_VERTICAL)
133		.SetInsets(B_USE_DEFAULT_SPACING)
134		.AddGroup(B_HORIZONTAL, 4.0f)
135			.Add(fAddressInput)
136		.End()
137		.AddGroup(B_HORIZONTAL, 4.0f)
138			.Add(fLengthInput)
139			.Add(labelItem)
140			.Add(fTypeField->CreateMenuBarLayoutItem())
141		.End()
142		.AddGroup(B_HORIZONTAL, 4.0f)
143			.AddGlue()
144			.Add((fWatchButton = new BButton("Set",
145					new BMessage(MSG_SET_WATCHPOINT))))
146			.Add((fCancelButton = new BButton("Cancel",
147					new BMessage(B_QUIT_REQUESTED))))
148		.End();
149
150	fWatchButton->SetTarget(this);
151	fCancelButton->SetTarget(this);
152
153	fTypeField->Menu()->SetLabelFromMarked(true);
154	fTypeField->Menu()->ItemAt(fInitialType)->SetMarked(true);
155}
156
157
158void
159WatchPromptWindow::Show()
160{
161	CenterOnScreen();
162	BWindow::Show();
163}
164
165
166void
167WatchPromptWindow::ExpressionEvaluated(ExpressionInfo* info, status_t result,
168	ExpressionResult* value)
169{
170	BMessage message(MSG_EXPRESSION_EVALUATED);
171	message.AddInt32("result", result);
172	message.AddPointer("info", info);
173	BReference<ExpressionResult> reference;
174	if (value != NULL) {
175		reference.SetTo(value);
176		message.AddPointer("value", value);
177	}
178
179	if (PostMessage(&message) == B_OK)
180		reference.Detach();
181}
182
183
184void
185WatchPromptWindow::MessageReceived(BMessage* message)
186{
187	switch (message->what) {
188		case MSG_EXPRESSION_EVALUATED:
189		{
190			BString errorMessage;
191			BReference<ExpressionResult> reference;
192			ExpressionResult* value = NULL;
193			ExpressionInfo* info = NULL;
194			if (message->FindPointer("info",
195					reinterpret_cast<void**>(&info)) != B_OK) {
196				break;
197			}
198
199			if (message->FindPointer("value",
200					reinterpret_cast<void**>(&value)) == B_OK) {
201				reference.SetTo(value, true);
202				if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) {
203					Value* primitive = value->PrimitiveValue();
204					if (dynamic_cast<IntegerValue*>(primitive) != NULL) {
205						BVariant resultVariant;
206						primitive->ToVariant(resultVariant);
207						if (info == fAddressExpressionInfo) {
208							fRequestedAddress = resultVariant.ToUInt64();
209							break;
210						} else
211							fRequestedLength = resultVariant.ToInt32();
212					}
213					else
214						primitive->ToString(errorMessage);
215				} else
216					errorMessage.SetTo("Unsupported expression result.");
217			} else {
218				status_t result = message->FindInt32("result");
219				errorMessage.SetToFormat("Failed to evaluate expression: %s",
220					strerror(result));
221			}
222
223			if (fRequestedLength <= 0)
224				errorMessage = "Watchpoint length must be at least 1 byte.";
225
226			if (!errorMessage.IsEmpty()) {
227				BAlert* alert = new(std::nothrow) BAlert("Edit Watchpoint",
228					errorMessage.String(), "Close");
229				if (alert != NULL)
230					alert->Go();
231				break;
232			}
233
234			fListener->ClearWatchpointRequested(fInitialAddress);
235			fListener->SetWatchpointRequested(fRequestedAddress,
236				fTypeField->Menu()->IndexOf(fTypeField->Menu()->FindMarked()),
237				fRequestedLength, true);
238
239			PostMessage(B_QUIT_REQUESTED);
240			break;
241		}
242
243		case MSG_SET_WATCHPOINT:
244		{
245			fRequestedAddress = 0;
246			fRequestedLength = 0;
247
248			fAddressExpressionInfo->SetTo(fAddressInput->Text());
249			fListener->ExpressionEvaluationRequested(fLanguage,
250				fAddressExpressionInfo);
251
252			fLengthExpressionInfo->SetTo(fLengthInput->Text());
253			fListener->ExpressionEvaluationRequested(fLanguage,
254				fLengthExpressionInfo);
255			break;
256		}
257
258		default:
259			BWindow::MessageReceived(message);
260			break;
261	}
262
263}
264