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