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