xref: /haiku/src/kits/debugger/controllers/DebugReportGenerator.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 		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 
321 status_t
322 DebugReportGenerator::_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 
368 status_t
369 DebugReportGenerator::_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 
414 status_t
415 DebugReportGenerator::_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 
449 status_t
450 DebugReportGenerator::_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 
504 status_t
505 DebugReportGenerator::_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 
659 status_t
660 DebugReportGenerator::_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 
738 status_t
739 DebugReportGenerator::_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 
783 status_t
784 DebugReportGenerator::_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 
824 void
825 DebugReportGenerator::_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
842 DebugReportGenerator::_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
852 DebugReportGenerator::_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
862 DebugReportGenerator::_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
873 DebugReportGenerator::_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