xref: /haiku/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.cpp (revision 10ba334855ad0d7297e23f15dea292f7dbdd231c)
1 /*
2  * Copyright 2014-2016, Rene Gollent, rene@gollent.com.
3  * Distributed under the terms of the MIT License.
4  */
5 #include "ExpressionEvaluationWindow.h"
6 
7 #include <Button.h>
8 #include <LayoutBuilder.h>
9 #include <MenuField.h>
10 #include <String.h>
11 #include <TextControl.h>
12 
13 #include <AutoDeleter.h>
14 #include <AutoLocker.h>
15 
16 #include "AppMessageCodes.h"
17 #include "CppLanguage.h"
18 #include "FunctionDebugInfo.h"
19 #include "FunctionInstance.h"
20 #include "MessageCodes.h"
21 #include "SourceLanguage.h"
22 #include "SpecificImageDebugInfo.h"
23 #include "StackFrame.h"
24 #include "StackTrace.h"
25 #include "Thread.h"
26 #include "UiUtils.h"
27 #include "UserInterface.h"
28 #include "ValueNodeManager.h"
29 
30 
31 enum {
32 	MSG_THREAD_ADDED				= 'thad',
33 	MSG_THREAD_REMOVED				= 'thar',
34 
35 	MSG_THREAD_SELECTION_CHANGED	= 'thsc',
36 	MSG_FRAME_SELECTION_CHANGED		= 'frsc'
37 };
38 
39 
ExpressionEvaluationWindow(BHandler * closeTarget,::Team * team,UserInterfaceListener * listener)40 ExpressionEvaluationWindow::ExpressionEvaluationWindow(BHandler* closeTarget,
41 		::Team* team, UserInterfaceListener* listener)
42 	:
43 	BWindow(BRect(), "Evaluate Expression", B_TITLED_WINDOW,
44 		B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
45 	fExpressionInput(NULL),
46 	fThreadList(NULL),
47 	fFrameList(NULL),
48 	fVariablesView(NULL),
49 	fCloseButton(NULL),
50 	fEvaluateButton(NULL),
51 	fCloseTarget(closeTarget),
52 	fCurrentLanguage(NULL),
53 	fFallbackLanguage(NULL),
54 	fTeam(team),
55 	fSelectedThread(NULL),
56 	fSelectedFrame(NULL),
57 	fListener(listener)
58 {
59 	team->AddListener(this);
60 }
61 
62 
~ExpressionEvaluationWindow()63 ExpressionEvaluationWindow::~ExpressionEvaluationWindow()
64 {
65 	fTeam->RemoveListener(this);
66 
67 	if (fCurrentLanguage != NULL)
68 		fCurrentLanguage->ReleaseReference();
69 
70 	if (fFallbackLanguage != NULL)
71 		fFallbackLanguage->ReleaseReference();
72 
73 	if (fSelectedThread != NULL)
74 		fSelectedThread->ReleaseReference();
75 
76 	if (fSelectedFrame != NULL)
77 		fSelectedFrame->ReleaseReference();
78 }
79 
80 
81 ExpressionEvaluationWindow*
Create(BHandler * closeTarget,::Team * team,UserInterfaceListener * listener)82 ExpressionEvaluationWindow::Create(BHandler* closeTarget, ::Team* team,
83 	UserInterfaceListener* listener)
84 {
85 	ExpressionEvaluationWindow* self = new ExpressionEvaluationWindow(
86 		closeTarget, team, listener);
87 
88 	try {
89 		self->_Init();
90 	} catch (...) {
91 		delete self;
92 		throw;
93 	}
94 
95 	return self;
96 
97 }
98 
99 
100 void
Show()101 ExpressionEvaluationWindow::Show()
102 {
103 	CenterOnScreen();
104 	BWindow::Show();
105 }
106 
107 
108 bool
QuitRequested()109 ExpressionEvaluationWindow::QuitRequested()
110 {
111 	BMessenger messenger(fCloseTarget);
112 	messenger.SendMessage(MSG_EXPRESSION_WINDOW_CLOSED);
113 
114 	return BWindow::QuitRequested();
115 }
116 
117 
118 void
MessageReceived(BMessage * message)119 ExpressionEvaluationWindow::MessageReceived(BMessage* message)
120 {
121 	switch (message->what) {
122 		case MSG_THREAD_SELECTION_CHANGED:
123 		{
124 			int32 threadID;
125 			if (message->FindInt32("thread", &threadID) != B_OK)
126 				threadID = -1;
127 
128 			_HandleThreadSelectionChanged(threadID);
129 			break;
130 		}
131 
132 		case MSG_FRAME_SELECTION_CHANGED:
133 		{
134 			if (fSelectedThread == NULL)
135 				break;
136 
137 			int32 frameIndex;
138 			if (message->FindInt32("frame", &frameIndex) != B_OK)
139 				frameIndex = -1;
140 
141 			_HandleFrameSelectionChanged(frameIndex);
142 			break;
143 		}
144 
145 		case MSG_EVALUATE_EXPRESSION:
146 		{
147 			BMessage message(MSG_ADD_NEW_EXPRESSION);
148 			message.AddString("expression", fExpressionInput->Text());
149 			BMessenger(fVariablesView).SendMessage(&message);
150 			break;
151 		}
152 
153 		case MSG_THREAD_ADDED:
154 		{
155 			int32 threadID;
156 			if (message->FindInt32("thread", &threadID) == B_OK)
157 				_HandleThreadAdded(threadID);
158 			break;
159 		}
160 
161 		case MSG_THREAD_REMOVED:
162 		{
163 			int32 threadID;
164 			if (message->FindInt32("thread", &threadID) == B_OK)
165 				_HandleThreadRemoved(threadID);
166 			break;
167 		}
168 
169 		case MSG_THREAD_STATE_CHANGED:
170 		{
171 			int32 threadID;
172 			if (message->FindInt32("thread", &threadID) == B_OK)
173 				_HandleThreadStateChanged(threadID);
174 			break;
175 		}
176 
177 		case MSG_THREAD_STACK_TRACE_CHANGED:
178 		{
179 			int32 threadID;
180 			if (message->FindInt32("thread", &threadID) == B_OK)
181 				_HandleThreadStackTraceChanged(threadID);
182 			break;
183 		}
184 
185 		default:
186 			BWindow::MessageReceived(message);
187 			break;
188 	}
189 
190 }
191 
192 
193 void
ThreadAdded(const Team::ThreadEvent & event)194 ExpressionEvaluationWindow::ThreadAdded(const Team::ThreadEvent& event)
195 {
196 	BMessage message(MSG_THREAD_ADDED);
197 	message.AddInt32("thread", event.GetThread()->ID());
198 	PostMessage(&message);
199 }
200 
201 
202 void
ThreadRemoved(const Team::ThreadEvent & event)203 ExpressionEvaluationWindow::ThreadRemoved(const Team::ThreadEvent& event)
204 {
205 	BMessage message(MSG_THREAD_REMOVED);
206 	message.AddInt32("thread", event.GetThread()->ID());
207 	PostMessage(&message);
208 }
209 
210 
211 void
ThreadStateChanged(const Team::ThreadEvent & event)212 ExpressionEvaluationWindow::ThreadStateChanged(const Team::ThreadEvent& event)
213 {
214 	BMessage message(MSG_THREAD_STATE_CHANGED);
215 	message.AddInt32("thread", event.GetThread()->ID());
216 	PostMessage(&message);
217 }
218 
219 
220 void
ThreadStackTraceChanged(const Team::ThreadEvent & event)221 ExpressionEvaluationWindow::ThreadStackTraceChanged(
222 	const Team::ThreadEvent& event)
223 {
224 	BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
225 	message.AddInt32("thread", event.GetThread()->ID());
226 	PostMessage(&message);
227 }
228 
229 
230 void
ValueNodeValueRequested(CpuState * cpuState,ValueNodeContainer * container,ValueNode * valueNode)231 ExpressionEvaluationWindow::ValueNodeValueRequested(CpuState* cpuState,
232 	ValueNodeContainer* container, ValueNode* valueNode)
233 {
234 	fListener->ValueNodeValueRequested(cpuState, container, valueNode);
235 }
236 
237 
238 void
ExpressionEvaluationRequested(ExpressionInfo * info,StackFrame * frame,::Thread * thread)239 ExpressionEvaluationWindow::ExpressionEvaluationRequested(ExpressionInfo* info,
240 	StackFrame* frame, ::Thread* thread)
241 {
242 	SourceLanguage* language = fCurrentLanguage;
243 	if (fCurrentLanguage == NULL)
244 		language = fFallbackLanguage;
245 	fListener->ExpressionEvaluationRequested(language, info, frame, thread);
246 }
247 
248 
249 void
ValueNodeWriteRequested(ValueNode * node,CpuState * state,Value * newValue)250 ExpressionEvaluationWindow::ValueNodeWriteRequested(ValueNode* node,
251 	CpuState* state, Value* newValue)
252 {
253 }
254 
255 
256 void
_Init()257 ExpressionEvaluationWindow::_Init()
258 {
259 	ValueNodeManager* nodeManager = new ValueNodeManager(false);
260 	fExpressionInput = new BTextControl("Expression:", NULL, NULL);
261 	BLayoutItem* labelItem = fExpressionInput->CreateLabelLayoutItem();
262 	BLayoutItem* inputItem = fExpressionInput->CreateTextViewLayoutItem();
263 	inputItem->SetExplicitMinSize(BSize(200.0, B_SIZE_UNSET));
264 	inputItem->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
265 	labelItem->View()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
266 
267 	BLayoutBuilder::Group<>(this, B_VERTICAL)
268 		.SetInsets(B_USE_DEFAULT_SPACING)
269 		.AddGroup(B_HORIZONTAL, 4.0f)
270 			.Add((fThreadList = new BMenuField("threadList", "Thread:",
271 					new BMenu("Thread"))))
272 			.Add((fFrameList = new BMenuField("frameList", "Frame:",
273 					new BMenu("Frame"))))
274 		.End()
275 		.AddGroup(B_HORIZONTAL, 4.0f)
276 			.Add(labelItem)
277 			.Add(inputItem)
278 		.End()
279 		.Add(fVariablesView = VariablesView::Create(this, nodeManager))
280 		.AddGroup(B_HORIZONTAL, 4.0f)
281 			.AddGlue()
282 			.Add((fCloseButton = new BButton("Close",
283 					new BMessage(B_QUIT_REQUESTED))))
284 			.Add((fEvaluateButton = new BButton("Evaluate",
285 					new BMessage(MSG_EVALUATE_EXPRESSION))))
286 		.End();
287 
288 	fCloseButton->SetTarget(this);
289 	fEvaluateButton->SetTarget(this);
290 	fExpressionInput->TextView()->MakeFocus(true);
291 
292 	fThreadList->Menu()->SetLabelFromMarked(true);
293 	fFrameList->Menu()->SetLabelFromMarked(true);
294 
295 	fThreadList->Menu()->AddItem(new BMenuItem("<None>",
296 			new BMessage(MSG_THREAD_SELECTION_CHANGED)));
297 	fFrameList->Menu()->AddItem(new BMenuItem("<None>",
298 			new BMessage(MSG_FRAME_SELECTION_CHANGED)));
299 
300 	_UpdateThreadList();
301 
302 	fFallbackLanguage = new CppLanguage();
303 }
304 
305 
306 void
_HandleThreadSelectionChanged(int32 threadID)307 ExpressionEvaluationWindow::_HandleThreadSelectionChanged(int32 threadID)
308 {
309 	if (fSelectedThread != NULL) {
310 		fSelectedThread->ReleaseReference();
311 		fSelectedThread = NULL;
312 	}
313 
314 	AutoLocker< ::Team> teamLocker(fTeam);
315 	fSelectedThread = fTeam->ThreadByID(threadID);
316 	if (fSelectedThread != NULL)
317 		fSelectedThread->AcquireReference();
318 	else if (fThreadList->Menu()->FindMarked() == NULL) {
319 		// if the selected thread was cleared due to a thread event
320 		// rather than user selection, we need to reset the marked item
321 		// to reflect the new state.
322 		fThreadList->Menu()->ItemAt(0)->SetMarked(true);
323 	}
324 
325 	_UpdateFrameList();
326 
327 	fVariablesView->SetStackFrame(fSelectedThread, fSelectedFrame);
328 }
329 
330 
331 void
_HandleFrameSelectionChanged(int32 index)332 ExpressionEvaluationWindow::_HandleFrameSelectionChanged(int32 index)
333 {
334 	if (fSelectedFrame != NULL) {
335 		fSelectedFrame->ReleaseReference();
336 		fSelectedFrame = NULL;
337 	}
338 
339 	if (fCurrentLanguage != NULL) {
340 		fCurrentLanguage->ReleaseReference();
341 		fCurrentLanguage = NULL;
342 	}
343 
344 	AutoLocker< ::Team> teamLocker(fTeam);
345 	StackTrace* stackTrace = fSelectedThread->GetStackTrace();
346 	if (stackTrace != NULL) {
347 		fSelectedFrame = stackTrace->FrameAt(index);
348 		if (fSelectedFrame != NULL) {
349 			fSelectedFrame->AcquireReference();
350 
351 			FunctionInstance* instance = fSelectedFrame->Function();
352 			if (instance != NULL) {
353 				FunctionDebugInfo* functionInfo
354 					= instance->GetFunctionDebugInfo();
355 				SpecificImageDebugInfo* imageInfo =
356 					functionInfo->GetSpecificImageDebugInfo();
357 
358 				if (imageInfo->GetSourceLanguage(functionInfo,
359 						fCurrentLanguage) == B_OK) {
360 					fCurrentLanguage->AcquireReference();
361 				}
362 			}
363 		}
364 	}
365 
366 	fVariablesView->SetStackFrame(fSelectedThread, fSelectedFrame);
367 }
368 
369 
370 void
_HandleThreadAdded(int32 threadID)371 ExpressionEvaluationWindow::_HandleThreadAdded(int32 threadID)
372 {
373 	AutoLocker< ::Team> teamLocker(fTeam);
374 	::Thread* thread = fTeam->ThreadByID(threadID);
375 	if (thread == NULL)
376 		return;
377 
378 	if (thread->State() != THREAD_STATE_STOPPED)
379 		return;
380 
381 	BMenuItem* item = NULL;
382 	if (_CreateThreadMenuItem(thread, item) != B_OK)
383 		return;
384 
385 	BMenu* threadMenu = fThreadList->Menu();
386 	int32 index = 1;
387 	// find appropriate insertion index to keep menu sorted in thread order.
388 	for (; index < threadMenu->CountItems(); index++) {
389 		BMenuItem* threadItem = threadMenu->ItemAt(index);
390 		BMessage* message = threadItem->Message();
391 		if (message->FindInt32("thread") > threadID)
392 			break;
393 	}
394 
395 	bool added = false;
396 	if (index == threadMenu->CountItems())
397 		added = threadMenu->AddItem(item);
398 	else
399 		added = threadMenu->AddItem(item, index);
400 
401 	if (!added)
402 		delete item;
403 }
404 
405 
406 void
_HandleThreadRemoved(int32 threadID)407 ExpressionEvaluationWindow::_HandleThreadRemoved(int32 threadID)
408 {
409 	BMenu* threadMenu = fThreadList->Menu();
410 	for (int32 i = 0; i < threadMenu->CountItems(); i++) {
411 		BMenuItem* item = threadMenu->ItemAt(i);
412 		BMessage* message = item->Message();
413 		if (message->FindInt32("thread") == threadID) {
414 			threadMenu->RemoveItem(i);
415 			delete item;
416 			break;
417 		}
418 	}
419 
420 	if (fSelectedThread != NULL && threadID == fSelectedThread->ID())
421 		_HandleThreadSelectionChanged(-1);
422 }
423 
424 
425 void
_HandleThreadStateChanged(int32 threadID)426 ExpressionEvaluationWindow::_HandleThreadStateChanged(int32 threadID)
427 {
428 	AutoLocker< ::Team> teamLocker(fTeam);
429 
430 	::Thread* thread = fTeam->ThreadByID(threadID);
431 	if (thread == NULL)
432 		return;
433 
434 	if (thread->State() == THREAD_STATE_STOPPED)
435 		_HandleThreadAdded(threadID);
436 	else
437 		_HandleThreadRemoved(threadID);
438 }
439 
440 
441 void
_HandleThreadStackTraceChanged(int32 threadID)442 ExpressionEvaluationWindow::_HandleThreadStackTraceChanged(int32 threadID)
443 {
444 	AutoLocker< ::Team> teamLocker(fTeam);
445 
446 	::Thread* thread = fTeam->ThreadByID(threadID);
447 	if (thread == NULL)
448 		return;
449 
450 	if (thread != fSelectedThread)
451 		return;
452 
453 	_UpdateFrameList();
454 }
455 
456 
457 void
_UpdateThreadList()458 ExpressionEvaluationWindow::_UpdateThreadList()
459 {
460 	AutoLocker< ::Team> teamLocker(fTeam);
461 
462 	BMenu* frameMenu = fFrameList->Menu();
463 	while (frameMenu->CountItems() > 1)
464 		delete frameMenu->RemoveItem(1);
465 
466 	BMenu* threadMenu = fThreadList->Menu();
467 	while (threadMenu->CountItems() > 1)
468 		delete threadMenu->RemoveItem(1);
469 
470 	const ThreadList& threads = fTeam->Threads();
471 	for (ThreadList::ConstIterator it = threads.GetIterator();
472 		::Thread* thread = it.Next();) {
473 		if (thread->State() != THREAD_STATE_STOPPED)
474 			continue;
475 
476 		BMenuItem* item = NULL;
477 		if (_CreateThreadMenuItem(thread, item) != B_OK)
478 			return;
479 
480 		ObjectDeleter<BMenuItem> itemDeleter(item);
481 		if (!threadMenu->AddItem(item))
482 			return;
483 
484 		itemDeleter.Detach();
485 		if (fSelectedThread == NULL) {
486 			item->SetMarked(true);
487 			_HandleThreadSelectionChanged(thread->ID());
488 		}
489 	}
490 
491 	if (fSelectedThread == NULL)
492 		frameMenu->ItemAt(0L)->SetMarked(true);
493 
494 }
495 
496 
497 void
_UpdateFrameList()498 ExpressionEvaluationWindow::_UpdateFrameList()
499 {
500 	AutoLocker< ::Team> teamLocker(fTeam);
501 
502 	BMenu* frameMenu = fFrameList->Menu();
503 	while (frameMenu->CountItems() > 1)
504 		delete frameMenu->RemoveItem(1);
505 
506 	frameMenu->ItemAt(0L)->SetMarked(true);
507 
508 	if (fSelectedThread == NULL)
509 		return;
510 
511 	StackTrace* stackTrace = fSelectedThread->GetStackTrace();
512 	if (stackTrace == NULL)
513 		return;
514 
515  	char buffer[128];
516 	for (int32 i = 0; i < stackTrace->CountFrames(); i++) {
517 		StackFrame* frame = stackTrace->FrameAt(i);
518 		UiUtils::FunctionNameForFrame(frame, buffer, sizeof(buffer));
519 
520 		BMessage* message = new(std::nothrow) BMessage(
521 			MSG_FRAME_SELECTION_CHANGED);
522 		if (message == NULL)
523 			return;
524 
525 		message->AddInt32("frame", i);
526 
527 		BMenuItem* item = new(std::nothrow) BMenuItem(buffer,
528 			message);
529 		if (item == NULL)
530 			return;
531 
532 		if (!frameMenu->AddItem(item))
533 			return;
534 
535 		if (fSelectedFrame == NULL) {
536 			item->SetMarked(true);
537 			_HandleFrameSelectionChanged(i);
538 		}
539 	}
540 }
541 
542 
543 status_t
_CreateThreadMenuItem(::Thread * thread,BMenuItem * & _item) const544 ExpressionEvaluationWindow::_CreateThreadMenuItem(::Thread* thread,
545 	BMenuItem*& _item) const
546 {
547 	BString nameString;
548 	nameString.SetToFormat("%" B_PRId32 ": %s", thread->ID(),
549 		thread->Name());
550 
551 	BMessage* message = new(std::nothrow) BMessage(
552 		MSG_THREAD_SELECTION_CHANGED);
553 	if (message == NULL)
554 		return B_NO_MEMORY;
555 
556 	ObjectDeleter<BMessage> messageDeleter(message);
557 	message->AddInt32("thread", thread->ID());
558 	_item = new(std::nothrow) BMenuItem(nameString,
559 		message);
560 	if (_item == NULL)
561 		return B_NO_MEMORY;
562 
563 	messageDeleter.Detach();
564 	return B_OK;
565 }
566