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