1/*
2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "DebugReportGenerator.h"
8
9#include <cpu_type.h>
10
11#include <AutoLocker.h>
12#include <Entry.h>
13#include <File.h>
14#include <Path.h>
15#include <StringForSize.h>
16
17#include "Architecture.h"
18#include "AreaInfo.h"
19#include "AutoDeleter.h"
20#include "CpuState.h"
21#include "DebuggerInterface.h"
22#include "DisassembledCode.h"
23#include "FunctionInstance.h"
24#include "Image.h"
25#include "ImageDebugInfo.h"
26#include "MessageCodes.h"
27#include "Register.h"
28#include "SemaphoreInfo.h"
29#include "StackFrame.h"
30#include "StackTrace.h"
31#include "Statement.h"
32#include "StringUtils.h"
33#include "SystemInfo.h"
34#include "Team.h"
35#include "TeamDebugInfo.h"
36#include "Thread.h"
37#include "Type.h"
38#include "UiUtils.h"
39#include "UserInterface.h"
40#include "Value.h"
41#include "ValueLoader.h"
42#include "ValueLocation.h"
43#include "ValueNode.h"
44#include "ValueNodeManager.h"
45
46
47#define WRITE_AND_CHECK(output, data) \
48	{ \
49		ssize_t error = output.Write(data.String(), data.Length()); \
50		if (error < 0) \
51			return error; \
52	}
53
54
55DebugReportGenerator::DebugReportGenerator(::Team* team,
56	UserInterfaceListener* listener, DebuggerInterface* interface)
57	:
58	BLooper("DebugReportGenerator"),
59	fTeam(team),
60	fArchitecture(team->GetArchitecture()),
61	fDebuggerInterface(interface),
62	fTeamDataSem(-1),
63	fNodeManager(NULL),
64	fListener(listener),
65	fWaitingNode(NULL),
66	fCurrentBlock(NULL),
67	fBlockRetrievalStatus(B_OK),
68	fTraceWaitingThread(NULL),
69	fSourceWaitingFunction(NULL)
70{
71	fTeam->AddListener(this);
72	fArchitecture->AcquireReference();
73}
74
75
76DebugReportGenerator::~DebugReportGenerator()
77{
78	fTeam->RemoveListener(this);
79	fArchitecture->ReleaseReference();
80	if (fNodeManager != NULL) {
81		fNodeManager->RemoveListener(this);
82		fNodeManager->ReleaseReference();
83	}
84
85	if (fCurrentBlock != NULL)
86		fCurrentBlock->ReleaseReference();
87
88	if (fTeamDataSem >= 0)
89		delete_sem(fTeamDataSem);
90}
91
92
93status_t
94DebugReportGenerator::Init()
95{
96	fTeamDataSem = create_sem(0, "debug_controller_data_wait");
97	if (fTeamDataSem < B_OK)
98		return fTeamDataSem;
99
100	fNodeManager = new(std::nothrow) ValueNodeManager();
101	if (fNodeManager == NULL)
102		return B_NO_MEMORY;
103
104	fNodeManager->AddListener(this);
105
106	Run();
107
108	return B_OK;
109}
110
111
112DebugReportGenerator*
113DebugReportGenerator::Create(::Team* team, UserInterfaceListener* listener,
114	DebuggerInterface* interface)
115{
116	DebugReportGenerator* self = new DebugReportGenerator(team, listener,
117		interface);
118
119	try {
120		self->Init();
121	} catch (...) {
122		delete self;
123		throw;
124	}
125
126	return self;
127}
128
129
130status_t
131DebugReportGenerator::_GenerateReport(const char* outputPath)
132{
133	BFile file(outputPath, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
134	status_t result = file.InitCheck();
135	if (result != B_OK)
136		return result;
137
138	result = _GenerateReportHeader(file);
139	if (result != B_OK)
140		return result;
141
142	result = _DumpRunningThreads(file);
143	if (result != B_OK)
144		return result;
145
146	result = _DumpLoadedImages(file);
147	if (result != B_OK)
148		return result;
149
150	result = _DumpAreas(file);
151	if (result != B_OK)
152		return result;
153
154	result = _DumpSemaphores(file);
155	if (result != B_OK)
156		return result;
157
158	AutoLocker< ::Team> teamLocker(fTeam);
159	fTeam->NotifyDebugReportChanged(outputPath, B_OK);
160
161	return B_OK;
162}
163
164
165void
166DebugReportGenerator::MessageReceived(BMessage* message)
167{
168	switch (message->what) {
169		case MSG_GENERATE_DEBUG_REPORT:
170		{
171			entry_ref ref;
172			if (message->FindRef("target", &ref) == B_OK) {
173				BPath path(&ref);
174				status_t error = _GenerateReport(path.Path());
175				if (error != B_OK)
176					fTeam->NotifyDebugReportChanged(path.Path(), error);
177			}
178			break;
179		}
180
181		default:
182			BLooper::MessageReceived(message);
183			break;
184	}
185}
186
187
188void
189DebugReportGenerator::ThreadStackTraceChanged(const ::Team::ThreadEvent& event)
190{
191	if (fTraceWaitingThread == event.GetThread()) {
192		fTraceWaitingThread = NULL;
193		release_sem(fTeamDataSem);
194	}
195}
196
197
198void
199DebugReportGenerator::MemoryBlockRetrieved(TeamMemoryBlock* block)
200{
201	_HandleMemoryBlockRetrieved(block, B_OK);
202}
203
204
205void
206DebugReportGenerator::MemoryBlockRetrievalFailed(TeamMemoryBlock* block,
207	status_t result)
208{
209	_HandleMemoryBlockRetrieved(block, result);
210}
211
212
213void
214DebugReportGenerator::ValueNodeValueChanged(ValueNode* node)
215{
216	if (node == fWaitingNode) {
217		fWaitingNode = NULL;
218		release_sem(fTeamDataSem);
219	}
220}
221
222
223void
224DebugReportGenerator::FunctionSourceCodeChanged(Function* function)
225{
226	AutoLocker< ::Team> teamLocker(fTeam);
227	if (function == fSourceWaitingFunction) {
228		function_source_state state = function->SourceCodeState();
229
230		switch (state) {
231			case FUNCTION_SOURCE_LOADED:
232			case FUNCTION_SOURCE_SUPPRESSED:
233			case FUNCTION_SOURCE_UNAVAILABLE:
234			{
235				fSourceWaitingFunction->RemoveListener(this);
236				fSourceWaitingFunction = NULL;
237				release_sem(fTeamDataSem);
238				// fall through
239			}
240			default:
241				break;
242		}
243	}
244}
245
246status_t
247DebugReportGenerator::_GenerateReportHeader(BFile& _output)
248{
249	AutoLocker< ::Team> locker(fTeam);
250
251	BString data;
252	data.SetToFormat("Debug information for team %s (%" B_PRId32 "):\n",
253		fTeam->Name(), fTeam->ID());
254	WRITE_AND_CHECK(_output, data);
255
256	cpu_platform platform = B_CPU_UNKNOWN;
257	cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN;
258	uint32 cpuModel = 0;
259	uint32 topologyNodeCount = 0;
260	cpu_topology_node_info* topology = NULL;
261	get_cpu_topology_info(NULL, &topologyNodeCount);
262	if (topologyNodeCount != 0) {
263		topology = new(std::nothrow) cpu_topology_node_info[topologyNodeCount];
264		if (topology == NULL)
265			return B_NO_MEMORY;
266
267		BPrivate::ArrayDeleter<cpu_topology_node_info> deleter(topology);
268		get_cpu_topology_info(topology, &topologyNodeCount);
269
270		for (uint32 i = 0; i < topologyNodeCount; i++) {
271			switch (topology[i].type) {
272				case B_TOPOLOGY_ROOT:
273					platform = topology[i].data.root.platform;
274					break;
275
276				case B_TOPOLOGY_PACKAGE:
277					cpuVendor = topology[i].data.package.vendor;
278					break;
279
280				case B_TOPOLOGY_CORE:
281					cpuModel = topology[i].data.core.model;
282					break;
283
284				default:
285					break;
286			}
287		}
288	}
289
290	SystemInfo sysInfo;
291	if (fDebuggerInterface->GetSystemInfo(sysInfo) == B_OK) {
292		const system_info &info = sysInfo.GetSystemInfo();
293		const char* vendor = get_cpu_vendor_string(cpuVendor);
294		const char* model = get_cpu_model_string(platform, cpuVendor, cpuModel);
295
296		data.SetToFormat("CPU(s): %" B_PRId32 "x %s %s\n",
297			info.cpu_count, vendor != NULL ? vendor : "unknown",
298			model != NULL ? model : "unknown");
299		WRITE_AND_CHECK(_output, data);
300
301		char maxSize[32];
302		char usedSize[32];
303
304		data.SetToFormat("Memory: %s total, %s used\n",
305			BPrivate::string_for_size((int64)info.max_pages * B_PAGE_SIZE,
306				maxSize, sizeof(maxSize)),
307			BPrivate::string_for_size((int64)info.used_pages * B_PAGE_SIZE,
308				usedSize, sizeof(usedSize)));
309		WRITE_AND_CHECK(_output, data);
310
311		const utsname& name = sysInfo.GetSystemName();
312		data.SetToFormat("Haiku revision: %s (%s)\n", name.version,
313			name.machine);
314		WRITE_AND_CHECK(_output, data);
315	}
316
317	return B_OK;
318}
319
320
321status_t
322DebugReportGenerator::_DumpLoadedImages(BFile& _output)
323{
324	AutoLocker< ::Team> locker(fTeam);
325
326	BString data("\nLoaded Images:\n");
327	WRITE_AND_CHECK(_output, data);
328	BObjectList<Image> images;
329	for (ImageList::ConstIterator it = fTeam->Images().GetIterator();
330		 Image* image = it.Next();) {
331		 images.AddItem(image);
332	}
333
334	images.SortItems(&_CompareImages);
335
336	Image* image = NULL;
337	data.SetToFormat("\tID\t\tText Base\tText End\tData Base\tData"
338		" End\tType\tName\n\t");
339	WRITE_AND_CHECK(_output, data);
340	data.Truncate(0L);
341	data.Append('-', 80);
342	data.Append("\n");
343	WRITE_AND_CHECK(_output, data);
344	for (int32 i = 0; (image = images.ItemAt(i)) != NULL; i++) {
345		const ImageInfo& info = image->Info();
346		char buffer[32];
347		try {
348			target_addr_t textBase = info.TextBase();
349			target_addr_t dataBase = info.DataBase();
350
351			data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t"
352				"0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t"
353				"%-7s\t%s\n", info.ImageID(), textBase, textBase + info.TextSize(),
354				dataBase, dataBase + info.DataSize(),
355				UiUtils::ImageTypeToString(info.Type(), buffer,
356					sizeof(buffer)), info.Name().String());
357
358			WRITE_AND_CHECK(_output, data);
359		} catch (...) {
360			return B_NO_MEMORY;
361		}
362	}
363
364	return B_OK;
365}
366
367
368status_t
369DebugReportGenerator::_DumpAreas(BFile& _output)
370{
371	BObjectList<AreaInfo> areas(20, true);
372	status_t result = fDebuggerInterface->GetAreaInfos(areas);
373	if (result != B_OK)
374		return result;
375
376	areas.SortItems(&_CompareAreas);
377
378	BString data("\nAreas:\n");
379	WRITE_AND_CHECK(_output, data);
380	data.SetToFormat("\tID\t\tBase\t\tEnd\t\t\tSize (KiB)\tProtection\tLocking\t\t\tName\n\t");
381	WRITE_AND_CHECK(_output, data);
382	data.Truncate(0L);
383	data.Append('-', 80);
384	data.Append("\n");
385	WRITE_AND_CHECK(_output, data);
386	AreaInfo* info;
387	BString protectionBuffer;
388	char lockingBuffer[32];
389	for (int32 i = 0; (info = areas.ItemAt(i)) != NULL; i++) {
390		try {
391			data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t"
392				"0x%08" B_PRIx64 "\t%10" B_PRId64 "\t%-11s\t%-14s\t%s\n",
393				info->AreaID(), info->BaseAddress(), info->BaseAddress()
394					+ info->Size(), info->Size() / 1024,
395				UiUtils::AreaProtectionFlagsToString(info->Protection(),
396					protectionBuffer).String(),
397				UiUtils::AreaLockingFlagsToString(info->Lock(), lockingBuffer,
398					sizeof(lockingBuffer)), info->Name().String());
399
400			WRITE_AND_CHECK(_output, data);
401		} catch (...) {
402			return B_NO_MEMORY;
403		}
404	}
405
406	data = "\nProtection Flags: r - read, w - write, x - execute, "
407		"s - stack, o - overcommit, c - cloneable, S - shared, k - kernel\n";
408	WRITE_AND_CHECK(_output, data);
409
410	return B_OK;
411}
412
413
414status_t
415DebugReportGenerator::_DumpSemaphores(BFile& _output)
416{
417	BObjectList<SemaphoreInfo> semaphores(20, true);
418	status_t error = fDebuggerInterface->GetSemaphoreInfos(semaphores);
419	if (error != B_OK)
420		return error;
421
422	semaphores.SortItems(&_CompareSemaphores);
423
424	BString data = "\nSemaphores:\n";
425	WRITE_AND_CHECK(_output, data);
426	data.SetToFormat("\tID\t\tCount\tLast Holder\tName\n\t");
427	WRITE_AND_CHECK(_output, data);
428	data.Truncate(0L);
429	data.Append('-', 60);
430	data.Append("\n");
431	WRITE_AND_CHECK(_output, data);
432	SemaphoreInfo* info;
433	for (int32 i = 0; (info = semaphores.ItemAt(i)) != NULL; i++) {
434		try {
435			data.SetToFormat("\t%" B_PRId32 "\t%5" B_PRId32 "\t%11" B_PRId32
436				"\t%s\n", info->SemID(), info->Count(),
437				info->LatestHolder(), info->Name().String());
438
439			WRITE_AND_CHECK(_output, data);
440		} catch (...) {
441			return B_NO_MEMORY;
442		}
443	}
444
445	return B_OK;
446}
447
448
449status_t
450DebugReportGenerator::_DumpRunningThreads(BFile& _output)
451{
452	AutoLocker< ::Team> locker(fTeam);
453
454	BString data("\nActive Threads:\n");
455	WRITE_AND_CHECK(_output, data);
456	BObjectList< ::Thread> threads;
457	::Thread* thread;
458	for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
459		  (thread = it.Next());) {
460		 threads.AddItem(thread);
461		 thread->AcquireReference();
462	}
463
464	status_t error = B_OK;
465	threads.SortItems(&_CompareThreads);
466	for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++) {
467		try {
468			data.SetToFormat("\tthread %" B_PRId32 ": %s %s\n", thread->ID(),
469					thread->Name(), thread->IsMainThread()
470						? "(main)" : "");
471			WRITE_AND_CHECK(_output, data);
472
473			if (thread->State() == THREAD_STATE_STOPPED) {
474				data.SetToFormat("\t\tstate: %s",
475					UiUtils::ThreadStateToString(thread->State(),
476							thread->StoppedReason()));
477				const BString& stoppedInfo = thread->StoppedReasonInfo();
478				if (stoppedInfo.Length() != 0)
479					data << " (" << stoppedInfo << ")";
480				data << "\n\n";
481				WRITE_AND_CHECK(_output, data);
482
483				// we need to release our lock on the team here
484				// since we might need to block and wait
485				// on the stack trace.
486				locker.Unlock();
487				error = _DumpDebuggedThreadInfo(_output, thread);
488				locker.Lock();
489				if (error != B_OK)
490					break;
491			}
492		} catch (...) {
493			error = B_NO_MEMORY;
494		}
495	}
496
497	for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++)
498		thread->ReleaseReference();
499
500	return error;
501}
502
503
504status_t
505DebugReportGenerator::_DumpDebuggedThreadInfo(BFile& _output,
506	::Thread* thread)
507{
508	AutoLocker< ::Team> locker;
509	if (thread->State() != THREAD_STATE_STOPPED)
510		return B_OK;
511
512	status_t error;
513	StackTrace* trace = NULL;
514	for (;;) {
515		trace = thread->GetStackTrace();
516		if (trace != NULL)
517			break;
518
519		locker.Unlock();
520		fTraceWaitingThread = thread;
521		do {
522			error = acquire_sem(fTeamDataSem);
523		} while (error == B_INTERRUPTED);
524
525		if (error != B_OK)
526			return error;
527
528		locker.Lock();
529	}
530
531	BString data("\t\tFrame\t\tIP\t\t\tFunction Name\n");
532	WRITE_AND_CHECK(_output, data);
533	data = "\t\t-----------------------------------------------\n";
534	WRITE_AND_CHECK(_output, data);
535	for (int32 i = 0; StackFrame* frame = trace->FrameAt(i); i++) {
536		char functionName[512];
537		BString sourcePath;
538
539		target_addr_t ip = frame->InstructionPointer();
540		Image* image = fTeam->ImageByAddress(ip);
541		FunctionInstance* functionInstance = NULL;
542		if (image != NULL && image->ImageDebugInfoState()
543				== IMAGE_DEBUG_INFO_LOADED) {
544			ImageDebugInfo* info = image->GetImageDebugInfo();
545			functionInstance = info->FunctionAtAddress(ip);
546		}
547
548		if (functionInstance != NULL) {
549			Function* function = functionInstance->GetFunction();
550			if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED
551				&& functionInstance->SourceCodeState()
552					== FUNCTION_SOURCE_NOT_LOADED) {
553				fSourceWaitingFunction = function;
554				fSourceWaitingFunction->AddListener(this);
555				fListener->FunctionSourceCodeRequested(functionInstance);
556
557				locker.Unlock();
558
559				do {
560					error = acquire_sem(fTeamDataSem);
561				} while (error == B_INTERRUPTED);
562
563				if (error != B_OK)
564					return error;
565
566				locker.Lock();
567			}
568		}
569
570		Statement* statement;
571		if (fTeam->GetStatementAtAddress(ip,
572				functionInstance, statement) == B_OK) {
573			BReference<Statement> statementReference(statement, true);
574
575			int32 line = statement->StartSourceLocation().Line();
576			LocatableFile* sourceFile = functionInstance->GetFunction()
577				->SourceFile();
578			if (sourceFile != NULL) {
579				sourceFile->GetPath(sourcePath);
580				sourcePath.SetToFormat("(%s:%" B_PRId32 ")",
581					sourcePath.String(), line);
582			}
583		}
584
585
586		data.SetToFormat("\t\t%#08" B_PRIx64 "\t%#08" B_PRIx64 "\t%s %s\n",
587			frame->FrameAddress(), ip, UiUtils::FunctionNameForFrame(
588				frame, functionName, sizeof(functionName)),
589				sourcePath.String());
590
591		WRITE_AND_CHECK(_output, data);
592
593		// only dump the topmost frame
594		if (i == 0) {
595			locker.Unlock();
596			error = _DumpFunctionDisassembly(_output,
597				frame->InstructionPointer());
598			if (error != B_OK)
599				return error;
600			error = _DumpStackFrameMemory(_output, thread->GetCpuState(),
601				frame->FrameAddress(), thread->GetTeam()->GetArchitecture()
602					->StackGrowthDirection());
603			if (error != B_OK)
604				return error;
605			locker.Lock();
606		}
607
608		if (frame->CountParameters() == 0 && frame->CountLocalVariables() == 0)
609			continue;
610
611		data = "\t\t\tVariables:\n";
612		WRITE_AND_CHECK(_output, data);
613		error = fNodeManager->SetStackFrame(thread, frame);
614		if (error != B_OK)
615			continue;
616
617		ValueNodeContainer* container = fNodeManager->GetContainer();
618		AutoLocker<ValueNodeContainer> containerLocker(container);
619		for (int32 i = 0; i < container->CountChildren(); i++) {
620			data.Truncate(0L);
621			ValueNodeChild* child = container->ChildAt(i);
622			containerLocker.Unlock();
623			_ResolveValueIfNeeded(child->Node(), frame, 1);
624			containerLocker.Lock();
625			UiUtils::PrintValueNodeGraph(data, child, 3, 1);
626			WRITE_AND_CHECK(_output, data);
627		}
628		data = "\n";
629		WRITE_AND_CHECK(_output, data);
630	}
631
632	data = "\n\t\tRegisters:\n";
633	WRITE_AND_CHECK(_output, data);
634
635	CpuState* state = thread->GetCpuState();
636	BVariant value;
637	const Register* reg = NULL;
638	for (int32 i = 0; i < fArchitecture->CountRegisters(); i++) {
639		reg = fArchitecture->Registers() + i;
640		state->GetRegisterValue(reg, value);
641
642		if (reg->Format() == REGISTER_FORMAT_SIMD) {
643			data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(),
644				UiUtils::FormatSIMDValue(value, reg->BitSize(),
645					SIMD_RENDER_FORMAT_INT16, data).String());
646		} else {
647			char buffer[64];
648			data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(),
649				UiUtils::VariantToString(value, buffer, sizeof(buffer)));
650		}
651
652		WRITE_AND_CHECK(_output, data);
653	}
654
655	return B_OK;
656}
657
658
659status_t
660DebugReportGenerator::_DumpFunctionDisassembly(BFile& _output,
661	target_addr_t instructionPointer)
662{
663	AutoLocker< ::Team> teamLocker(fTeam);
664	BString data;
665	FunctionInstance* instance = NULL;
666	Image* image = fTeam->ImageByAddress(instructionPointer);
667	if (image == NULL) {
668		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
669			B_PRIx64 ": address not contained in any valid image.\n",
670			instructionPointer);
671		WRITE_AND_CHECK(_output, data);
672		return B_OK;
673	}
674
675	ImageDebugInfo* info = image->GetImageDebugInfo();
676	if (info == NULL) {
677		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
678			B_PRIx64 ": no debug info available for image '%s'.\n",
679			instructionPointer,	image->Name().String());
680		WRITE_AND_CHECK(_output, data);
681		return B_OK;
682	}
683
684	instance = info->FunctionAtAddress(instructionPointer);
685	if (instance == NULL) {
686		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
687			B_PRIx64 ": address does not point to a function.\n",
688			instructionPointer);
689		WRITE_AND_CHECK(_output, data);
690		return B_OK;
691	}
692
693	Statement* statement = NULL;
694	DisassembledCode* code = instance->GetSourceCode();
695	BReference<DisassembledCode> codeReference;
696	if (code == NULL) {
697		status_t error = fTeam->DebugInfo()->DisassembleFunction(instance,
698			code);
699		if (error != B_OK) {
700			data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
701				B_PRIx64 ": %s.\n", instructionPointer, strerror(error));
702			WRITE_AND_CHECK(_output, data);
703			return B_OK;
704		}
705
706		codeReference.SetTo(code, true);
707	} else
708		codeReference.SetTo(code);
709
710	statement = code->StatementAtAddress(instructionPointer);
711	if (statement == NULL) {
712		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
713			B_PRIx64 ": address does not map to a valid instruction.\n",
714			instructionPointer);
715		WRITE_AND_CHECK(_output, data);
716		return B_OK;
717	}
718
719	SourceLocation location = statement->StartSourceLocation();
720
721	data = "\t\t\tDisassembly:\n";
722	WRITE_AND_CHECK(_output, data);
723	for (int32 i = 0; i <= location.Line(); i++) {
724		data = "\t\t\t\t";
725		data << code->LineAt(i);
726		if (i == location.Line())
727			data << " <--";
728		data << "\n";
729		WRITE_AND_CHECK(_output, data);
730	}
731	data = "\n";
732	WRITE_AND_CHECK(_output, data);
733
734	return B_OK;
735}
736
737
738status_t
739DebugReportGenerator::_DumpStackFrameMemory(BFile& _output,
740	CpuState* state, target_addr_t framePointer, uint8 stackDirection)
741{
742	target_addr_t startAddress;
743	target_addr_t endAddress;
744	if (stackDirection == STACK_GROWTH_DIRECTION_POSITIVE) {
745		startAddress = framePointer;
746		endAddress = state->StackPointer();
747	} else {
748		startAddress = state->StackPointer();
749		endAddress = framePointer;
750	}
751
752	if (endAddress <= startAddress)
753		return B_OK;
754
755	if (fCurrentBlock == NULL || !fCurrentBlock->Contains(startAddress)) {
756		status_t error;
757		fListener->InspectRequested(startAddress, this);
758		do {
759			error = acquire_sem(fTeamDataSem);
760		} while (error == B_INTERRUPTED);
761
762		if (error != B_OK)
763			return error;
764	}
765
766	BString data("\t\t\tFrame memory:\n");
767	WRITE_AND_CHECK(_output, data);
768	if (fBlockRetrievalStatus == B_OK) {
769		data.Truncate(0L);
770		UiUtils::DumpMemory(data, 4, fCurrentBlock, startAddress, 1, 16,
771			endAddress - startAddress);
772		WRITE_AND_CHECK(_output, data);
773	} else {
774		data.SetToFormat("\t\t\t\tUnavailable (%s)\n", strerror(
775				fBlockRetrievalStatus));
776		WRITE_AND_CHECK(_output, data);
777	}
778
779	return B_OK;
780}
781
782
783status_t
784DebugReportGenerator::_ResolveValueIfNeeded(ValueNode* node, StackFrame* frame,
785	int32 maxDepth)
786{
787	status_t result = B_OK;
788	if (node->LocationAndValueResolutionState() == VALUE_NODE_UNRESOLVED) {
789		fWaitingNode = node;
790		fListener->ValueNodeValueRequested(frame->GetCpuState(),
791			fNodeManager->GetContainer(), node);
792		do {
793			result = acquire_sem(fTeamDataSem);
794		} while (result == B_INTERRUPTED);
795	}
796
797	if (node->LocationAndValueResolutionState() == B_OK && maxDepth > 0) {
798		AutoLocker<ValueNodeContainer> containerLocker(
799			fNodeManager->GetContainer());
800		for (int32 i = 0; i < node->CountChildren(); i++) {
801			ValueNodeChild* child = node->ChildAt(i);
802			containerLocker.Unlock();
803			result = fNodeManager->AddChildNodes(child);
804			if (result != B_OK)
805				continue;
806
807			// since in the case of a pointer to a compound we hide
808			// the intervening compound, don't consider the hidden node
809			// a level for the purposes of depth traversal
810			if (node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
811				&& child->GetType()->ResolveRawType(false)->Kind()
812					== TYPE_COMPOUND) {
813				_ResolveValueIfNeeded(child->Node(), frame, maxDepth);
814			} else
815				_ResolveValueIfNeeded(child->Node(), frame, maxDepth - 1);
816			containerLocker.Lock();
817		}
818	}
819
820	return result;
821}
822
823
824void
825DebugReportGenerator::_HandleMemoryBlockRetrieved(TeamMemoryBlock* block,
826	status_t result)
827{
828	if (fCurrentBlock != NULL) {
829		fCurrentBlock->ReleaseReference();
830		fCurrentBlock = NULL;
831	}
832
833	fBlockRetrievalStatus = result;
834
835	fCurrentBlock = block;
836	release_sem(fTeamDataSem);
837}
838
839
840
841/*static*/ int
842DebugReportGenerator::_CompareAreas(const AreaInfo* a, const AreaInfo* b)
843{
844	if (a->BaseAddress() < b->BaseAddress())
845		return -1;
846
847	return 1;
848}
849
850
851/*static*/ int
852DebugReportGenerator::_CompareImages(const Image* a, const Image* b)
853{
854	if (a->Info().TextBase() < b->Info().TextBase())
855		return -1;
856
857	return 1;
858}
859
860
861/*static*/ int
862DebugReportGenerator::_CompareSemaphores(const SemaphoreInfo* a,
863	const SemaphoreInfo* b)
864{
865	if (a->SemID() < b->SemID())
866		return -1;
867
868	return 1;
869}
870
871
872/*static*/ int
873DebugReportGenerator::_CompareThreads(const ::Thread* a,
874	const ::Thread* b)
875{
876	// sort stopped threads last, otherwise sort by thread ID
877	if (a->State() == b->State())
878		return a->ID() < b->ID() ? -1 : 1;
879
880	if (a->State() == THREAD_STATE_STOPPED && b->State()
881			!= THREAD_STATE_STOPPED) {
882		return 1;
883	}
884
885	return -1;
886}
887