xref: /haiku/src/kits/debugger/controllers/DebugReportGenerator.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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 
55 DebugReportGenerator::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 
76 DebugReportGenerator::~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 
93 status_t
94 DebugReportGenerator::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 
112 DebugReportGenerator*
113 DebugReportGenerator::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 
130 status_t
131 DebugReportGenerator::_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 
165 void
166 DebugReportGenerator::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 
188 void
189 DebugReportGenerator::ThreadStackTraceChanged(const ::Team::ThreadEvent& event)
190 {
191 	if (fTraceWaitingThread == event.GetThread()) {
192 		fTraceWaitingThread = NULL;
193 		release_sem(fTeamDataSem);
194 	}
195 }
196 
197 
198 void
199 DebugReportGenerator::MemoryBlockRetrieved(TeamMemoryBlock* block)
200 {
201 	_HandleMemoryBlockRetrieved(block, B_OK);
202 }
203 
204 
205 void
206 DebugReportGenerator::MemoryBlockRetrievalFailed(TeamMemoryBlock* block,
207 	status_t result)
208 {
209 	_HandleMemoryBlockRetrieved(block, result);
210 }
211 
212 
213 void
214 DebugReportGenerator::ValueNodeValueChanged(ValueNode* node)
215 {
216 	if (node == fWaitingNode) {
217 		fWaitingNode = NULL;
218 		release_sem(fTeamDataSem);
219 	}
220 }
221 
222 
223 void
224 DebugReportGenerator::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 
246 status_t
247 DebugReportGenerator::_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 		data.SetToFormat("CPU(s): %" B_PRId32 "x %s %s\n",
294 			info.cpu_count, get_cpu_vendor_string(cpuVendor),
295 			get_cpu_model_string(platform, cpuVendor, cpuModel));
296 		WRITE_AND_CHECK(_output, data);
297 
298 		char maxSize[32];
299 		char usedSize[32];
300 
301 		data.SetToFormat("Memory: %s total, %s used\n",
302 			BPrivate::string_for_size((int64)info.max_pages * B_PAGE_SIZE,
303 				maxSize, sizeof(maxSize)),
304 			BPrivate::string_for_size((int64)info.used_pages * B_PAGE_SIZE,
305 				usedSize, sizeof(usedSize)));
306 		WRITE_AND_CHECK(_output, data);
307 
308 		const utsname& name = sysInfo.GetSystemName();
309 		data.SetToFormat("Haiku revision: %s (%s)\n", name.version,
310 			name.machine);
311 		WRITE_AND_CHECK(_output, data);
312 	}
313 
314 	return B_OK;
315 }
316 
317 
318 status_t
319 DebugReportGenerator::_DumpLoadedImages(BFile& _output)
320 {
321 	AutoLocker< ::Team> locker(fTeam);
322 
323 	BString data("\nLoaded Images:\n");
324 	WRITE_AND_CHECK(_output, data);
325 	BObjectList<Image> images;
326 	for (ImageList::ConstIterator it = fTeam->Images().GetIterator();
327 		 Image* image = it.Next();) {
328 		 images.AddItem(image);
329 	}
330 
331 	images.SortItems(&_CompareImages);
332 
333 	Image* image = NULL;
334 	data.SetToFormat("\tID\t\tText Base\tText End\tData Base\tData"
335 		" End\tType\tName\n\t");
336 	WRITE_AND_CHECK(_output, data);
337 	data.Truncate(0L);
338 	data.Append('-', 80);
339 	data.Append("\n");
340 	WRITE_AND_CHECK(_output, data);
341 	for (int32 i = 0; (image = images.ItemAt(i)) != NULL; i++) {
342 		const ImageInfo& info = image->Info();
343 		char buffer[32];
344 		try {
345 			target_addr_t textBase = info.TextBase();
346 			target_addr_t dataBase = info.DataBase();
347 
348 			data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t"
349 				"0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t"
350 				"%-7s\t%s\n", info.ImageID(), textBase, textBase + info.TextSize(),
351 				dataBase, dataBase + info.DataSize(),
352 				UiUtils::ImageTypeToString(info.Type(), buffer,
353 					sizeof(buffer)), info.Name().String());
354 
355 			WRITE_AND_CHECK(_output, data);
356 		} catch (...) {
357 			return B_NO_MEMORY;
358 		}
359 	}
360 
361 	return B_OK;
362 }
363 
364 
365 status_t
366 DebugReportGenerator::_DumpAreas(BFile& _output)
367 {
368 	BObjectList<AreaInfo> areas(20, true);
369 	status_t result = fDebuggerInterface->GetAreaInfos(areas);
370 	if (result != B_OK)
371 		return result;
372 
373 	areas.SortItems(&_CompareAreas);
374 
375 	BString data("\nAreas:\n");
376 	WRITE_AND_CHECK(_output, data);
377 	data.SetToFormat("\tID\t\tBase\t\tEnd\t\t\tSize (KiB)\tProtection\tLocking\t\t\tName\n\t");
378 	WRITE_AND_CHECK(_output, data);
379 	data.Truncate(0L);
380 	data.Append('-', 80);
381 	data.Append("\n");
382 	WRITE_AND_CHECK(_output, data);
383 	AreaInfo* info;
384 	BString protectionBuffer;
385 	char lockingBuffer[32];
386 	for (int32 i = 0; (info = areas.ItemAt(i)) != NULL; i++) {
387 		try {
388 			data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t"
389 				"0x%08" B_PRIx64 "\t%10" B_PRId64 "\t%-11s\t%-14s\t%s\n",
390 				info->AreaID(), info->BaseAddress(), info->BaseAddress()
391 					+ info->Size(), info->Size() / 1024,
392 				UiUtils::AreaProtectionFlagsToString(info->Protection(),
393 					protectionBuffer).String(),
394 				UiUtils::AreaLockingFlagsToString(info->Lock(), lockingBuffer,
395 					sizeof(lockingBuffer)), info->Name().String());
396 
397 			WRITE_AND_CHECK(_output, data);
398 		} catch (...) {
399 			return B_NO_MEMORY;
400 		}
401 	}
402 
403 	data = "\nProtection Flags: r - read, w - write, x - execute, "
404 		"s - stack, o - overcommit, c - cloneable, S - shared, k - kernel\n";
405 	WRITE_AND_CHECK(_output, data);
406 
407 	return B_OK;
408 }
409 
410 
411 status_t
412 DebugReportGenerator::_DumpSemaphores(BFile& _output)
413 {
414 	BObjectList<SemaphoreInfo> semaphores(20, true);
415 	status_t error = fDebuggerInterface->GetSemaphoreInfos(semaphores);
416 	if (error != B_OK)
417 		return error;
418 
419 	semaphores.SortItems(&_CompareSemaphores);
420 
421 	BString data = "\nSemaphores:\n";
422 	WRITE_AND_CHECK(_output, data);
423 	data.SetToFormat("\tID\t\tCount\tLast Holder\tName\n\t");
424 	WRITE_AND_CHECK(_output, data);
425 	data.Truncate(0L);
426 	data.Append('-', 60);
427 	data.Append("\n");
428 	WRITE_AND_CHECK(_output, data);
429 	SemaphoreInfo* info;
430 	for (int32 i = 0; (info = semaphores.ItemAt(i)) != NULL; i++) {
431 		try {
432 			data.SetToFormat("\t%" B_PRId32 "\t%5" B_PRId32 "\t%11" B_PRId32
433 				"\t%s\n", info->SemID(), info->Count(),
434 				info->LatestHolder(), info->Name().String());
435 
436 			WRITE_AND_CHECK(_output, data);
437 		} catch (...) {
438 			return B_NO_MEMORY;
439 		}
440 	}
441 
442 	return B_OK;
443 }
444 
445 
446 status_t
447 DebugReportGenerator::_DumpRunningThreads(BFile& _output)
448 {
449 	AutoLocker< ::Team> locker(fTeam);
450 
451 	BString data("\nActive Threads:\n");
452 	WRITE_AND_CHECK(_output, data);
453 	BObjectList< ::Thread> threads;
454 	::Thread* thread;
455 	for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
456 		  (thread = it.Next());) {
457 		 threads.AddItem(thread);
458 		 thread->AcquireReference();
459 	}
460 
461 	status_t error = B_OK;
462 	threads.SortItems(&_CompareThreads);
463 	for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++) {
464 		try {
465 			data.SetToFormat("\tthread %" B_PRId32 ": %s %s\n", thread->ID(),
466 					thread->Name(), thread->IsMainThread()
467 						? "(main)" : "");
468 			WRITE_AND_CHECK(_output, data);
469 
470 			if (thread->State() == THREAD_STATE_STOPPED) {
471 				data.SetToFormat("\t\tstate: %s",
472 					UiUtils::ThreadStateToString(thread->State(),
473 							thread->StoppedReason()));
474 				const BString& stoppedInfo = thread->StoppedReasonInfo();
475 				if (stoppedInfo.Length() != 0)
476 					data << " (" << stoppedInfo << ")";
477 				data << "\n\n";
478 				WRITE_AND_CHECK(_output, data);
479 
480 				// we need to release our lock on the team here
481 				// since we might need to block and wait
482 				// on the stack trace.
483 				locker.Unlock();
484 				error = _DumpDebuggedThreadInfo(_output, thread);
485 				locker.Lock();
486 				if (error != B_OK)
487 					break;
488 			}
489 		} catch (...) {
490 			error = B_NO_MEMORY;
491 		}
492 	}
493 
494 	for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++)
495 		thread->ReleaseReference();
496 
497 	return error;
498 }
499 
500 
501 status_t
502 DebugReportGenerator::_DumpDebuggedThreadInfo(BFile& _output,
503 	::Thread* thread)
504 {
505 	AutoLocker< ::Team> locker;
506 	if (thread->State() != THREAD_STATE_STOPPED)
507 		return B_OK;
508 
509 	status_t error;
510 	StackTrace* trace = NULL;
511 	for (;;) {
512 		trace = thread->GetStackTrace();
513 		if (trace != NULL)
514 			break;
515 
516 		locker.Unlock();
517 		fTraceWaitingThread = thread;
518 		do {
519 			error = acquire_sem(fTeamDataSem);
520 		} while (error == B_INTERRUPTED);
521 
522 		if (error != B_OK)
523 			return error;
524 
525 		locker.Lock();
526 	}
527 
528 	BString data("\t\tFrame\t\tIP\t\t\tFunction Name\n");
529 	WRITE_AND_CHECK(_output, data);
530 	data = "\t\t-----------------------------------------------\n";
531 	WRITE_AND_CHECK(_output, data);
532 	for (int32 i = 0; StackFrame* frame = trace->FrameAt(i); i++) {
533 		char functionName[512];
534 		BString sourcePath;
535 
536 		target_addr_t ip = frame->InstructionPointer();
537 		Image* image = fTeam->ImageByAddress(ip);
538 		FunctionInstance* functionInstance = NULL;
539 		if (image != NULL && image->ImageDebugInfoState()
540 				== IMAGE_DEBUG_INFO_LOADED) {
541 			ImageDebugInfo* info = image->GetImageDebugInfo();
542 			functionInstance = info->FunctionAtAddress(ip);
543 		}
544 
545 		if (functionInstance != NULL) {
546 			Function* function = functionInstance->GetFunction();
547 			if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED
548 				&& functionInstance->SourceCodeState()
549 					== FUNCTION_SOURCE_NOT_LOADED) {
550 				fSourceWaitingFunction = function;
551 				fSourceWaitingFunction->AddListener(this);
552 				fListener->FunctionSourceCodeRequested(functionInstance);
553 
554 				locker.Unlock();
555 
556 				do {
557 					error = acquire_sem(fTeamDataSem);
558 				} while (error == B_INTERRUPTED);
559 
560 				if (error != B_OK)
561 					return error;
562 
563 				locker.Lock();
564 			}
565 		}
566 
567 		Statement* statement;
568 		if (fTeam->GetStatementAtAddress(ip,
569 				functionInstance, statement) == B_OK) {
570 			BReference<Statement> statementReference(statement, true);
571 
572 			int32 line = statement->StartSourceLocation().Line();
573 			LocatableFile* sourceFile = functionInstance->GetFunction()
574 				->SourceFile();
575 			if (sourceFile != NULL) {
576 				sourceFile->GetPath(sourcePath);
577 				sourcePath.SetToFormat("(%s:%" B_PRId32 ")",
578 					sourcePath.String(), line);
579 			}
580 		}
581 
582 
583 		data.SetToFormat("\t\t%#08" B_PRIx64 "\t%#08" B_PRIx64 "\t%s %s\n",
584 			frame->FrameAddress(), ip, UiUtils::FunctionNameForFrame(
585 				frame, functionName, sizeof(functionName)),
586 				sourcePath.String());
587 
588 		WRITE_AND_CHECK(_output, data);
589 
590 		// only dump the topmost frame
591 		if (i == 0) {
592 			locker.Unlock();
593 			error = _DumpFunctionDisassembly(_output,
594 				frame->InstructionPointer());
595 			if (error != B_OK)
596 				return error;
597 			error = _DumpStackFrameMemory(_output, thread->GetCpuState(),
598 				frame->FrameAddress(), thread->GetTeam()->GetArchitecture()
599 					->StackGrowthDirection());
600 			if (error != B_OK)
601 				return error;
602 			locker.Lock();
603 		}
604 
605 		if (frame->CountParameters() == 0 && frame->CountLocalVariables() == 0)
606 			continue;
607 
608 		data = "\t\t\tVariables:\n";
609 		WRITE_AND_CHECK(_output, data);
610 		error = fNodeManager->SetStackFrame(thread, frame);
611 		if (error != B_OK)
612 			continue;
613 
614 		ValueNodeContainer* container = fNodeManager->GetContainer();
615 		AutoLocker<ValueNodeContainer> containerLocker(container);
616 		for (int32 i = 0; i < container->CountChildren(); i++) {
617 			data.Truncate(0L);
618 			ValueNodeChild* child = container->ChildAt(i);
619 			containerLocker.Unlock();
620 			_ResolveValueIfNeeded(child->Node(), frame, 1);
621 			containerLocker.Lock();
622 			UiUtils::PrintValueNodeGraph(data, child, 3, 1);
623 			WRITE_AND_CHECK(_output, data);
624 		}
625 		data = "\n";
626 		WRITE_AND_CHECK(_output, data);
627 	}
628 
629 	data = "\n\t\tRegisters:\n";
630 	WRITE_AND_CHECK(_output, data);
631 
632 	CpuState* state = thread->GetCpuState();
633 	BVariant value;
634 	const Register* reg = NULL;
635 	for (int32 i = 0; i < fArchitecture->CountRegisters(); i++) {
636 		reg = fArchitecture->Registers() + i;
637 		state->GetRegisterValue(reg, value);
638 
639 		if (reg->Format() == REGISTER_FORMAT_SIMD) {
640 			data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(),
641 				UiUtils::FormatSIMDValue(value, reg->BitSize(),
642 					SIMD_RENDER_FORMAT_INT16, data).String());
643 		} else {
644 			char buffer[64];
645 			data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(),
646 				UiUtils::VariantToString(value, buffer, sizeof(buffer)));
647 		}
648 
649 		WRITE_AND_CHECK(_output, data);
650 	}
651 
652 	return B_OK;
653 }
654 
655 
656 status_t
657 DebugReportGenerator::_DumpFunctionDisassembly(BFile& _output,
658 	target_addr_t instructionPointer)
659 {
660 	AutoLocker< ::Team> teamLocker(fTeam);
661 	BString data;
662 	FunctionInstance* instance = NULL;
663 	Image* image = fTeam->ImageByAddress(instructionPointer);
664 	if (image == NULL) {
665 		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
666 			B_PRIx64 ": address not contained in any valid image.\n",
667 			instructionPointer);
668 		WRITE_AND_CHECK(_output, data);
669 		return B_OK;
670 	}
671 
672 	ImageDebugInfo* info = image->GetImageDebugInfo();
673 	if (info == NULL) {
674 		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
675 			B_PRIx64 ": no debug info available for image '%s'.\n",
676 			instructionPointer,	image->Name().String());
677 		WRITE_AND_CHECK(_output, data);
678 		return B_OK;
679 	}
680 
681 	instance = info->FunctionAtAddress(instructionPointer);
682 	if (instance == NULL) {
683 		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
684 			B_PRIx64 ": address does not point to a function.\n",
685 			instructionPointer);
686 		WRITE_AND_CHECK(_output, data);
687 		return B_OK;
688 	}
689 
690 	Statement* statement = NULL;
691 	DisassembledCode* code = instance->GetSourceCode();
692 	BReference<DisassembledCode> codeReference;
693 	if (code == NULL) {
694 		status_t error = fTeam->DebugInfo()->DisassembleFunction(instance,
695 			code);
696 		if (error != B_OK) {
697 			data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
698 				B_PRIx64 ": %s.\n", instructionPointer, strerror(error));
699 			WRITE_AND_CHECK(_output, data);
700 			return B_OK;
701 		}
702 
703 		codeReference.SetTo(code, true);
704 	} else
705 		codeReference.SetTo(code);
706 
707 	statement = code->StatementAtAddress(instructionPointer);
708 	if (statement == NULL) {
709 		data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#"
710 			B_PRIx64 ": address does not map to a valid instruction.\n",
711 			instructionPointer);
712 		WRITE_AND_CHECK(_output, data);
713 		return B_OK;
714 	}
715 
716 	SourceLocation location = statement->StartSourceLocation();
717 
718 	data = "\t\t\tDisassembly:\n";
719 	WRITE_AND_CHECK(_output, data);
720 	for (int32 i = 0; i <= location.Line(); i++) {
721 		data = "\t\t\t\t";
722 		data << code->LineAt(i);
723 		if (i == location.Line())
724 			data << " <--";
725 		data << "\n";
726 		WRITE_AND_CHECK(_output, data);
727 	}
728 	data = "\n";
729 	WRITE_AND_CHECK(_output, data);
730 
731 	return B_OK;
732 }
733 
734 
735 status_t
736 DebugReportGenerator::_DumpStackFrameMemory(BFile& _output,
737 	CpuState* state, target_addr_t framePointer, uint8 stackDirection)
738 {
739 	target_addr_t startAddress;
740 	target_addr_t endAddress;
741 	if (stackDirection == STACK_GROWTH_DIRECTION_POSITIVE) {
742 		startAddress = framePointer;
743 		endAddress = state->StackPointer();
744 	} else {
745 		startAddress = state->StackPointer();
746 		endAddress = framePointer;
747 	}
748 
749 	if (endAddress <= startAddress)
750 		return B_OK;
751 
752 	if (fCurrentBlock == NULL || !fCurrentBlock->Contains(startAddress)) {
753 		status_t error;
754 		fListener->InspectRequested(startAddress, this);
755 		do {
756 			error = acquire_sem(fTeamDataSem);
757 		} while (error == B_INTERRUPTED);
758 
759 		if (error != B_OK)
760 			return error;
761 	}
762 
763 	BString data("\t\t\tFrame memory:\n");
764 	WRITE_AND_CHECK(_output, data);
765 	if (fBlockRetrievalStatus == B_OK) {
766 		data.Truncate(0L);
767 		UiUtils::DumpMemory(data, 4, fCurrentBlock, startAddress, 1, 16,
768 			endAddress - startAddress);
769 		WRITE_AND_CHECK(_output, data);
770 	} else {
771 		data.SetToFormat("\t\t\t\tUnavailable (%s)\n", strerror(
772 				fBlockRetrievalStatus));
773 		WRITE_AND_CHECK(_output, data);
774 	}
775 
776 	return B_OK;
777 }
778 
779 
780 status_t
781 DebugReportGenerator::_ResolveValueIfNeeded(ValueNode* node, StackFrame* frame,
782 	int32 maxDepth)
783 {
784 	status_t result = B_OK;
785 	if (node->LocationAndValueResolutionState() == VALUE_NODE_UNRESOLVED) {
786 		fWaitingNode = node;
787 		fListener->ValueNodeValueRequested(frame->GetCpuState(),
788 			fNodeManager->GetContainer(), node);
789 		do {
790 			result = acquire_sem(fTeamDataSem);
791 		} while (result == B_INTERRUPTED);
792 	}
793 
794 	if (node->LocationAndValueResolutionState() == B_OK && maxDepth > 0) {
795 		AutoLocker<ValueNodeContainer> containerLocker(
796 			fNodeManager->GetContainer());
797 		for (int32 i = 0; i < node->CountChildren(); i++) {
798 			ValueNodeChild* child = node->ChildAt(i);
799 			containerLocker.Unlock();
800 			result = fNodeManager->AddChildNodes(child);
801 			if (result != B_OK)
802 				continue;
803 
804 			// since in the case of a pointer to a compound we hide
805 			// the intervening compound, don't consider the hidden node
806 			// a level for the purposes of depth traversal
807 			if (node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
808 				&& child->GetType()->ResolveRawType(false)->Kind()
809 					== TYPE_COMPOUND) {
810 				_ResolveValueIfNeeded(child->Node(), frame, maxDepth);
811 			} else
812 				_ResolveValueIfNeeded(child->Node(), frame, maxDepth - 1);
813 			containerLocker.Lock();
814 		}
815 	}
816 
817 	return result;
818 }
819 
820 
821 void
822 DebugReportGenerator::_HandleMemoryBlockRetrieved(TeamMemoryBlock* block,
823 	status_t result)
824 {
825 	if (fCurrentBlock != NULL) {
826 		fCurrentBlock->ReleaseReference();
827 		fCurrentBlock = NULL;
828 	}
829 
830 	fBlockRetrievalStatus = result;
831 
832 	fCurrentBlock = block;
833 	release_sem(fTeamDataSem);
834 }
835 
836 
837 
838 /*static*/ int
839 DebugReportGenerator::_CompareAreas(const AreaInfo* a, const AreaInfo* b)
840 {
841 	if (a->BaseAddress() < b->BaseAddress())
842 		return -1;
843 
844 	return 1;
845 }
846 
847 
848 /*static*/ int
849 DebugReportGenerator::_CompareImages(const Image* a, const Image* b)
850 {
851 	if (a->Info().TextBase() < b->Info().TextBase())
852 		return -1;
853 
854 	return 1;
855 }
856 
857 
858 /*static*/ int
859 DebugReportGenerator::_CompareSemaphores(const SemaphoreInfo* a,
860 	const SemaphoreInfo* b)
861 {
862 	if (a->SemID() < b->SemID())
863 		return -1;
864 
865 	return 1;
866 }
867 
868 
869 /*static*/ int
870 DebugReportGenerator::_CompareThreads(const ::Thread* a,
871 	const ::Thread* b)
872 {
873 	// sort stopped threads last, otherwise sort by thread ID
874 	if (a->State() == b->State())
875 		return a->ID() < b->ID() ? -1 : 1;
876 
877 	if (a->State() == THREAD_STATE_STOPPED && b->State()
878 			!= THREAD_STATE_STOPPED) {
879 		return 1;
880 	}
881 
882 	return -1;
883 }
884