Debugger: Split into core library and application.
[haiku.git] / src / apps / debugger / user_interface / gui / inspector_window / InspectorWindow.cpp
blobfa68daa6b00852f0fe7d82c7c0ea55702fe0a3ad
1 /*
2 * Copyright 2011-2016, Rene Gollent, rene@gollent.com. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "InspectorWindow.h"
8 #include <stdio.h>
10 #include <Alert.h>
11 #include <Application.h>
12 #include <AutoLocker.h>
13 #include <Button.h>
14 #include <ControlLook.h>
15 #include <LayoutBuilder.h>
16 #include <ScrollView.h>
17 #include <StringView.h>
18 #include <TextControl.h>
20 #include "Architecture.h"
21 #include "CppLanguage.h"
22 #include "GuiTeamUiSettings.h"
23 #include "MemoryView.h"
24 #include "MessageCodes.h"
25 #include "Team.h"
26 #include "UserInterface.h"
27 #include "Value.h"
30 enum {
31 MSG_NAVIGATE_PREVIOUS_BLOCK = 'npbl',
32 MSG_NAVIGATE_NEXT_BLOCK = 'npnl',
33 MSG_MEMORY_BLOCK_RETRIEVED = 'mbre',
34 MSG_EDIT_CURRENT_BLOCK = 'mecb',
35 MSG_COMMIT_MODIFIED_BLOCK = 'mcmb',
36 MSG_REVERT_MODIFIED_BLOCK = 'mrmb'
40 InspectorWindow::InspectorWindow(::Team* team, UserInterfaceListener* listener,
41 BHandler* target)
43 BWindow(BRect(100, 100, 700, 500), "Inspector", B_TITLED_WINDOW,
44 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
45 fListener(listener),
46 fAddressInput(NULL),
47 fHexMode(NULL),
48 fTextMode(NULL),
49 fWritableBlockIndicator(NULL),
50 fMemoryView(NULL),
51 fCurrentBlock(NULL),
52 fCurrentAddress(0LL),
53 fTeam(team),
54 fLanguage(NULL),
55 fExpressionInfo(NULL),
56 fTarget(target)
58 AutoLocker< ::Team> teamLocker(fTeam);
59 fTeam->AddListener(this);
63 InspectorWindow::~InspectorWindow()
65 _SetCurrentBlock(NULL);
67 if (fLanguage != NULL)
68 fLanguage->ReleaseReference();
70 if (fExpressionInfo != NULL) {
71 fExpressionInfo->RemoveListener(this);
72 fExpressionInfo->ReleaseReference();
75 AutoLocker< ::Team> teamLocker(fTeam);
76 fTeam->RemoveListener(this);
80 /* static */ InspectorWindow*
81 InspectorWindow::Create(::Team* team, UserInterfaceListener* listener,
82 BHandler* target)
84 InspectorWindow* self = new InspectorWindow(team, listener, target);
86 try {
87 self->_Init();
88 } catch (...) {
89 delete self;
90 throw;
93 return self;
97 void
98 InspectorWindow::_Init()
100 fLanguage = new CppLanguage();
101 fExpressionInfo = new ExpressionInfo();
102 fExpressionInfo->AddListener(this);
104 BScrollView* scrollView;
106 BMenu* hexMenu = new BMenu("Hex Mode");
107 BMessage* message = new BMessage(MSG_SET_HEX_MODE);
108 message->AddInt32("mode", HexModeNone);
109 BMenuItem* item = new BMenuItem("<None>", message, '0');
110 hexMenu->AddItem(item);
111 message = new BMessage(*message);
112 message->ReplaceInt32("mode", HexMode8BitInt);
113 item = new BMenuItem("8-bit integer", message, '1');
114 hexMenu->AddItem(item);
115 message = new BMessage(*message);
116 message->ReplaceInt32("mode", HexMode16BitInt);
117 item = new BMenuItem("16-bit integer", message, '2');
118 hexMenu->AddItem(item);
119 message = new BMessage(*message);
120 message->ReplaceInt32("mode", HexMode32BitInt);
121 item = new BMenuItem("32-bit integer", message, '3');
122 hexMenu->AddItem(item);
123 message = new BMessage(*message);
124 message->ReplaceInt32("mode", HexMode64BitInt);
125 item = new BMenuItem("64-bit integer", message, '4');
126 hexMenu->AddItem(item);
128 BMenu* endianMenu = new BMenu("Endian Mode");
129 message = new BMessage(MSG_SET_ENDIAN_MODE);
130 message->AddInt32("mode", EndianModeLittleEndian);
131 item = new BMenuItem("Little Endian", message, 'L');
132 endianMenu->AddItem(item);
133 message = new BMessage(*message);
134 message->ReplaceInt32("mode", EndianModeBigEndian);
135 item = new BMenuItem("Big Endian", message, 'B');
136 endianMenu->AddItem(item);
138 BMenu* textMenu = new BMenu("Text Mode");
139 message = new BMessage(MSG_SET_TEXT_MODE);
140 message->AddInt32("mode", TextModeNone);
141 item = new BMenuItem("<None>", message, 'N');
142 textMenu->AddItem(item);
143 message = new BMessage(*message);
144 message->ReplaceInt32("mode", TextModeASCII);
145 item = new BMenuItem("ASCII", message, 'A');
146 textMenu->AddItem(item);
148 BLayoutBuilder::Group<>(this, B_VERTICAL)
149 .SetInsets(B_USE_DEFAULT_SPACING)
150 .AddGroup(B_HORIZONTAL)
151 .Add(fAddressInput = new BTextControl("addrInput",
152 "Target Address:", "",
153 new BMessage(MSG_INSPECT_ADDRESS)))
154 .Add(fPreviousBlockButton = new BButton("navPrevious", "<",
155 new BMessage(MSG_NAVIGATE_PREVIOUS_BLOCK)))
156 .Add(fNextBlockButton = new BButton("navNext", ">",
157 new BMessage(MSG_NAVIGATE_NEXT_BLOCK)))
158 .End()
159 .AddGroup(B_HORIZONTAL)
160 .Add(fHexMode = new BMenuField("hexMode", "Hex Mode:",
161 hexMenu))
162 .AddGlue()
163 .Add(fEndianMode = new BMenuField("endianMode", "Endian Mode:",
164 endianMenu))
165 .AddGlue()
166 .Add(fTextMode = new BMenuField("viewMode", "Text Mode:",
167 textMenu))
168 .End()
169 .Add(scrollView = new BScrollView("memory scroll",
170 NULL, 0, false, true), 3.0f)
171 .AddGroup(B_HORIZONTAL)
172 .Add(fWritableBlockIndicator = new BStringView("writableIndicator",
173 _GetCurrentWritableIndicator()))
174 .AddGlue()
175 .Add(fEditBlockButton = new BButton("editBlock", "Edit",
176 new BMessage(MSG_EDIT_CURRENT_BLOCK)))
177 .Add(fCommitBlockButton = new BButton("commitBlock", "Commit",
178 new BMessage(MSG_COMMIT_MODIFIED_BLOCK)))
179 .Add(fRevertBlockButton = new BButton("revertBlock", "Revert",
180 new BMessage(MSG_REVERT_MODIFIED_BLOCK)))
181 .End()
182 .End();
184 fHexMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
185 fEndianMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
186 fTextMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
188 int32 targetEndian = fTeam->GetArchitecture()->IsBigEndian()
189 ? EndianModeBigEndian : EndianModeLittleEndian;
191 scrollView->SetTarget(fMemoryView = MemoryView::Create(fTeam, this));
193 fAddressInput->SetTarget(this);
194 fPreviousBlockButton->SetTarget(this);
195 fNextBlockButton->SetTarget(this);
196 fPreviousBlockButton->SetEnabled(false);
197 fNextBlockButton->SetEnabled(false);
199 fEditBlockButton->SetTarget(this);
200 fCommitBlockButton->SetTarget(this);
201 fRevertBlockButton->SetTarget(this);
203 fEditBlockButton->SetEnabled(false);
204 fCommitBlockButton->Hide();
205 fRevertBlockButton->Hide();
207 hexMenu->SetLabelFromMarked(true);
208 hexMenu->SetTargetForItems(fMemoryView);
209 endianMenu->SetLabelFromMarked(true);
210 endianMenu->SetTargetForItems(fMemoryView);
211 textMenu->SetLabelFromMarked(true);
212 textMenu->SetTargetForItems(fMemoryView);
214 // default to 8-bit format w/ text display
215 hexMenu->ItemAt(1)->SetMarked(true);
216 textMenu->ItemAt(1)->SetMarked(true);
218 if (targetEndian == EndianModeBigEndian)
219 endianMenu->ItemAt(1)->SetMarked(true);
220 else
221 endianMenu->ItemAt(0)->SetMarked(true);
223 fAddressInput->TextView()->MakeFocus(true);
225 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, new BMessage(
226 MSG_NAVIGATE_PREVIOUS_BLOCK));
227 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, new BMessage(
228 MSG_NAVIGATE_NEXT_BLOCK));
232 void
233 InspectorWindow::MessageReceived(BMessage* message)
235 switch (message->what) {
236 case MSG_THREAD_STATE_CHANGED:
238 ::Thread* thread;
239 if (message->FindPointer("thread",
240 reinterpret_cast<void**>(&thread)) != B_OK) {
241 break;
244 BReference< ::Thread> threadReference(thread, true);
245 if (thread->State() == THREAD_STATE_STOPPED) {
246 if (fCurrentBlock != NULL) {
247 _SetCurrentBlock(NULL);
248 _SetToAddress(fCurrentAddress);
251 break;
253 case MSG_INSPECT_ADDRESS:
255 target_addr_t address = 0;
256 if (message->FindUInt64("address", &address) != B_OK) {
257 if (fAddressInput->TextView()->TextLength() == 0)
258 break;
260 fExpressionInfo->SetTo(fAddressInput->Text());
262 fListener->ExpressionEvaluationRequested(fLanguage,
263 fExpressionInfo);
264 } else
265 _SetToAddress(address);
266 break;
268 case MSG_EXPRESSION_EVALUATED:
270 BString errorMessage;
271 BReference<ExpressionResult> reference;
272 ExpressionResult* value = NULL;
273 if (message->FindPointer("value",
274 reinterpret_cast<void**>(&value)) == B_OK) {
275 reference.SetTo(value, true);
276 if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) {
277 Value* primitive = value->PrimitiveValue();
278 BVariant variantValue;
279 primitive->ToVariant(variantValue);
280 if (variantValue.Type() == B_STRING_TYPE) {
281 errorMessage.SetTo(variantValue.ToString());
282 } else {
283 _SetToAddress(variantValue.ToUInt64());
284 break;
287 } else {
288 status_t result = message->FindInt32("result");
289 errorMessage.SetToFormat("Failed to evaluate expression: %s",
290 strerror(result));
293 BAlert* alert = new(std::nothrow) BAlert("Inspect Address",
294 errorMessage.String(), "Close");
295 if (alert != NULL)
296 alert->Go();
297 break;
300 case MSG_NAVIGATE_PREVIOUS_BLOCK:
301 case MSG_NAVIGATE_NEXT_BLOCK:
303 if (fCurrentBlock != NULL) {
304 target_addr_t address = fCurrentBlock->BaseAddress();
305 if (message->what == MSG_NAVIGATE_PREVIOUS_BLOCK)
306 address -= fCurrentBlock->Size();
307 else
308 address += fCurrentBlock->Size();
310 BMessage setMessage(MSG_INSPECT_ADDRESS);
311 setMessage.AddUInt64("address", address);
312 PostMessage(&setMessage);
314 break;
316 case MSG_MEMORY_BLOCK_RETRIEVED:
318 TeamMemoryBlock* block = NULL;
319 status_t result;
320 if (message->FindPointer("block",
321 reinterpret_cast<void **>(&block)) != B_OK
322 || message->FindInt32("result", &result) != B_OK) {
323 break;
326 if (result == B_OK) {
327 _SetCurrentBlock(block);
328 fPreviousBlockButton->SetEnabled(true);
329 fNextBlockButton->SetEnabled(true);
330 } else {
331 BString errorMessage;
332 errorMessage.SetToFormat("Unable to read address 0x%" B_PRIx64
333 ": %s", block->BaseAddress(), strerror(result));
335 BAlert* alert = new(std::nothrow) BAlert("Inspect address",
336 errorMessage.String(), "Close");
337 if (alert == NULL)
338 break;
340 alert->Go(NULL);
341 block->ReleaseReference();
343 break;
345 case MSG_EDIT_CURRENT_BLOCK:
347 _SetEditMode(true);
348 break;
350 case MSG_MEMORY_DATA_CHANGED:
352 if (fCurrentBlock == NULL)
353 break;
355 target_addr_t address;
356 if (message->FindUInt64("address", &address) == B_OK
357 && address >= fCurrentBlock->BaseAddress()
358 && address < fCurrentBlock->BaseAddress()
359 + fCurrentBlock->Size()) {
360 fCurrentBlock->Invalidate();
361 _SetEditMode(false);
362 fListener->InspectRequested(address, this);
364 break;
366 case MSG_COMMIT_MODIFIED_BLOCK:
368 // TODO: this could conceivably be extended to detect the
369 // individual modified regions and only write those back.
370 // That would require potentially submitting multiple separate
371 // write requests, and thus require tracking all the writes being
372 // waited upon for completion.
373 fListener->MemoryWriteRequested(fCurrentBlock->BaseAddress(),
374 fMemoryView->GetEditedData(), fCurrentBlock->Size());
375 break;
377 case MSG_REVERT_MODIFIED_BLOCK:
379 _SetEditMode(false);
380 break;
382 default:
384 BWindow::MessageReceived(message);
385 break;
391 bool
392 InspectorWindow::QuitRequested()
394 BMessage settings(MSG_INSPECTOR_WINDOW_CLOSED);
395 SaveSettings(settings);
397 BMessenger(fTarget).SendMessage(&settings);
398 return true;
402 void
403 InspectorWindow::ThreadStateChanged(const Team::ThreadEvent& event)
405 BMessage message(MSG_THREAD_STATE_CHANGED);
406 BReference< ::Thread> threadReference(event.GetThread());
407 message.AddPointer("thread", threadReference.Get());
409 if (PostMessage(&message) == B_OK)
410 threadReference.Detach();
414 void
415 InspectorWindow::MemoryChanged(const Team::MemoryChangedEvent& event)
417 BMessage message(MSG_MEMORY_DATA_CHANGED);
418 message.AddUInt64("address", event.GetTargetAddress());
419 message.AddUInt64("size", event.GetSize());
421 PostMessage(&message);
425 void
426 InspectorWindow::MemoryBlockRetrieved(TeamMemoryBlock* block)
428 BMessage message(MSG_MEMORY_BLOCK_RETRIEVED);
429 message.AddPointer("block", block);
430 message.AddInt32("result", B_OK);
431 PostMessage(&message);
435 void
436 InspectorWindow::MemoryBlockRetrievalFailed(TeamMemoryBlock* block,
437 status_t result)
439 BMessage message(MSG_MEMORY_BLOCK_RETRIEVED);
440 message.AddPointer("block", block);
441 message.AddInt32("result", result);
442 PostMessage(&message);
446 void
447 InspectorWindow::TargetAddressChanged(target_addr_t address)
449 AutoLocker<BLooper> lock(this);
450 if (lock.IsLocked()) {
451 fCurrentAddress = address;
452 BString computedAddress;
453 computedAddress.SetToFormat("0x%" B_PRIx64, address);
454 fAddressInput->SetText(computedAddress.String());
459 void
460 InspectorWindow::HexModeChanged(int32 newMode)
462 AutoLocker<BLooper> lock(this);
463 if (lock.IsLocked()) {
464 BMenu* menu = fHexMode->Menu();
465 if (newMode < 0 || newMode > menu->CountItems())
466 return;
467 BMenuItem* item = menu->ItemAt(newMode);
468 item->SetMarked(true);
473 void
474 InspectorWindow::EndianModeChanged(int32 newMode)
476 AutoLocker<BLooper> lock(this);
477 if (lock.IsLocked()) {
478 BMenu* menu = fEndianMode->Menu();
479 if (newMode < 0 || newMode > menu->CountItems())
480 return;
481 BMenuItem* item = menu->ItemAt(newMode);
482 item->SetMarked(true);
487 void
488 InspectorWindow::TextModeChanged(int32 newMode)
490 AutoLocker<BLooper> lock(this);
491 if (lock.IsLocked()) {
492 BMenu* menu = fTextMode->Menu();
493 if (newMode < 0 || newMode > menu->CountItems())
494 return;
495 BMenuItem* item = menu->ItemAt(newMode);
496 item->SetMarked(true);
501 void
502 InspectorWindow::ExpressionEvaluated(ExpressionInfo* info, status_t result,
503 ExpressionResult* value)
505 BMessage message(MSG_EXPRESSION_EVALUATED);
506 message.AddInt32("result", result);
507 BReference<ExpressionResult> reference;
508 if (value != NULL) {
509 reference.SetTo(value);
510 message.AddPointer("value", value);
513 if (PostMessage(&message) == B_OK)
514 reference.Detach();
518 status_t
519 InspectorWindow::LoadSettings(const GuiTeamUiSettings& settings)
521 AutoLocker<BLooper> lock(this);
522 if (!lock.IsLocked())
523 return B_ERROR;
525 BMessage inspectorSettings;
526 if (settings.Settings("inspectorWindow", inspectorSettings) != B_OK)
527 return B_OK;
529 BRect frameRect;
530 if (inspectorSettings.FindRect("frame", &frameRect) == B_OK) {
531 ResizeTo(frameRect.Width(), frameRect.Height());
532 MoveTo(frameRect.left, frameRect.top);
535 _LoadMenuFieldMode(fHexMode, "Hex", inspectorSettings);
536 _LoadMenuFieldMode(fEndianMode, "Endian", inspectorSettings);
537 _LoadMenuFieldMode(fTextMode, "Text", inspectorSettings);
539 return B_OK;
543 status_t
544 InspectorWindow::SaveSettings(BMessage& settings)
546 AutoLocker<BLooper> lock(this);
547 if (!lock.IsLocked())
548 return B_ERROR;
550 settings.MakeEmpty();
552 status_t error = settings.AddRect("frame", Frame());
553 if (error != B_OK)
554 return error;
556 error = _SaveMenuFieldMode(fHexMode, "Hex", settings);
557 if (error != B_OK)
558 return error;
560 error = _SaveMenuFieldMode(fEndianMode, "Endian", settings);
561 if (error != B_OK)
562 return error;
564 error = _SaveMenuFieldMode(fTextMode, "Text", settings);
565 if (error != B_OK)
566 return error;
568 return B_OK;
572 void
573 InspectorWindow::_LoadMenuFieldMode(BMenuField* field, const char* name,
574 const BMessage& settings)
576 BString fieldName;
577 int32 mode;
578 fieldName.SetToFormat("%sMode", name);
579 if (settings.FindInt32(fieldName.String(), &mode) == B_OK) {
580 BMenu* menu = field->Menu();
581 for (int32 i = 0; i < menu->CountItems(); i++) {
582 BInvoker* item = menu->ItemAt(i);
583 if (item->Message()->FindInt32("mode") == mode) {
584 item->Invoke();
585 break;
592 status_t
593 InspectorWindow::_SaveMenuFieldMode(BMenuField* field, const char* name,
594 BMessage& settings)
596 BMenuItem* item = field->Menu()->FindMarked();
597 if (item && item->Message()) {
598 int32 mode = item->Message()->FindInt32("mode");
599 BString fieldName;
600 fieldName.SetToFormat("%sMode", name);
601 return settings.AddInt32(fieldName.String(), mode);
604 return B_OK;
608 void
609 InspectorWindow::_SetToAddress(target_addr_t address)
611 fCurrentAddress = address;
612 if (fCurrentBlock == NULL
613 || !fCurrentBlock->Contains(address)) {
614 fListener->InspectRequested(address, this);
615 } else
616 fMemoryView->SetTargetAddress(fCurrentBlock, address);
620 void
621 InspectorWindow::_SetCurrentBlock(TeamMemoryBlock* block)
623 AutoLocker< ::Team> teamLocker(fTeam);
624 if (fCurrentBlock != NULL) {
625 fCurrentBlock->RemoveListener(this);
626 fCurrentBlock->ReleaseReference();
629 fCurrentBlock = block;
630 fMemoryView->SetTargetAddress(fCurrentBlock, fCurrentAddress);
631 _UpdateWritableOptions();
635 bool
636 InspectorWindow::_GetWritableState() const
638 return fCurrentBlock != NULL ? fCurrentBlock->IsWritable() : false;
642 void
643 InspectorWindow::_SetEditMode(bool enabled)
645 if (enabled == fMemoryView->GetEditMode())
646 return;
648 status_t error = fMemoryView->SetEditMode(enabled);
649 if (error != B_OK)
650 return;
652 if (enabled) {
653 fEditBlockButton->Hide();
654 fCommitBlockButton->Show();
655 fRevertBlockButton->Show();
656 } else {
657 fEditBlockButton->Show();
658 fCommitBlockButton->Hide();
659 fRevertBlockButton->Hide();
662 fHexMode->SetEnabled(!enabled);
663 fEndianMode->SetEnabled(!enabled);
665 // while the block is being edited, disable block navigation controls.
666 fAddressInput->SetEnabled(!enabled);
667 fPreviousBlockButton->SetEnabled(!enabled);
668 fNextBlockButton->SetEnabled(!enabled);
670 InvalidateLayout();
674 void
675 InspectorWindow::_UpdateWritableOptions()
677 fEditBlockButton->SetEnabled(_GetWritableState());
678 _UpdateWritableIndicator();
682 void
683 InspectorWindow::_UpdateWritableIndicator()
685 fWritableBlockIndicator->SetText(_GetCurrentWritableIndicator());
689 const char*
690 InspectorWindow::_GetCurrentWritableIndicator() const
692 static char buffer[32];
693 snprintf(buffer, sizeof(buffer), "Writable: %s", fCurrentBlock == NULL
694 ? "N/A" : fCurrentBlock->IsWritable() ? "Yes" : "No");
696 return buffer;