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