Rework control flow for debug report generator.
- DebugReportGenerator is now its own BLooper that generates reports asynchronously instead of in TeamDebugger's message loop. - If a stack trace isn't yet available, DebugReportGenerator now waits for it to be generated. - Extended Team to add a listener event for report generation completing. DebugReportGenerator now generates such an event when it has finished writing a report.
This commit is contained in:
parent
793d00f975
commit
248c2ff45c
@ -10,12 +10,15 @@
|
|||||||
#include <system_revision.h>
|
#include <system_revision.h>
|
||||||
|
|
||||||
#include <AutoLocker.h>
|
#include <AutoLocker.h>
|
||||||
|
#include <Entry.h>
|
||||||
#include <File.h>
|
#include <File.h>
|
||||||
|
#include <Path.h>
|
||||||
#include <StringForSize.h>
|
#include <StringForSize.h>
|
||||||
|
|
||||||
#include "Architecture.h"
|
#include "Architecture.h"
|
||||||
#include "CpuState.h"
|
#include "CpuState.h"
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
#include "MessageCodes.h"
|
||||||
#include "Register.h"
|
#include "Register.h"
|
||||||
#include "StackFrame.h"
|
#include "StackFrame.h"
|
||||||
#include "StackTrace.h"
|
#include "StackTrace.h"
|
||||||
@ -27,16 +30,21 @@
|
|||||||
|
|
||||||
DebugReportGenerator::DebugReportGenerator(::Team* team)
|
DebugReportGenerator::DebugReportGenerator(::Team* team)
|
||||||
:
|
:
|
||||||
|
BLooper("DebugReportGenerator"),
|
||||||
BReferenceable(),
|
BReferenceable(),
|
||||||
|
|
||||||
fTeam(team),
|
fTeam(team),
|
||||||
fArchitecture(team->GetArchitecture())
|
fArchitecture(team->GetArchitecture()),
|
||||||
|
fTeamDataSem(-1)
|
||||||
{
|
{
|
||||||
|
fTeam->AddListener(this);
|
||||||
fArchitecture->AcquireReference();
|
fArchitecture->AcquireReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DebugReportGenerator::~DebugReportGenerator()
|
DebugReportGenerator::~DebugReportGenerator()
|
||||||
{
|
{
|
||||||
|
fTeam->RemoveListener(this);
|
||||||
fArchitecture->ReleaseReference();
|
fArchitecture->ReleaseReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +52,12 @@ DebugReportGenerator::~DebugReportGenerator()
|
|||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::Init()
|
DebugReportGenerator::Init()
|
||||||
{
|
{
|
||||||
// TODO: anything needed here?
|
fTeamDataSem = create_sem(0, "debug_controller_data_wait");
|
||||||
|
if (fTeamDataSem < B_OK)
|
||||||
|
return fTeamDataSem;
|
||||||
|
|
||||||
|
Run();
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +79,7 @@ DebugReportGenerator::Create(::Team* team)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::GenerateReport(const entry_ref& outputPath)
|
DebugReportGenerator::_GenerateReport(const entry_ref& outputPath)
|
||||||
{
|
{
|
||||||
BFile file(&outputPath, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
BFile file(&outputPath, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||||
status_t result = file.InitCheck();
|
status_t result = file.InitCheck();
|
||||||
@ -90,14 +103,44 @@ DebugReportGenerator::GenerateReport(const entry_ref& outputPath)
|
|||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
BPath path(&outputPath);
|
||||||
|
|
||||||
|
fTeam->NotifyDebugReportChanged(path.Path());
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DebugReportGenerator::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what) {
|
||||||
|
case MSG_GENERATE_DEBUG_REPORT:
|
||||||
|
{
|
||||||
|
entry_ref ref;
|
||||||
|
if (message->FindRef("target", &ref) == B_OK)
|
||||||
|
_GenerateReport(ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
BLooper::MessageReceived(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DebugReportGenerator::ThreadStackTraceChanged(const ::Team::ThreadEvent& event)
|
||||||
|
{
|
||||||
|
release_sem(fTeamDataSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::_GenerateReportHeader(BString& _output)
|
DebugReportGenerator::_GenerateReportHeader(BString& _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",
|
||||||
@ -134,7 +177,7 @@ DebugReportGenerator::_GenerateReportHeader(BString& _output)
|
|||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::_DumpLoadedImages(BString& _output)
|
DebugReportGenerator::_DumpLoadedImages(BString& _output)
|
||||||
{
|
{
|
||||||
AutoLocker<Team> locker(fTeam);
|
AutoLocker< ::Team> locker(fTeam);
|
||||||
|
|
||||||
_output << "\nLoaded Images:\n";
|
_output << "\nLoaded Images:\n";
|
||||||
BString data;
|
BString data;
|
||||||
@ -167,13 +210,13 @@ DebugReportGenerator::_DumpLoadedImages(BString& _output)
|
|||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::_DumpRunningThreads(BString& _output)
|
DebugReportGenerator::_DumpRunningThreads(BString& _output)
|
||||||
{
|
{
|
||||||
AutoLocker<Team> locker(fTeam);
|
AutoLocker< ::Team> locker(fTeam);
|
||||||
|
|
||||||
_output << "\nActive Threads:\n";
|
_output << "\nActive Threads:\n";
|
||||||
BString data;
|
BString data;
|
||||||
status_t result = B_OK;
|
status_t result = B_OK;
|
||||||
for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
|
for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
|
||||||
Thread* thread = it.Next();) {
|
::Thread* thread = it.Next();) {
|
||||||
try {
|
try {
|
||||||
data.SetToFormat("\t%s %s, id: %" B_PRId32", state: %s\n",
|
data.SetToFormat("\t%s %s, id: %" B_PRId32", state: %s\n",
|
||||||
thread->Name(), thread->IsMainThread()
|
thread->Name(), thread->IsMainThread()
|
||||||
@ -183,8 +226,15 @@ DebugReportGenerator::_DumpRunningThreads(BString& _output)
|
|||||||
|
|
||||||
_output << data;
|
_output << data;
|
||||||
|
|
||||||
if (thread->State() == THREAD_STATE_STOPPED)
|
if (thread->State() == THREAD_STATE_STOPPED) {
|
||||||
|
// 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);
|
result = _DumpDebuggedThreadInfo(_output, thread);
|
||||||
|
locker.Lock();
|
||||||
|
}
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
@ -197,14 +247,27 @@ DebugReportGenerator::_DumpRunningThreads(BString& _output)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, Thread* thread)
|
DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output,
|
||||||
|
::Thread* thread)
|
||||||
{
|
{
|
||||||
AutoLocker<Team> locker(fTeam);
|
AutoLocker< ::Team> locker;
|
||||||
|
if (thread->State() != THREAD_STATE_STOPPED)
|
||||||
StackTrace* trace = thread->GetStackTrace();
|
|
||||||
if (trace == NULL)
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
|
StackTrace* trace = NULL;
|
||||||
|
for (;;) {
|
||||||
|
trace = thread->GetStackTrace();
|
||||||
|
if (trace != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
locker.Unlock();
|
||||||
|
status_t result = acquire_sem(fTeamDataSem);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
locker.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
_output << "\t\tFrame\t\tIP\t\t\tFunction Name\n";
|
_output << "\t\tFrame\t\tIP\t\t\tFunction Name\n";
|
||||||
_output << "\t\t-----------------------------------------------\n";
|
_output << "\t\t-----------------------------------------------\n";
|
||||||
BString data;
|
BString data;
|
||||||
|
@ -5,8 +5,12 @@
|
|||||||
#ifndef DEBUG_REPORT_GENERATOR_H
|
#ifndef DEBUG_REPORT_GENERATOR_H
|
||||||
#define DEBUG_GENERATOR_H
|
#define DEBUG_GENERATOR_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Looper.h>
|
||||||
#include <Referenceable.h>
|
#include <Referenceable.h>
|
||||||
|
|
||||||
|
#include "Team.h"
|
||||||
|
|
||||||
|
|
||||||
class entry_ref;
|
class entry_ref;
|
||||||
class Architecture;
|
class Architecture;
|
||||||
@ -15,7 +19,8 @@ class Team;
|
|||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
|
|
||||||
class DebugReportGenerator : public BReferenceable
|
class DebugReportGenerator : public BLooper, public BReferenceable,
|
||||||
|
public Team::Listener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DebugReportGenerator(::Team* team);
|
DebugReportGenerator(::Team* team);
|
||||||
@ -25,18 +30,23 @@ public:
|
|||||||
|
|
||||||
static DebugReportGenerator* Create(::Team* team);
|
static DebugReportGenerator* Create(::Team* team);
|
||||||
|
|
||||||
status_t GenerateReport(const entry_ref& outputPath);
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
|
virtual void ThreadStackTraceChanged(
|
||||||
|
const Team::ThreadEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
status_t _GenerateReport(const entry_ref& outputPath);
|
||||||
status_t _GenerateReportHeader(BString& output);
|
status_t _GenerateReportHeader(BString& output);
|
||||||
status_t _DumpLoadedImages(BString& output);
|
status_t _DumpLoadedImages(BString& output);
|
||||||
status_t _DumpRunningThreads(BString& output);
|
status_t _DumpRunningThreads(BString& output);
|
||||||
status_t _DumpDebuggedThreadInfo(BString& output,
|
status_t _DumpDebuggedThreadInfo(BString& output,
|
||||||
Thread* thread);
|
::Thread* thread);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
::Team* fTeam;
|
::Team* fTeam;
|
||||||
Architecture* fArchitecture;
|
Architecture* fArchitecture;
|
||||||
|
sem_id fTeamDataSem;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEBUG_REPORT_GENERATOR_H
|
#endif // DEBUG_REPORT_GENERATOR_H
|
||||||
|
@ -207,6 +207,8 @@ TeamDebugger::~TeamDebugger()
|
|||||||
delete fBreakpointManager;
|
delete fBreakpointManager;
|
||||||
delete fWatchpointManager;
|
delete fWatchpointManager;
|
||||||
delete fMemoryBlockManager;
|
delete fMemoryBlockManager;
|
||||||
|
fReportGenerator->Lock();
|
||||||
|
fReportGenerator->Quit();
|
||||||
delete fReportGenerator;
|
delete fReportGenerator;
|
||||||
delete fWorker;
|
delete fWorker;
|
||||||
delete fTeam;
|
delete fTeam;
|
||||||
@ -550,9 +552,7 @@ TeamDebugger::MessageReceived(BMessage* message)
|
|||||||
|
|
||||||
case MSG_GENERATE_DEBUG_REPORT:
|
case MSG_GENERATE_DEBUG_REPORT:
|
||||||
{
|
{
|
||||||
entry_ref ref;
|
fReportGenerator->PostMessage(message);
|
||||||
if (message->FindRef("target", &ref) == B_OK)
|
|
||||||
_HandleGenerateDebugReport(ref);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1532,13 +1532,6 @@ TeamDebugger::_HandleInspectAddress(target_addr_t address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
TeamDebugger::_HandleGenerateDebugReport(const entry_ref& ref)
|
|
||||||
{
|
|
||||||
fReportGenerator->GenerateReport(ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ThreadHandler*
|
ThreadHandler*
|
||||||
TeamDebugger::_GetThreadHandler(thread_id threadID)
|
TeamDebugger::_GetThreadHandler(thread_id threadID)
|
||||||
{
|
{
|
||||||
|
@ -147,9 +147,6 @@ private:
|
|||||||
target_addr_t address,
|
target_addr_t address,
|
||||||
TeamMemoryBlock::Listener* listener);
|
TeamMemoryBlock::Listener* listener);
|
||||||
|
|
||||||
void _HandleGenerateDebugReport(
|
|
||||||
const entry_ref& target);
|
|
||||||
|
|
||||||
ThreadHandler* _GetThreadHandler(thread_id threadID);
|
ThreadHandler* _GetThreadHandler(thread_id threadID);
|
||||||
|
|
||||||
status_t _AddImage(const ImageInfo& imageInfo,
|
status_t _AddImage(const ImageInfo& imageInfo,
|
||||||
|
@ -635,6 +635,17 @@ Team::NotifyWatchpointChanged(Watchpoint* watchpoint)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Team::NotifyDebugReportChanged(const char* reportPath)
|
||||||
|
{
|
||||||
|
for (ListenerList::Iterator it = fListeners.GetIterator();
|
||||||
|
Listener* listener = it.Next();) {
|
||||||
|
listener->DebugReportChanged(DebugReportEvent(
|
||||||
|
TEAM_EVENT_DEBUG_REPORT_CHANGED, this, reportPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Team::_NotifyThreadAdded(Thread* thread)
|
Team::_NotifyThreadAdded(Thread* thread)
|
||||||
{
|
{
|
||||||
@ -720,6 +731,18 @@ Team::BreakpointEvent::BreakpointEvent(uint32 type, Team* team,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - DebugReportEvent
|
||||||
|
|
||||||
|
|
||||||
|
Team::DebugReportEvent::DebugReportEvent(uint32 type, Team* team,
|
||||||
|
const char* reportPath)
|
||||||
|
:
|
||||||
|
Event(type, team),
|
||||||
|
fReportPath(reportPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - WatchpointEvent
|
// #pragma mark - WatchpointEvent
|
||||||
|
|
||||||
|
|
||||||
@ -834,3 +857,9 @@ void
|
|||||||
Team::Listener::WatchpointChanged(const Team::WatchpointEvent& event)
|
Team::Listener::WatchpointChanged(const Team::WatchpointEvent& event)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Team::Listener::DebugReportChanged(const Team::DebugReportEvent& event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -38,7 +38,9 @@ enum {
|
|||||||
|
|
||||||
TEAM_EVENT_WATCHPOINT_ADDED,
|
TEAM_EVENT_WATCHPOINT_ADDED,
|
||||||
TEAM_EVENT_WATCHPOINT_REMOVED,
|
TEAM_EVENT_WATCHPOINT_REMOVED,
|
||||||
TEAM_EVENT_WATCHPOINT_CHANGED
|
TEAM_EVENT_WATCHPOINT_CHANGED,
|
||||||
|
|
||||||
|
TEAM_EVENT_DEBUG_REPORT_CHANGED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +63,7 @@ class Team {
|
|||||||
public:
|
public:
|
||||||
class Event;
|
class Event;
|
||||||
class BreakpointEvent;
|
class BreakpointEvent;
|
||||||
|
class DebugReportEvent;
|
||||||
class ImageEvent;
|
class ImageEvent;
|
||||||
class ThreadEvent;
|
class ThreadEvent;
|
||||||
class UserBreakpointEvent;
|
class UserBreakpointEvent;
|
||||||
@ -183,6 +186,10 @@ public:
|
|||||||
void NotifyWatchpointChanged(
|
void NotifyWatchpointChanged(
|
||||||
Watchpoint* watchpoint);
|
Watchpoint* watchpoint);
|
||||||
|
|
||||||
|
// debug report related service methods
|
||||||
|
void NotifyDebugReportChanged(
|
||||||
|
const char* reportPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct BreakpointByAddressPredicate;
|
struct BreakpointByAddressPredicate;
|
||||||
struct WatchpointByAddressPredicate;
|
struct WatchpointByAddressPredicate;
|
||||||
@ -261,6 +268,17 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Team::DebugReportEvent : public Event {
|
||||||
|
public:
|
||||||
|
DebugReportEvent(uint32 type, Team* team,
|
||||||
|
const char* reportPath);
|
||||||
|
|
||||||
|
const char* GetReportPath() const { return fReportPath; }
|
||||||
|
protected:
|
||||||
|
const char* fReportPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Team::WatchpointEvent : public Event {
|
class Team::WatchpointEvent : public Event {
|
||||||
public:
|
public:
|
||||||
WatchpointEvent(uint32 type, Team* team,
|
WatchpointEvent(uint32 type, Team* team,
|
||||||
@ -318,6 +336,9 @@ public:
|
|||||||
const Team::WatchpointEvent& event);
|
const Team::WatchpointEvent& event);
|
||||||
virtual void WatchpointChanged(
|
virtual void WatchpointChanged(
|
||||||
const Team::WatchpointEvent& event);
|
const Team::WatchpointEvent& event);
|
||||||
|
|
||||||
|
virtual void DebugReportChanged(
|
||||||
|
const Team::DebugReportEvent& event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user