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