From dee948553c1a0c3edadf2872f3b70cc8f38a6002 Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Mon, 16 Jun 2014 23:21:12 -0400 Subject: [PATCH] Debugger: Rework report generation. Rather than trying to buffer the entire report in memory and write it in a single shot, write it incrementally as the data is gathered. Fixes an issue reported by Diver with generating crash reports for apps that had crashed due to an infinite recursion stack overflow. --- .../controllers/DebugReportGenerator.cpp | 214 ++++++++++-------- .../controllers/DebugReportGenerator.h | 18 +- 2 files changed, 134 insertions(+), 98 deletions(-) diff --git a/src/apps/debugger/controllers/DebugReportGenerator.cpp b/src/apps/debugger/controllers/DebugReportGenerator.cpp index e89bf7fb4d..dd09bde244 100644 --- a/src/apps/debugger/controllers/DebugReportGenerator.cpp +++ b/src/apps/debugger/controllers/DebugReportGenerator.cpp @@ -41,6 +41,14 @@ #include "ValueNodeManager.h" +#define WRITE_AND_CHECK(output, data) \ + { \ + ssize_t error = output.Write(data.String(), data.Length()); \ + if (error < 0) \ + return error; \ + } + + DebugReportGenerator::DebugReportGenerator(::Team* team, UserInterfaceListener* listener, DebuggerInterface* interface) : @@ -121,31 +129,26 @@ DebugReportGenerator::_GenerateReport(const entry_ref& outputPath) if (result != B_OK) return result; - BString output; - result = _GenerateReportHeader(output); + result = _GenerateReportHeader(file); if (result != B_OK) return result; - result = _DumpRunningThreads(output); + result = _DumpRunningThreads(file); if (result != B_OK) return result; - result = _DumpLoadedImages(output); + result = _DumpLoadedImages(file); if (result != B_OK) return result; - result = _DumpAreas(output); + result = _DumpAreas(file); if (result != B_OK) return result; - result = _DumpSemaphores(output); + result = _DumpSemaphores(file); if (result != B_OK) return result; - result = file.Write(output.String(), output.Length()); - if (result < 0) - return result; - BPath path(&outputPath); fTeam->NotifyDebugReportChanged(path.Path()); @@ -223,14 +226,14 @@ DebugReportGenerator::FunctionSourceCodeChanged(Function* function) } status_t -DebugReportGenerator::_GenerateReportHeader(BString& _output) +DebugReportGenerator::_GenerateReportHeader(BFile& _output) { AutoLocker< ::Team> locker(fTeam); BString data; data.SetToFormat("Debug information for team %s (%" B_PRId32 "):\n", fTeam->Name(), fTeam->ID()); - _output << data; + WRITE_AND_CHECK(_output, data); SystemInfo sysInfo; @@ -268,7 +271,8 @@ DebugReportGenerator::_GenerateReportHeader(BString& _output) data.SetToFormat("CPU(s): %" B_PRId32 "x %s %s\n", info.cpu_count, get_cpu_vendor_string(cpuVendor), get_cpu_model_string(platform, cpuVendor, cpuModel)); - _output << data; + WRITE_AND_CHECK(_output, data); + char maxSize[32]; char usedSize[32]; @@ -277,12 +281,12 @@ DebugReportGenerator::_GenerateReportHeader(BString& _output) maxSize, sizeof(maxSize)), BPrivate::string_for_size((int64)info.used_pages * B_PAGE_SIZE, usedSize, sizeof(usedSize))); - _output << data; + WRITE_AND_CHECK(_output, data); const utsname& name = sysInfo.GetSystemName(); data.SetToFormat("Haiku revision: %s (%s)\n", name.version, name.machine); - _output << data; + WRITE_AND_CHECK(_output, data); } delete[] topology; @@ -291,13 +295,13 @@ DebugReportGenerator::_GenerateReportHeader(BString& _output) status_t -DebugReportGenerator::_DumpLoadedImages(BString& _output) +DebugReportGenerator::_DumpLoadedImages(BFile& _output) { AutoLocker< ::Team> locker(fTeam); - _output << "\nLoaded Images:\n"; + BString data("\nLoaded Images:\n"); + WRITE_AND_CHECK(_output, data); BObjectList images; - BString data; for (ImageList::ConstIterator it = fTeam->Images().GetIterator(); Image* image = it.Next();) { images.AddItem(image); @@ -308,9 +312,11 @@ DebugReportGenerator::_DumpLoadedImages(BString& _output) Image* image = NULL; data.SetToFormat("\tID\t\tText Base\tText End\tData Base\tData" " End\tType\tName\n\t"); - _output << data; - _output.Append('-', 80); - _output.Append("\n"); + WRITE_AND_CHECK(_output, data); + data.Truncate(0L); + data.Append('-', 80); + data.Append("\n"); + WRITE_AND_CHECK(_output, data); for (int32 i = 0; (image = images.ItemAt(i)) != NULL; i++) { const ImageInfo& info = image->Info(); char buffer[32]; @@ -325,7 +331,7 @@ DebugReportGenerator::_DumpLoadedImages(BString& _output) UiUtils::ImageTypeToString(info.Type(), buffer, sizeof(buffer)), info.Name().String()); - _output << data; + WRITE_AND_CHECK(_output, data); } catch (...) { return B_NO_MEMORY; } @@ -336,7 +342,7 @@ DebugReportGenerator::_DumpLoadedImages(BString& _output) status_t -DebugReportGenerator::_DumpAreas(BString& _output) +DebugReportGenerator::_DumpAreas(BFile& _output) { BObjectList areas(20, true); status_t result = fDebuggerInterface->GetAreaInfos(areas); @@ -345,12 +351,14 @@ DebugReportGenerator::_DumpAreas(BString& _output) areas.SortItems(&_CompareAreas); - _output << "\nAreas:\n"; - BString data; + BString data("\nAreas:\n"); + WRITE_AND_CHECK(_output, data); data.SetToFormat("\tID\t\tBase\t\tEnd\t\t\tSize (KiB)\tProtection\tLocking\t\t\tName\n\t"); - _output << data; - _output.Append('-', 80); - _output.Append("\n"); + WRITE_AND_CHECK(_output, data); + data.Truncate(0L); + data.Append('-', 80); + data.Append("\n"); + WRITE_AND_CHECK(_output, data); AreaInfo* info; BString protectionBuffer; char lockingBuffer[32]; @@ -365,35 +373,38 @@ DebugReportGenerator::_DumpAreas(BString& _output) UiUtils::AreaLockingFlagsToString(info->Lock(), lockingBuffer, sizeof(lockingBuffer)), info->Name().String()); - _output << data; + WRITE_AND_CHECK(_output, data); } catch (...) { return B_NO_MEMORY; } } - _output << "\nProtection Flags: r - read, w - write, x - execute, " + data = "\nProtection Flags: r - read, w - write, x - execute, " "s - stack, o - overcommit, c - cloneable, S - shared, k - kernel\n"; + WRITE_AND_CHECK(_output, data); return B_OK; } status_t -DebugReportGenerator::_DumpSemaphores(BString& _output) +DebugReportGenerator::_DumpSemaphores(BFile& _output) { BObjectList semaphores(20, true); - status_t result = fDebuggerInterface->GetSemaphoreInfos(semaphores); - if (result != B_OK) - return result; + status_t error = fDebuggerInterface->GetSemaphoreInfos(semaphores); + if (error != B_OK) + return error; semaphores.SortItems(&_CompareSemaphores); - _output << "\nSemaphores:\n"; - BString data; + BString data = "\nSemaphores:\n"; + WRITE_AND_CHECK(_output, data); data.SetToFormat("\tID\t\tCount\tLast Holder\tName\n\t"); - _output << data; - _output.Append('-', 60); - _output.Append("\n"); + WRITE_AND_CHECK(_output, data); + data.Truncate(0L); + data.Append('-', 60); + data.Append("\n"); + WRITE_AND_CHECK(_output, data); SemaphoreInfo* info; for (int32 i = 0; (info = semaphores.ItemAt(i)) != NULL; i++) { try { @@ -401,7 +412,7 @@ DebugReportGenerator::_DumpSemaphores(BString& _output) "\t%s\n", info->SemID(), info->Count(), info->LatestHolder(), info->Name().String()); - _output << data; + WRITE_AND_CHECK(_output, data); } catch (...) { return B_NO_MEMORY; } @@ -412,13 +423,12 @@ DebugReportGenerator::_DumpSemaphores(BString& _output) status_t -DebugReportGenerator::_DumpRunningThreads(BString& _output) +DebugReportGenerator::_DumpRunningThreads(BFile& _output) { AutoLocker< ::Team> locker(fTeam); - _output << "\nActive Threads:\n"; - BString data; - status_t result = B_OK; + BString data("\nActive Threads:\n"); + WRITE_AND_CHECK(_output, data); BObjectList< ::Thread> threads; ::Thread* thread; for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); @@ -429,10 +439,10 @@ DebugReportGenerator::_DumpRunningThreads(BString& _output) threads.SortItems(&_CompareThreads); for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++) { try { - data.SetToFormat("\tthread %" B_PRId32 ": %s %s", thread->ID(), + data.SetToFormat("\tthread %" B_PRId32 ": %s %s\n", thread->ID(), thread->Name(), thread->IsMainThread() ? "(main)" : ""); - _output << data << "\n"; + WRITE_AND_CHECK(_output, data); if (thread->State() == THREAD_STATE_STOPPED) { data.SetToFormat("\t\tstate: %s", @@ -441,18 +451,19 @@ DebugReportGenerator::_DumpRunningThreads(BString& _output) const BString& stoppedInfo = thread->StoppedReasonInfo(); if (stoppedInfo.Length() != 0) data << " (" << stoppedInfo << ")"; - _output << data << "\n\n"; + data << "\n\n"; + WRITE_AND_CHECK(_output, data); // we need to release our lock on the team here // since we might need to block and wait // on the stack trace. BReference< ::Thread> threadRef(thread); locker.Unlock(); - result = _DumpDebuggedThreadInfo(_output, thread); + status_t error = _DumpDebuggedThreadInfo(_output, thread); + if (error != B_OK) + return error; locker.Lock(); } - if (result != B_OK) - return result; } catch (...) { return B_NO_MEMORY; } @@ -463,13 +474,14 @@ DebugReportGenerator::_DumpRunningThreads(BString& _output) status_t -DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, +DebugReportGenerator::_DumpDebuggedThreadInfo(BFile& _output, ::Thread* thread) { AutoLocker< ::Team> locker; if (thread->State() != THREAD_STATE_STOPPED) return B_OK; + status_t error; StackTrace* trace = NULL; for (;;) { trace = thread->GetStackTrace(); @@ -478,16 +490,19 @@ DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, locker.Unlock(); fTraceWaitingThread = thread; - status_t result = acquire_sem(fTeamDataSem); - if (result != B_OK) - return result; + error = acquire_sem(fTeamDataSem); + if (error == B_INTERRUPTED) + continue; + else if (error != B_OK) + return error; locker.Lock(); } - _output << "\t\tFrame\t\tIP\t\t\tFunction Name\n"; - _output << "\t\t-----------------------------------------------\n"; - BString data; + BString data("\t\tFrame\t\tIP\t\t\tFunction Name\n"); + WRITE_AND_CHECK(_output, data); + data = "\t\t-----------------------------------------------\n"; + WRITE_AND_CHECK(_output, data); for (int32 i = 0; StackFrame* frame = trace->FrameAt(i); i++) { char functionName[512]; BString sourcePath; @@ -515,39 +530,48 @@ DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, frame, functionName, sizeof(functionName)), sourcePath.String()); - _output << data; + WRITE_AND_CHECK(_output, data); // only dump the topmost frame if (i == 0) { locker.Unlock(); - _DumpFunctionDisassembly(_output, frame->InstructionPointer()); - _DumpStackFrameMemory(_output, thread->GetCpuState(), + error = _DumpFunctionDisassembly(_output, frame->InstructionPointer()); + if (error != B_OK) + return error; + error = _DumpStackFrameMemory(_output, thread->GetCpuState(), frame->FrameAddress(), thread->GetTeam()->GetArchitecture() ->StackGrowthDirection()); + if (error != B_OK) + return error; locker.Lock(); } if (frame->CountParameters() == 0 && frame->CountLocalVariables() == 0) continue; - _output << "\t\t\tVariables:\n"; - status_t result = fNodeManager->SetStackFrame(thread, frame); - if (result != B_OK) + data = "\t\t\tVariables:\n"; + WRITE_AND_CHECK(_output, data); + error = fNodeManager->SetStackFrame(thread, frame); + if (error != B_OK) continue; ValueNodeContainer* container = fNodeManager->GetContainer(); AutoLocker containerLocker(container); for (int32 i = 0; i < container->CountChildren(); i++) { + data.Truncate(0L); ValueNodeChild* child = container->ChildAt(i); containerLocker.Unlock(); _ResolveValueIfNeeded(child->Node(), frame, 1); containerLocker.Lock(); - UiUtils::PrintValueNodeGraph(_output, child, 3, 1); + UiUtils::PrintValueNodeGraph(data, child, 3, 1); + WRITE_AND_CHECK(_output, data); } - _output << "\n"; + data = "\n"; + WRITE_AND_CHECK(_output, data); } - _output << "\n\t\tRegisters:\n"; + data = "\n\t\tRegisters:\n"; + WRITE_AND_CHECK(_output, data); CpuState* state = thread->GetCpuState(); BVariant value; @@ -559,15 +583,15 @@ DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, char buffer[64]; data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(), UiUtils::VariantToString(value, buffer, sizeof(buffer))); - _output << data; + WRITE_AND_CHECK(_output, data); } return B_OK; } -void -DebugReportGenerator::_DumpFunctionDisassembly(BString& _output, +status_t +DebugReportGenerator::_DumpFunctionDisassembly(BFile& _output, target_addr_t instructionPointer) { AutoLocker< ::Team> teamLocker(fTeam); @@ -579,8 +603,8 @@ DebugReportGenerator::_DumpFunctionDisassembly(BString& _output, if (error != B_OK) { data.SetToFormat("Unable to retrieve disassembly for IP %#" B_PRIx64 ": %s\n", instructionPointer, strerror(error)); - _output << data; - return; + WRITE_AND_CHECK(_output, data); + return B_OK; } DisassembledCode* code = instance->GetSourceCode(); @@ -599,18 +623,20 @@ DebugReportGenerator::_DumpFunctionDisassembly(BString& _output, case FUNCTION_SOURCE_LOADING: { teamLocker.Unlock(); - error = acquire_sem(fTeamDataSem); + do { + error = acquire_sem(fTeamDataSem); + } while (error == B_INTERRUPTED); if (error != B_OK) - return; + return error; teamLocker.Lock(); break; } default: - return; + return B_OK; } if (instance->SourceCodeState() == FUNCTION_SOURCE_UNAVAILABLE) - return; + return B_OK; error = fTeam->GetStatementAtAddress(instructionPointer, instance, statement); @@ -619,19 +645,25 @@ DebugReportGenerator::_DumpFunctionDisassembly(BString& _output, SourceLocation location = statement->StartSourceLocation(); - _output << "\t\t\tDisassembly:\n"; + data = "\t\t\tDisassembly:\n"; + WRITE_AND_CHECK(_output, data); for (int32 i = 0; i <= location.Line(); i++) { - _output << "\t\t\t\t" << code->LineAt(i); + data = "\t\t\t\t"; + data << code->LineAt(i); if (i == location.Line()) - _output << " <--"; - _output << "\n"; + data << " <--"; + data << "\n"; + WRITE_AND_CHECK(_output, data); } - _output << "\n"; + data = "\n"; + WRITE_AND_CHECK(_output, data); + + return B_OK; } -void -DebugReportGenerator::_DumpStackFrameMemory(BString& _output, +status_t +DebugReportGenerator::_DumpStackFrameMemory(BFile& _output, CpuState* state, target_addr_t framePointer, uint8 stackDirection) { target_addr_t startAddress; @@ -644,25 +676,29 @@ DebugReportGenerator::_DumpStackFrameMemory(BString& _output, endAddress = framePointer; } + status_t error; if (fCurrentBlock == NULL || !fCurrentBlock->Contains(startAddress)) { fListener->InspectRequested(startAddress, this); - status_t result = B_OK; + error = B_OK; do { - result = acquire_sem(fTeamDataSem); - } while (result == B_INTERRUPTED); + error = acquire_sem(fTeamDataSem); + } while (error == B_INTERRUPTED); } - _output << "\t\t\tFrame memory:\n"; + BString data("\t\t\tFrame memory:\n"); + WRITE_AND_CHECK(_output, data); if (fBlockRetrievalStatus == B_OK) { - UiUtils::DumpMemory(_output, 4 , fCurrentBlock, startAddress, 1, 16, + data.Truncate(0L); + UiUtils::DumpMemory(data, 4, fCurrentBlock, startAddress, 1, 16, endAddress - startAddress); + WRITE_AND_CHECK(_output, data); } else { - BString data; data.SetToFormat("\t\t\t\tUnavailable (%s)\n", strerror( fBlockRetrievalStatus)); - _output += data; + WRITE_AND_CHECK(_output, data); } + return B_OK; } diff --git a/src/apps/debugger/controllers/DebugReportGenerator.h b/src/apps/debugger/controllers/DebugReportGenerator.h index 121bb5a20c..e88d62f2d7 100644 --- a/src/apps/debugger/controllers/DebugReportGenerator.h +++ b/src/apps/debugger/controllers/DebugReportGenerator.h @@ -17,7 +17,7 @@ class entry_ref; class Architecture; class AreaInfo; -class BString; +class BFile; class DebuggerInterface; class SemaphoreInfo; class StackFrame; @@ -65,16 +65,16 @@ private: private: status_t _GenerateReport(const entry_ref& outputPath); - status_t _GenerateReportHeader(BString& _output); - status_t _DumpLoadedImages(BString& _output); - status_t _DumpAreas(BString& _output); - status_t _DumpSemaphores(BString& _output); - status_t _DumpRunningThreads(BString& _output); - status_t _DumpDebuggedThreadInfo(BString& _output, + status_t _GenerateReportHeader(BFile& _output); + status_t _DumpLoadedImages(BFile& _output); + status_t _DumpAreas(BFile& _output); + status_t _DumpSemaphores(BFile& _output); + status_t _DumpRunningThreads(BFile& _output); + status_t _DumpDebuggedThreadInfo(BFile& _output, ::Thread* thread); - void _DumpFunctionDisassembly(BString& _output, + status_t _DumpFunctionDisassembly(BFile& _output, target_addr_t instructionPointer); - void _DumpStackFrameMemory(BString& _output, + status_t _DumpStackFrameMemory(BFile& _output, CpuState* state, target_addr_t framePointer, uint8 stackDirection);