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 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 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* 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 101 ExpressionEvaluationWindow::Show() 102 { 103 CenterOnScreen(); 104 BWindow::Show(); 105 } 106 107 108 bool 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 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 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 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 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 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 231 ExpressionEvaluationWindow::ValueNodeValueRequested(CpuState* cpuState, 232 ValueNodeContainer* container, ValueNode* valueNode) 233 { 234 fListener->ValueNodeValueRequested(cpuState, container, valueNode); 235 } 236 237 238 void 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 250 ExpressionEvaluationWindow::ValueNodeWriteRequested(ValueNode* node, 251 CpuState* state, Value* newValue) 252 { 253 } 254 255 256 void 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 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 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 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 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 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 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 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 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 544 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