1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2010-2012, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "TeamWindow.h"
9
10#include <stdio.h>
11
12#include <Alert.h>
13#include <Button.h>
14#include <FilePanel.h>
15#include <FindDirectory.h>
16#include <LayoutBuilder.h>
17#include <Menu.h>
18#include <MenuBar.h>
19#include <MenuItem.h>
20#include <Message.h>
21#include <MessageFilter.h>
22#include <Path.h>
23#include <StringView.h>
24#include <TabView.h>
25#include <ScrollView.h>
26#include <SplitView.h>
27#include <TextView.h>
28
29#include <AutoLocker.h>
30
31#include "Breakpoint.h"
32#include "CpuState.h"
33#include "DisassembledCode.h"
34#include "FileSourceCode.h"
35#include "GuiSettingsUtils.h"
36#include "GuiTeamUiSettings.h"
37#include "Image.h"
38#include "ImageDebugInfo.h"
39#include "InspectorWindow.h"
40#include "LocatableFile.h"
41#include "MessageCodes.h"
42#include "RegistersView.h"
43#include "StackTrace.h"
44#include "StackTraceView.h"
45#include "Tracing.h"
46#include "TypeComponentPath.h"
47#include "UiUtils.h"
48#include "UserInterface.h"
49#include "Variable.h"
50#include "WatchPromptWindow.h"
51
52
53enum {
54	MAIN_TAB_INDEX_THREADS	= 0,
55	MAIN_TAB_INDEX_IMAGES	= 1
56};
57
58
59enum {
60	MSG_CHOOSE_DEBUG_REPORT_LOCATION = 'ccrl',
61	MSG_DEBUG_REPORT_SAVED = 'drsa',
62	MSG_LOCATE_SOURCE_IF_NEEDED = 'lsin'
63};
64
65
66class PathViewMessageFilter : public BMessageFilter {
67public:
68		PathViewMessageFilter(BMessenger teamWindow)
69			:
70			BMessageFilter(B_MOUSE_UP),
71			fTeamWindowMessenger(teamWindow)
72		{
73		}
74
75		virtual filter_result Filter(BMessage*, BHandler**)
76		{
77			fTeamWindowMessenger.SendMessage(MSG_LOCATE_SOURCE_IF_NEEDED);
78
79			return B_DISPATCH_MESSAGE;
80		}
81
82private:
83		BMessenger fTeamWindowMessenger;
84};
85
86
87// #pragma mark - TeamWindow
88
89
90TeamWindow::TeamWindow(::Team* team, UserInterfaceListener* listener)
91	:
92	BWindow(BRect(100, 100, 899, 699), "Team", B_TITLED_WINDOW,
93		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
94	fTeam(team),
95	fActiveThread(NULL),
96	fActiveImage(NULL),
97	fActiveStackTrace(NULL),
98	fActiveStackFrame(NULL),
99	fActiveBreakpoint(NULL),
100	fActiveFunction(NULL),
101	fActiveSourceCode(NULL),
102	fActiveSourceObject(ACTIVE_SOURCE_NONE),
103	fListener(listener),
104	fTabView(NULL),
105	fLocalsTabView(NULL),
106	fThreadListView(NULL),
107	fImageListView(NULL),
108	fImageFunctionsView(NULL),
109	fBreakpointsView(NULL),
110	fVariablesView(NULL),
111	fRegistersView(NULL),
112	fStackTraceView(NULL),
113	fSourceView(NULL),
114	fRunButton(NULL),
115	fStepOverButton(NULL),
116	fStepIntoButton(NULL),
117	fStepOutButton(NULL),
118	fInspectorWindow(NULL),
119	fFilePanel(NULL)
120{
121	fTeam->Lock();
122	BString name = fTeam->Name();
123	fTeam->Unlock();
124	if (fTeam->ID() >= 0)
125		name << " (" << fTeam->ID() << ")";
126	SetTitle(name.String());
127
128	fTeam->AddListener(this);
129}
130
131
132TeamWindow::~TeamWindow()
133{
134	if (fThreadListView != NULL)
135		fThreadListView->UnsetListener();
136	if (fStackTraceView != NULL)
137		fStackTraceView->UnsetListener();
138	if (fSourceView != NULL)
139		fSourceView->UnsetListener();
140
141	fTeam->RemoveListener(this);
142
143	_SetActiveSourceCode(NULL);
144	_SetActiveFunction(NULL);
145	_SetActiveBreakpoint(NULL);
146	_SetActiveStackFrame(NULL);
147	_SetActiveStackTrace(NULL);
148	_SetActiveImage(NULL);
149	_SetActiveThread(NULL);
150
151	delete fFilePanel;
152}
153
154
155/*static*/ TeamWindow*
156TeamWindow::Create(::Team* team, UserInterfaceListener* listener)
157{
158	TeamWindow* self = new TeamWindow(team, listener);
159
160	try {
161		self->_Init();
162	} catch (...) {
163		delete self;
164		throw;
165	}
166
167	return self;
168}
169
170
171void
172TeamWindow::DispatchMessage(BMessage* message, BHandler* handler)
173{
174	// Handle function key shortcuts for stepping
175	switch (message->what) {
176		case B_KEY_DOWN:
177			if (fActiveThread != NULL) {
178				int32 key;
179				uint32 modifiers;
180				if (message->FindInt32("key", &key) == B_OK
181					&& message->FindInt32("modifiers", (int32*)&modifiers)
182					== B_OK) {
183					switch (key) {
184						case B_F5_KEY:
185							fListener->ThreadActionRequested(
186								fActiveThread->ID(), MSG_THREAD_RUN);
187							break;
188						case B_F10_KEY:
189							fListener->ThreadActionRequested(
190								fActiveThread->ID(), MSG_THREAD_STEP_OVER);
191							break;
192						case B_F11_KEY:
193							if ((modifiers & B_SHIFT_KEY) != 0) {
194								fListener->ThreadActionRequested(
195									fActiveThread->ID(), MSG_THREAD_STEP_OUT);
196							} else {
197								fListener->ThreadActionRequested(
198									fActiveThread->ID(), MSG_THREAD_STEP_INTO);
199							}
200							break;
201						default:
202							break;
203					}
204				}
205			}
206			break;
207
208		case B_COPY:
209		case B_SELECT_ALL:
210			BView* focusView = CurrentFocus();
211			if (focusView != NULL)
212				focusView->MessageReceived(message);
213			break;
214	}
215	BWindow::DispatchMessage(message, handler);
216}
217
218
219void
220TeamWindow::MessageReceived(BMessage* message)
221{
222	switch (message->what) {
223		case MSG_CHOOSE_DEBUG_REPORT_LOCATION:
224		{
225			try {
226				char filename[B_FILE_NAME_LENGTH];
227				UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename));
228				BMessenger msgr(this);
229				fFilePanel = new BFilePanel(B_SAVE_PANEL, &msgr,
230					NULL, 0, false, new BMessage(MSG_GENERATE_DEBUG_REPORT));
231				fFilePanel->SetSaveText(filename);
232				fFilePanel->Show();
233			} catch (...) {
234				delete fFilePanel;
235				fFilePanel = NULL;
236			}
237			break;
238		}
239		case MSG_GENERATE_DEBUG_REPORT:
240		{
241			delete fFilePanel;
242			fFilePanel = NULL;
243
244			BPath path;
245			entry_ref ref;
246			if (message->FindRef("directory", &ref) == B_OK
247				&& message->HasString("name")) {
248				path.SetTo(&ref);
249				path.Append(message->FindString("name"));
250				if (get_ref_for_path(path.Path(), &ref) == B_OK)
251					fListener->DebugReportRequested(&ref);
252			}
253			break;
254		}
255		case MSG_DEBUG_REPORT_SAVED:
256		{
257			BString data;
258			data.SetToFormat("Debug report successfully saved to '%s'",
259				message->FindString("path"));
260			BAlert *alert = new BAlert("Report saved", data.String(),
261				"OK");
262			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
263			alert->Go();
264			break;
265		}
266		case MSG_SHOW_INSPECTOR_WINDOW:
267		{
268			if (fInspectorWindow) {
269				fInspectorWindow->Activate(true);
270			} else {
271				try {
272					fInspectorWindow = InspectorWindow::Create(fTeam,
273						fListener, this);
274					if (fInspectorWindow != NULL) {
275						BMessage settings;
276						fInspectorWindow->LoadSettings(fUiSettings);
277						fInspectorWindow->Show();
278					}
279	           	} catch (...) {
280	           		// TODO: notify user
281	           	}
282			}
283
284			target_addr_t address;
285			if (message->FindUInt64("address", &address) == B_OK) {
286				BMessage addressMessage(MSG_INSPECT_ADDRESS);
287				addressMessage.AddUInt64("address", address);
288				fInspectorWindow->PostMessage(&addressMessage);
289			}
290           	break;
291		}
292		case MSG_INSPECTOR_WINDOW_CLOSED:
293		{
294			_SaveInspectorSettings(CurrentMessage());
295			fInspectorWindow = NULL;
296			break;
297
298		}
299		case MSG_SHOW_WATCH_VARIABLE_PROMPT:
300		{
301			target_addr_t address;
302			uint32 type;
303			int32 length;
304
305			if (message->FindUInt64("address", &address) != B_OK
306				|| message->FindUInt32("type", &type) != B_OK
307				|| message->FindInt32("length", &length) != B_OK) {
308				break;
309			}
310
311			try {
312				WatchPromptWindow* window = WatchPromptWindow::Create(
313					fTeam->GetArchitecture(), address, type, length,
314					fListener);
315				window->Show();
316			} catch (...) {
317				// TODO: notify user
318			}
319			break;
320		}
321		case B_REFS_RECEIVED:
322		{
323			entry_ref locatedPath;
324			message->FindRef("refs", &locatedPath);
325			_HandleResolveMissingSourceFile(locatedPath);
326			break;
327		}
328		case MSG_LOCATE_SOURCE_IF_NEEDED:
329		{
330			if (fActiveFunction != NULL
331				&& fActiveFunction->GetFunctionDebugInfo()
332					->SourceFile() != NULL && fActiveSourceCode != NULL
333				&& fActiveSourceCode->GetSourceFile() == NULL) {
334				try {
335					if (fFilePanel == NULL) {
336						fFilePanel = new BFilePanel(B_OPEN_PANEL,
337							new BMessenger(this));
338					}
339					fFilePanel->Show();
340				} catch (...) {
341					delete fFilePanel;
342					fFilePanel = NULL;
343				}
344			}
345			break;
346		}
347		case MSG_THREAD_RUN:
348		case MSG_THREAD_STOP:
349		case MSG_THREAD_STEP_OVER:
350		case MSG_THREAD_STEP_INTO:
351		case MSG_THREAD_STEP_OUT:
352			if (fActiveThread != NULL) {
353				fListener->ThreadActionRequested(fActiveThread->ID(),
354					message->what);
355			}
356			break;
357
358		case MSG_THREAD_STATE_CHANGED:
359		{
360			int32 threadID;
361			if (message->FindInt32("thread", &threadID) != B_OK)
362				break;
363
364			_HandleThreadStateChanged(threadID);
365			break;
366		}
367		case MSG_THREAD_CPU_STATE_CHANGED:
368		{
369			int32 threadID;
370			if (message->FindInt32("thread", &threadID) != B_OK)
371				break;
372
373			_HandleCpuStateChanged(threadID);
374			break;
375		}
376
377		case MSG_THREAD_STACK_TRACE_CHANGED:
378		{
379			int32 threadID;
380			if (message->FindInt32("thread", &threadID) != B_OK)
381				break;
382
383			_HandleStackTraceChanged(threadID);
384			break;
385		}
386
387		case MSG_IMAGE_DEBUG_INFO_CHANGED:
388		{
389			int32 imageID;
390			if (message->FindInt32("image", &imageID) != B_OK)
391				break;
392
393			_HandleImageDebugInfoChanged(imageID);
394			break;
395		}
396
397		case MSG_USER_BREAKPOINT_CHANGED:
398		{
399			UserBreakpoint* breakpoint;
400			if (message->FindPointer("breakpoint", (void**)&breakpoint) != B_OK)
401				break;
402			BReference<UserBreakpoint> breakpointReference(breakpoint, true);
403
404			_HandleUserBreakpointChanged(breakpoint);
405			break;
406		}
407
408		case MSG_WATCHPOINT_CHANGED:
409		{
410			Watchpoint* watchpoint;
411			if (message->FindPointer("watchpoint", (void**)&watchpoint) != B_OK)
412				break;
413			BReference<Watchpoint> watchpointReference(watchpoint, true);
414
415			_HandleWatchpointChanged(watchpoint);
416			break;
417
418		}
419
420		case MSG_FUNCTION_SOURCE_CODE_CHANGED:
421		{
422			_HandleSourceCodeChanged();
423			break;
424		}
425
426		default:
427			BWindow::MessageReceived(message);
428			break;
429	}
430}
431
432
433bool
434TeamWindow::QuitRequested()
435{
436	fListener->UserInterfaceQuitRequested();
437
438	return false;
439}
440
441
442status_t
443TeamWindow::LoadSettings(const GuiTeamUiSettings* settings)
444{
445	AutoLocker<BWindow> lock(this);
446	if (!lock.IsLocked())
447		return B_ERROR;
448
449	BMessage teamWindowSettings;
450	// no settings stored yet
451	if (settings->Settings("teamWindow", teamWindowSettings) != B_OK)
452		return B_OK;
453
454	BRect frame;
455	if (teamWindowSettings.FindRect("frame", &frame) == B_OK) {
456		ResizeTo(frame.Width(), frame.Height());
457		MoveTo(frame.left, frame.top);
458	}
459
460	BMessage archive;
461	if (teamWindowSettings.FindMessage("sourceSplit", &archive) == B_OK)
462		GuiSettingsUtils::UnarchiveSplitView(archive, fSourceSplitView);
463
464	if (teamWindowSettings.FindMessage("functionSplit", &archive) == B_OK)
465		GuiSettingsUtils::UnarchiveSplitView(archive, fFunctionSplitView);
466
467	if (teamWindowSettings.FindMessage("imageSplit", &archive) == B_OK)
468		GuiSettingsUtils::UnarchiveSplitView(archive, fImageSplitView);
469
470	if (teamWindowSettings.FindMessage("threadSplit", &archive) == B_OK)
471		GuiSettingsUtils::UnarchiveSplitView(archive, fThreadSplitView);
472
473	if (teamWindowSettings.FindMessage("imageListView", &archive) == B_OK)
474		fImageListView->LoadSettings(archive);
475
476	if (teamWindowSettings.FindMessage("imageFunctionsView", &archive) == B_OK)
477		fImageFunctionsView->LoadSettings(archive);
478
479	if (teamWindowSettings.FindMessage("threadListView", &archive) == B_OK)
480		fThreadListView->LoadSettings(archive);
481
482	if (teamWindowSettings.FindMessage("variablesView", &archive) == B_OK)
483		fVariablesView->LoadSettings(archive);
484
485	if (teamWindowSettings.FindMessage("registersView", &archive) == B_OK)
486		fRegistersView->LoadSettings(archive);
487
488	if (teamWindowSettings.FindMessage("stackTraceView", &archive) == B_OK)
489		fStackTraceView->LoadSettings(archive);
490
491	if (teamWindowSettings.FindMessage("breakpointsView", &archive) == B_OK)
492		fBreakpointsView->LoadSettings(archive);
493
494	fUiSettings = *settings;
495
496	return B_OK;
497}
498
499
500status_t
501TeamWindow::SaveSettings(GuiTeamUiSettings* settings)
502{
503	AutoLocker<BWindow> lock(this);
504	if (!lock.IsLocked())
505		return B_ERROR;
506
507	BMessage inspectorSettings;
508	if (fUiSettings.Settings("inspectorWindow", inspectorSettings) == B_OK) {
509		if (!settings->AddSettings("inspectorWindow", inspectorSettings))
510			return B_NO_MEMORY;
511	}
512
513	BMessage archive;
514	BMessage teamWindowSettings;
515	if (teamWindowSettings.AddRect("frame", Frame()) != B_OK)
516		return B_NO_MEMORY;
517
518	if (GuiSettingsUtils::ArchiveSplitView(archive, fSourceSplitView) != B_OK)
519		return B_NO_MEMORY;
520	if (teamWindowSettings.AddMessage("sourceSplit", &archive) != B_OK)
521		return B_NO_MEMORY;
522
523	if (GuiSettingsUtils::ArchiveSplitView(archive, fFunctionSplitView) != B_OK)
524		return B_NO_MEMORY;
525	if (teamWindowSettings.AddMessage("functionSplit", &archive) != B_OK)
526		return B_NO_MEMORY;
527
528	if (GuiSettingsUtils::ArchiveSplitView(archive, fImageSplitView) != B_OK)
529		return B_NO_MEMORY;
530	if (teamWindowSettings.AddMessage("imageSplit", &archive))
531		return B_NO_MEMORY;
532
533	if (GuiSettingsUtils::ArchiveSplitView(archive, fThreadSplitView) != B_OK)
534		return B_NO_MEMORY;
535	if (teamWindowSettings.AddMessage("threadSplit", &archive))
536		return B_NO_MEMORY;
537
538	if (fImageListView->SaveSettings(archive) != B_OK)
539		return B_NO_MEMORY;
540	if (teamWindowSettings.AddMessage("imageListView", &archive))
541		return B_NO_MEMORY;
542
543	if (fImageFunctionsView->SaveSettings(archive) != B_OK)
544		return B_NO_MEMORY;
545	if (teamWindowSettings.AddMessage("imageFunctionsView", &archive))
546		return B_NO_MEMORY;
547
548	if (fThreadListView->SaveSettings(archive) != B_OK)
549		return B_NO_MEMORY;
550	if (teamWindowSettings.AddMessage("threadListView", &archive))
551		return B_NO_MEMORY;
552
553	if (fVariablesView->SaveSettings(archive) != B_OK)
554		return B_NO_MEMORY;
555	if (teamWindowSettings.AddMessage("variablesView", &archive))
556		return B_NO_MEMORY;
557
558	if (fRegistersView->SaveSettings(archive) != B_OK)
559		return B_NO_MEMORY;
560	if (teamWindowSettings.AddMessage("registersView", &archive))
561		return B_NO_MEMORY;
562
563	if (fStackTraceView->SaveSettings(archive) != B_OK)
564		return B_NO_MEMORY;
565	if (teamWindowSettings.AddMessage("stackTraceView", &archive))
566		return B_NO_MEMORY;
567
568	if (fBreakpointsView->SaveSettings(archive) != B_OK)
569		return B_NO_MEMORY;
570	if (teamWindowSettings.AddMessage("breakpointsView", &archive))
571		return B_NO_MEMORY;
572
573	if (!settings->AddSettings("teamWindow", teamWindowSettings))
574		return B_NO_MEMORY;
575
576	return B_OK;
577}
578
579
580void
581TeamWindow::ThreadSelectionChanged(::Thread* thread)
582{
583	_SetActiveThread(thread);
584}
585
586
587void
588TeamWindow::ImageSelectionChanged(Image* image)
589{
590	_SetActiveImage(image);
591}
592
593
594void
595TeamWindow::StackFrameSelectionChanged(StackFrame* frame)
596{
597	_SetActiveStackFrame(frame);
598}
599
600
601void
602TeamWindow::FunctionSelectionChanged(FunctionInstance* function)
603{
604	// If the function wasn't already active, it was just selected by the user.
605	if (function != NULL && function != fActiveFunction)
606		fActiveSourceObject = ACTIVE_SOURCE_FUNCTION;
607
608	_SetActiveFunction(function);
609}
610
611
612void
613TeamWindow::BreakpointSelectionChanged(UserBreakpoint* breakpoint)
614{
615	_SetActiveBreakpoint(breakpoint);
616}
617
618
619void
620TeamWindow::SetBreakpointEnabledRequested(UserBreakpoint* breakpoint,
621	bool enabled)
622{
623	fListener->SetBreakpointEnabledRequested(breakpoint, enabled);
624}
625
626
627void
628TeamWindow::ClearBreakpointRequested(UserBreakpoint* breakpoint)
629{
630	fListener->ClearBreakpointRequested(breakpoint);
631}
632
633
634void
635TeamWindow::SetBreakpointRequested(target_addr_t address, bool enabled)
636{
637	fListener->SetBreakpointRequested(address, enabled);
638}
639
640
641void
642TeamWindow::ClearBreakpointRequested(target_addr_t address)
643{
644	fListener->ClearBreakpointRequested(address);
645}
646
647
648void
649TeamWindow::WatchpointSelectionChanged(Watchpoint* watchpoint)
650{
651	fBreakpointsView->SetBreakpoint(NULL, watchpoint);
652}
653
654
655void
656TeamWindow::SetWatchpointEnabledRequested(Watchpoint* watchpoint,
657	bool enabled)
658{
659	fListener->SetWatchpointEnabledRequested(watchpoint, enabled);
660}
661
662
663void
664TeamWindow::ClearWatchpointRequested(Watchpoint* watchpoint)
665{
666	fListener->ClearWatchpointRequested(watchpoint);
667}
668
669
670void
671TeamWindow::ValueNodeValueRequested(CpuState* cpuState,
672	ValueNodeContainer* container, ValueNode* valueNode)
673{
674	fListener->ValueNodeValueRequested(cpuState, container, valueNode);
675}
676
677
678void
679TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event)
680{
681	BMessage message(MSG_THREAD_STATE_CHANGED);
682	message.AddInt32("thread", event.GetThread()->ID());
683	PostMessage(&message);
684}
685
686
687void
688TeamWindow::ThreadCpuStateChanged(const Team::ThreadEvent& event)
689{
690	BMessage message(MSG_THREAD_CPU_STATE_CHANGED);
691	message.AddInt32("thread", event.GetThread()->ID());
692	PostMessage(&message);
693}
694
695
696void
697TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event)
698{
699	BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
700	message.AddInt32("thread", event.GetThread()->ID());
701	PostMessage(&message);
702}
703
704
705void
706TeamWindow::ImageDebugInfoChanged(const Team::ImageEvent& event)
707{
708	BMessage message(MSG_IMAGE_DEBUG_INFO_CHANGED);
709	message.AddInt32("image", event.GetImage()->ID());
710	PostMessage(&message);
711}
712
713
714void
715TeamWindow::UserBreakpointChanged(const Team::UserBreakpointEvent& event)
716{
717	BMessage message(MSG_USER_BREAKPOINT_CHANGED);
718	BReference<UserBreakpoint> breakpointReference(event.GetBreakpoint());
719	if (message.AddPointer("breakpoint", event.GetBreakpoint()) == B_OK
720		&& PostMessage(&message) == B_OK) {
721		breakpointReference.Detach();
722	}
723}
724
725
726void
727TeamWindow::WatchpointChanged(const Team::WatchpointEvent& event)
728{
729	BMessage message(MSG_WATCHPOINT_CHANGED);
730	BReference<Watchpoint> watchpointReference(event.GetWatchpoint());
731	if (message.AddPointer("watchpoint", event.GetWatchpoint()) == B_OK
732		&& PostMessage(&message) == B_OK) {
733		watchpointReference.Detach();
734	}
735}
736
737
738void
739TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event)
740{
741	BMessage message(MSG_DEBUG_REPORT_SAVED);
742	message.AddString("path", event.GetReportPath());
743	PostMessage(&message);
744}
745
746
747void
748TeamWindow::FunctionSourceCodeChanged(Function* function)
749{
750	TRACE_GUI("TeamWindow::FunctionSourceCodeChanged(%p): source: %p, "
751		"state: %d\n", function, function->GetSourceCode(),
752		function->SourceCodeState());
753
754	PostMessage(MSG_FUNCTION_SOURCE_CODE_CHANGED);
755}
756
757
758void
759TeamWindow::_Init()
760{
761	BScrollView* sourceScrollView;
762
763	BLayoutBuilder::Group<>(this, B_VERTICAL)
764		.Add(fMenuBar = new BMenuBar("Menu"))
765		.AddSplit(B_VERTICAL, 3.0f)
766			.GetSplitView(&fFunctionSplitView)
767			.SetInsets(4.0f, 4.0f, 4.0f, 4.0f)
768			.Add(fTabView = new BTabView("tab view"), 0.4f)
769			.AddGroup(B_VERTICAL, 4.0f)
770				.AddGroup(B_HORIZONTAL, 4.0f)
771					.Add(fRunButton = new BButton("Run"))
772					.Add(fStepOverButton = new BButton("Step Over"))
773					.Add(fStepIntoButton = new BButton("Step Into"))
774					.Add(fStepOutButton = new BButton("Step Out"))
775					.AddGlue()
776				.End()
777				.Add(fSourcePathView = new BStringView(
778					"source path",
779					"Source path unavailable."), 4.0f)
780				.AddSplit(B_HORIZONTAL, 3.0f)
781					.GetSplitView(&fSourceSplitView)
782					.Add(sourceScrollView = new BScrollView("source scroll",
783						NULL, 0, true, true), 3.0f)
784					.Add(fLocalsTabView = new BTabView("locals view"))
785				.End()
786			.End()
787		.End();
788
789	// add source view
790	sourceScrollView->SetTarget(fSourceView = SourceView::Create(fTeam, this));
791
792	// add threads tab
793	BSplitView* threadGroup = new BSplitView(B_HORIZONTAL);
794	threadGroup->SetName("Threads");
795	fTabView->AddTab(threadGroup);
796	BLayoutBuilder::Split<>(threadGroup)
797		.GetSplitView(&fThreadSplitView)
798		.Add(fThreadListView = ThreadListView::Create(fTeam, this))
799		.Add(fStackTraceView = StackTraceView::Create(this));
800
801	// add images tab
802	BSplitView* imagesGroup = new BSplitView(B_HORIZONTAL);
803	imagesGroup->SetName("Images");
804	fTabView->AddTab(imagesGroup);
805	BLayoutBuilder::Split<>(imagesGroup)
806		.GetSplitView(&fImageSplitView)
807		.Add(fImageListView = ImageListView::Create(fTeam, this))
808		.Add(fImageFunctionsView = ImageFunctionsView::Create(this));
809
810	// add breakpoints tab
811	BGroupView* breakpointsGroup = new BGroupView(B_HORIZONTAL, 4.0f);
812	breakpointsGroup->SetName("Breakpoints");
813	fTabView->AddTab(breakpointsGroup);
814	BLayoutBuilder::Group<>(breakpointsGroup)
815		.SetInsets(4.0f, 4.0f, 4.0f, 4.0f)
816		.Add(fBreakpointsView = BreakpointsView::Create(fTeam, this));
817
818	// add local variables tab
819	BView* tab = fVariablesView = VariablesView::Create(this);
820	fLocalsTabView->AddTab(tab);
821
822	// add registers tab
823	tab = fRegistersView = RegistersView::Create(fTeam->GetArchitecture());
824	fLocalsTabView->AddTab(tab);
825
826	fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN));
827	fStepOverButton->SetMessage(new BMessage(MSG_THREAD_STEP_OVER));
828	fStepIntoButton->SetMessage(new BMessage(MSG_THREAD_STEP_INTO));
829	fStepOutButton->SetMessage(new BMessage(MSG_THREAD_STEP_OUT));
830	fRunButton->SetTarget(this);
831	fStepOverButton->SetTarget(this);
832	fStepIntoButton->SetTarget(this);
833	fStepOutButton->SetTarget(this);
834
835	fSourcePathView->SetExplicitMinSize(BSize(100.0, B_SIZE_UNSET));
836	fSourcePathView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
837	BMessageFilter* filter = new(std::nothrow) PathViewMessageFilter(
838		BMessenger(this));
839	if (filter != NULL)
840		fSourcePathView->AddFilter(filter);
841
842	// add menus and menu items
843	BMenu* menu = new BMenu("File");
844	fMenuBar->AddItem(menu);
845	BMenuItem* item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED),
846		'Q');
847	menu->AddItem(item);
848	item->SetTarget(this);
849	menu = new BMenu("Edit");
850	fMenuBar->AddItem(menu);
851	item = new BMenuItem("Copy", new BMessage(B_COPY), 'C');
852	menu->AddItem(item);
853	item->SetTarget(this);
854	item = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A');
855	menu->AddItem(item);
856	item->SetTarget(this);
857	menu = new BMenu("Tools");
858	fMenuBar->AddItem(menu);
859	item = new BMenuItem("Save Debug Report",
860		new BMessage(MSG_CHOOSE_DEBUG_REPORT_LOCATION));
861	menu->AddItem(item);
862	item->SetTarget(this);
863	item = new BMenuItem("Inspect Memory",
864		new BMessage(MSG_SHOW_INSPECTOR_WINDOW), 'I');
865	menu->AddItem(item);
866	item->SetTarget(this);
867
868	AutoLocker< ::Team> locker(fTeam);
869	_UpdateRunButtons();
870}
871
872
873void
874TeamWindow::_SetActiveThread(::Thread* thread)
875{
876	if (thread == fActiveThread)
877		return;
878
879	if (fActiveThread != NULL)
880		fActiveThread->ReleaseReference();
881
882	fActiveThread = thread;
883
884	if (fActiveThread != NULL)
885		fActiveThread->AcquireReference();
886
887	AutoLocker< ::Team> locker(fTeam);
888	_UpdateRunButtons();
889
890	StackTrace* stackTrace = fActiveThread != NULL
891		? fActiveThread->GetStackTrace() : NULL;
892	BReference<StackTrace> stackTraceReference(stackTrace);
893		// hold a reference until we've set it
894
895	locker.Unlock();
896
897	fThreadListView->SetThread(fActiveThread);
898
899	_SetActiveStackTrace(stackTrace);
900	_UpdateCpuState();
901}
902
903
904void
905TeamWindow::_SetActiveImage(Image* image)
906{
907	if (image == fActiveImage)
908		return;
909
910	if (fActiveImage != NULL)
911		fActiveImage->ReleaseReference();
912
913	fActiveImage = image;
914
915	AutoLocker< ::Team> locker(fTeam);
916
917	ImageDebugInfo* imageDebugInfo = NULL;
918	BReference<ImageDebugInfo> imageDebugInfoReference;
919
920	if (fActiveImage != NULL) {
921		fActiveImage->AcquireReference();
922
923		imageDebugInfo = fActiveImage->GetImageDebugInfo();
924		imageDebugInfoReference.SetTo(imageDebugInfo);
925
926		// If the debug info is not loaded yet, request it.
927		if (fActiveImage->ImageDebugInfoState() == IMAGE_DEBUG_INFO_NOT_LOADED)
928			fListener->ImageDebugInfoRequested(fActiveImage);
929	}
930
931	locker.Unlock();
932
933	fImageListView->SetImage(fActiveImage);
934	fImageFunctionsView->SetImageDebugInfo(imageDebugInfo);
935}
936
937
938void
939TeamWindow::_SetActiveStackTrace(StackTrace* stackTrace)
940{
941	if (stackTrace == fActiveStackTrace)
942		return;
943
944	if (fActiveStackTrace != NULL)
945		fActiveStackTrace->ReleaseReference();
946
947	fActiveStackTrace = stackTrace;
948
949	if (fActiveStackTrace != NULL)
950		fActiveStackTrace->AcquireReference();
951
952	fStackTraceView->SetStackTrace(fActiveStackTrace);
953	fSourceView->SetStackTrace(fActiveStackTrace);
954
955	if (fActiveStackTrace != NULL)
956		_SetActiveStackFrame(fActiveStackTrace->FrameAt(0));
957}
958
959
960void
961TeamWindow::_SetActiveStackFrame(StackFrame* frame)
962{
963	if (frame == fActiveStackFrame)
964		return;
965
966	if (fActiveStackFrame != NULL) {
967		AutoLocker< ::Team> locker(fTeam);
968		fActiveStackFrame->RemoveListener(this);
969		locker.Unlock();
970
971		fActiveStackFrame->ReleaseReference();
972	}
973
974	fActiveStackFrame = frame;
975
976	if (fActiveStackFrame != NULL) {
977		fActiveStackFrame->AcquireReference();
978
979		AutoLocker< ::Team> locker(fTeam);
980		fActiveStackFrame->AddListener(this);
981		locker.Unlock();
982
983		fActiveSourceObject = ACTIVE_SOURCE_STACK_FRAME;
984
985		_SetActiveFunction(fActiveStackFrame->Function());
986	}
987
988	_UpdateCpuState();
989
990	fStackTraceView->SetStackFrame(fActiveStackFrame);
991	if (fActiveStackFrame != NULL)
992		fVariablesView->SetStackFrame(fActiveThread, fActiveStackFrame);
993	else
994		fVariablesView->SetStackFrame(NULL, NULL);
995	fSourceView->SetStackFrame(fActiveStackFrame);
996}
997
998
999void
1000TeamWindow::_SetActiveBreakpoint(UserBreakpoint* breakpoint)
1001{
1002	if (breakpoint == fActiveBreakpoint)
1003		return;
1004
1005	if (fActiveBreakpoint != NULL)
1006		fActiveBreakpoint->ReleaseReference();
1007
1008	fActiveBreakpoint = breakpoint;
1009
1010	if (fActiveBreakpoint != NULL) {
1011		fActiveBreakpoint->AcquireReference();
1012
1013		// get the breakpoint's function (more exactly: some function instance)
1014		AutoLocker< ::Team> locker(fTeam);
1015
1016		Function* function = fTeam->FunctionByID(
1017			breakpoint->Location().GetFunctionID());
1018		FunctionInstance* functionInstance = function != NULL
1019			? function->FirstInstance() : NULL;
1020		BReference<FunctionInstance> functionInstanceReference(
1021			functionInstance);
1022
1023		locker.Unlock();
1024
1025		fActiveSourceObject = ACTIVE_SOURCE_BREAKPOINT;
1026
1027		_SetActiveFunction(functionInstance);
1028
1029		// scroll to the breakpoint's source code line number (it is not done
1030		// automatically, if the active function remains the same)
1031		_ScrollToActiveFunction();
1032	}
1033
1034	fBreakpointsView->SetBreakpoint(fActiveBreakpoint, NULL);
1035}
1036
1037
1038void
1039TeamWindow::_SetActiveFunction(FunctionInstance* functionInstance)
1040{
1041	if (functionInstance == fActiveFunction)
1042		return;
1043
1044	AutoLocker< ::Team> locker(fTeam);
1045
1046	if (fActiveFunction != NULL) {
1047		fActiveFunction->GetFunction()->RemoveListener(this);
1048		fActiveFunction->ReleaseReference();
1049	}
1050
1051	// to avoid listener feedback problems, first unset the active function and
1052	// set the new image, if any
1053	locker.Unlock();
1054
1055	fActiveFunction = NULL;
1056
1057	if (functionInstance != NULL)
1058		_SetActiveImage(fTeam->ImageByAddress(functionInstance->Address()));
1059
1060	fActiveFunction = functionInstance;
1061
1062	locker.Lock();
1063
1064	SourceCode* sourceCode = NULL;
1065	BReference<SourceCode> sourceCodeReference;
1066
1067	if (fActiveFunction != NULL) {
1068		fActiveFunction->AcquireReference();
1069		fActiveFunction->GetFunction()->AddListener(this);
1070
1071		Function* function = fActiveFunction->GetFunction();
1072		sourceCode = function->GetSourceCode();
1073		if (sourceCode == NULL)
1074			sourceCode = fActiveFunction->GetSourceCode();
1075		sourceCodeReference.SetTo(sourceCode);
1076
1077		// If the source code is not loaded yet, request it.
1078		if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED)
1079			fListener->FunctionSourceCodeRequested(fActiveFunction);
1080	}
1081
1082	locker.Unlock();
1083
1084	_SetActiveSourceCode(sourceCode);
1085
1086	fImageFunctionsView->SetFunction(fActiveFunction);
1087
1088	locker.Lock();
1089
1090	// look if our current stack trace has a frame matching the selected
1091	// function. If so, set it to match.
1092	StackFrame* matchingFrame = NULL;
1093	BReference<StackFrame> frameRef;
1094
1095	if (fActiveStackTrace != NULL) {
1096		for (int32 i = 0; i < fActiveStackTrace->CountFrames(); i++) {
1097			StackFrame* frame = fActiveStackTrace->FrameAt(i);
1098			if (frame->Function() == fActiveFunction) {
1099				matchingFrame = frame;
1100				frameRef.SetTo(frame);
1101				break;
1102			}
1103		}
1104	}
1105
1106	locker.Unlock();
1107
1108	if (matchingFrame != NULL)
1109		_SetActiveStackFrame(matchingFrame);
1110}
1111
1112
1113void
1114TeamWindow::_SetActiveSourceCode(SourceCode* sourceCode)
1115{
1116	if (sourceCode == fActiveSourceCode) {
1117		_ScrollToActiveFunction();
1118		return;
1119	}
1120
1121	if (fActiveSourceCode != NULL)
1122		fActiveSourceCode->ReleaseReference();
1123
1124	fActiveSourceCode = sourceCode;
1125
1126	if (fActiveSourceCode != NULL)
1127		fActiveSourceCode->AcquireReference();
1128
1129	fSourceView->SetSourceCode(fActiveSourceCode);
1130
1131	_UpdateSourcePathState();
1132	_ScrollToActiveFunction();
1133}
1134
1135void
1136TeamWindow::_UpdateCpuState()
1137{
1138	// get the CPU state
1139	CpuState* cpuState = NULL;
1140	BReference<CpuState> cpuStateReference;
1141		// hold a reference until the register view has one
1142
1143	if (fActiveThread != NULL) {
1144		// Get the CPU state from the active stack frame or the thread directly.
1145		if (fActiveStackFrame == NULL) {
1146			AutoLocker< ::Team> locker(fTeam);
1147			cpuState = fActiveThread->GetCpuState();
1148			cpuStateReference.SetTo(cpuState);
1149			locker.Unlock();
1150		} else
1151			cpuState = fActiveStackFrame->GetCpuState();
1152	}
1153
1154	fRegistersView->SetCpuState(cpuState);
1155}
1156
1157
1158void
1159TeamWindow::_UpdateRunButtons()
1160{
1161	uint32 threadState = fActiveThread != NULL
1162		? fActiveThread->State() : THREAD_STATE_UNKNOWN;
1163
1164	switch (threadState) {
1165		case THREAD_STATE_UNKNOWN:
1166			fRunButton->SetEnabled(false);
1167			fStepOverButton->SetEnabled(false);
1168			fStepIntoButton->SetEnabled(false);
1169			fStepOutButton->SetEnabled(false);
1170			break;
1171		case THREAD_STATE_RUNNING:
1172			fRunButton->SetLabel("Debug");
1173			fRunButton->SetMessage(new BMessage(MSG_THREAD_STOP));
1174			fRunButton->SetEnabled(true);
1175			fStepOverButton->SetEnabled(false);
1176			fStepIntoButton->SetEnabled(false);
1177			fStepOutButton->SetEnabled(false);
1178			break;
1179		case THREAD_STATE_STOPPED:
1180			fRunButton->SetLabel("Run");
1181			fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN));
1182			fRunButton->SetEnabled(true);
1183			fStepOverButton->SetEnabled(true);
1184			fStepIntoButton->SetEnabled(true);
1185			fStepOutButton->SetEnabled(true);
1186			break;
1187	}
1188}
1189
1190
1191void
1192TeamWindow::_UpdateSourcePathState()
1193{
1194	LocatableFile* sourceFile = NULL;
1195	BString sourceText = "Source file unavailable.";
1196	BString truncatedText;
1197
1198	if (fActiveSourceCode != NULL) {
1199		sourceFile = fActiveFunction->GetFunctionDebugInfo()->SourceFile();
1200
1201		if (sourceFile != NULL && !sourceFile->GetLocatedPath(sourceText))
1202			sourceFile->GetPath(sourceText);
1203
1204		if (fActiveSourceCode->GetSourceFile() == NULL && sourceFile != NULL) {
1205			sourceText.Prepend("Click to locate source file '");
1206			sourceText += "'";
1207			truncatedText = sourceText;
1208			fSourcePathView->TruncateString(&truncatedText, B_TRUNCATE_MIDDLE,
1209				fSourcePathView->Bounds().Width());
1210		} else if (sourceFile != NULL) {
1211			sourceText.Prepend("File: ");
1212		}
1213	}
1214
1215	if (!truncatedText.IsEmpty() && truncatedText != sourceText) {
1216		fSourcePathView->SetToolTip(sourceText);
1217		fSourcePathView->SetText(truncatedText);
1218	} else
1219		fSourcePathView->SetText(sourceText);
1220}
1221
1222
1223void
1224TeamWindow::_ScrollToActiveFunction()
1225{
1226	// Scroll to the active function, if it has been selected manually.
1227	if (fActiveFunction == NULL || fActiveSourceCode == NULL)
1228		return;
1229
1230	switch (fActiveSourceObject) {
1231		case ACTIVE_SOURCE_FUNCTION:
1232			fSourceView->ScrollToAddress(fActiveFunction->Address());
1233			break;
1234		case ACTIVE_SOURCE_BREAKPOINT:
1235		{
1236			if (fActiveBreakpoint == NULL)
1237				break;
1238
1239			const UserBreakpointLocation& location
1240				= fActiveBreakpoint->Location();
1241			int32 line = location.GetSourceLocation().Line();
1242
1243			if (location.SourceFile() != NULL && line >= 0
1244				&& fActiveSourceCode->GetSourceFile()
1245					== location.SourceFile()) {
1246				fSourceView->ScrollToLine(line);
1247			} else {
1248				fSourceView->ScrollToAddress(
1249					fActiveFunction->Address()
1250						+ location.RelativeAddress());
1251			}
1252			break;
1253		}
1254		case ACTIVE_SOURCE_NONE:
1255		case ACTIVE_SOURCE_STACK_FRAME:
1256			break;
1257	}
1258}
1259
1260
1261void
1262TeamWindow::_HandleThreadStateChanged(thread_id threadID)
1263{
1264	AutoLocker< ::Team> locker(fTeam);
1265
1266	::Thread* thread = fTeam->ThreadByID(threadID);
1267	if (thread == NULL)
1268		return;
1269
1270	// If the thread has been stopped and we don't have an active thread yet
1271	// (or it isn't stopped), switch to this thread. Otherwise ignore the event.
1272	if (thread->State() == THREAD_STATE_STOPPED
1273		&& (fActiveThread == NULL
1274			|| (thread != fActiveThread
1275				&& fActiveThread->State() != THREAD_STATE_STOPPED))) {
1276		_SetActiveThread(thread);
1277	} else if (thread != fActiveThread) {
1278		// otherwise ignore the event, if the thread is not the active one
1279		return;
1280	}
1281
1282	// Switch to the threads tab view when the thread has stopped.
1283	if (thread->State() == THREAD_STATE_STOPPED)
1284		fTabView->Select(MAIN_TAB_INDEX_THREADS);
1285
1286	_UpdateRunButtons();
1287}
1288
1289
1290void
1291TeamWindow::_HandleCpuStateChanged(thread_id threadID)
1292{
1293	// We're only interested in the currently selected thread
1294	if (fActiveThread == NULL || threadID != fActiveThread->ID())
1295		return;
1296
1297	_UpdateCpuState();
1298}
1299
1300
1301void
1302TeamWindow::_HandleStackTraceChanged(thread_id threadID)
1303{
1304	// We're only interested in the currently selected thread
1305	if (fActiveThread == NULL || threadID != fActiveThread->ID())
1306		return;
1307
1308	AutoLocker< ::Team> locker(fTeam);
1309
1310	StackTrace* stackTrace = fActiveThread != NULL
1311		? fActiveThread->GetStackTrace() : NULL;
1312	BReference<StackTrace> stackTraceReference(stackTrace);
1313		// hold a reference until the register view has one
1314
1315	locker.Unlock();
1316
1317	_SetActiveStackTrace(stackTrace);
1318}
1319
1320
1321void
1322TeamWindow::_HandleImageDebugInfoChanged(image_id imageID)
1323{
1324	TRACE_GUI("TeamWindow::_HandleImageDebugInfoChanged(%" B_PRId32 ")\n",
1325		imageID);
1326
1327	// We're only interested in the currently selected thread
1328	if (fActiveImage == NULL || imageID != fActiveImage->ID())
1329		return;
1330
1331	AutoLocker< ::Team> locker(fTeam);
1332
1333	ImageDebugInfo* imageDebugInfo = fActiveImage != NULL
1334		? fActiveImage->GetImageDebugInfo() : NULL;
1335
1336	TRACE_GUI("  image debug info: %p\n", imageDebugInfo);
1337
1338	BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo);
1339		// hold a reference until we've set it
1340
1341	locker.Unlock();
1342
1343	fImageFunctionsView->SetImageDebugInfo(imageDebugInfo);
1344}
1345
1346
1347void
1348TeamWindow::_HandleSourceCodeChanged()
1349{
1350	// If we don't have an active function anymore, the message is obsolete.
1351	if (fActiveFunction == NULL)
1352		return;
1353
1354	// get a reference to the source code
1355	AutoLocker< ::Team> locker(fTeam);
1356
1357	SourceCode* sourceCode = fActiveFunction->GetFunction()->GetSourceCode();
1358	if (sourceCode == NULL)
1359		sourceCode = fActiveFunction->GetSourceCode();
1360
1361	BReference<SourceCode> sourceCodeReference(sourceCode);
1362
1363	locker.Unlock();
1364
1365	_SetActiveSourceCode(sourceCode);
1366}
1367
1368
1369void
1370TeamWindow::_HandleUserBreakpointChanged(UserBreakpoint* breakpoint)
1371{
1372	fSourceView->UserBreakpointChanged(breakpoint);
1373	fBreakpointsView->UserBreakpointChanged(breakpoint);
1374}
1375
1376
1377void
1378TeamWindow::_HandleWatchpointChanged(Watchpoint* watchpoint)
1379{
1380	fBreakpointsView->WatchpointChanged(watchpoint);
1381}
1382
1383
1384void
1385TeamWindow::_HandleResolveMissingSourceFile(entry_ref& locatedPath)
1386{
1387	if (fActiveFunction != NULL) {
1388		LocatableFile* sourceFile = fActiveFunction->GetFunctionDebugInfo()
1389			->SourceFile();
1390		if (sourceFile != NULL) {
1391			BString sourcePath;
1392			BString targetPath;
1393			sourceFile->GetPath(sourcePath);
1394			BPath path(&locatedPath);
1395			targetPath = path.Path();
1396			fListener->SourceEntryLocateRequested(sourcePath, targetPath);
1397			fListener->FunctionSourceCodeRequested(fActiveFunction);
1398		}
1399	}
1400}
1401
1402
1403status_t
1404TeamWindow::_SaveInspectorSettings(const BMessage* settings)
1405{
1406	if (fUiSettings.AddSettings("inspectorWindow", *settings) != B_OK)
1407		return B_NO_MEMORY;
1408
1409	return B_OK;
1410}
1411