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