Do an actual analysis of the scheduling data -- basically equivalent to what
the kernel scheduling analysis does. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30386 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
SubDir HAIKU_TOP src apps debuganalyzer ;
|
||||
|
||||
UsePrivateHeaders debug interface shared ;
|
||||
UsePrivateHeaders debug interface kernel shared ;
|
||||
UsePrivateSystemHeaders ;
|
||||
|
||||
Application DebugAnalyzer
|
||||
|
||||
@@ -13,6 +13,75 @@
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
|
||||
// #pragma mark - WaitObject
|
||||
|
||||
|
||||
Model::WaitObject::WaitObject(const system_profiler_wait_object_info* event)
|
||||
:
|
||||
fEvent(event)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObject::~WaitObject()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - WaitObjectGroup
|
||||
|
||||
|
||||
Model::WaitObjectGroup::WaitObjectGroup(WaitObject* waitObject)
|
||||
{
|
||||
fWaitObjects.Add(waitObject);
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObjectGroup::~WaitObjectGroup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ThreadWaitObject
|
||||
|
||||
|
||||
Model::ThreadWaitObject::ThreadWaitObject(WaitObject* waitObject)
|
||||
:
|
||||
fWaitObject(waitObject),
|
||||
fWaits(0),
|
||||
fTotalWaitTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObject::~ThreadWaitObject()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::ThreadWaitObject::AddWait(bigtime_t waitTime)
|
||||
{
|
||||
fWaits++;
|
||||
fTotalWaitTime += waitTime;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ThreadWaitObjectGroup
|
||||
|
||||
|
||||
Model::ThreadWaitObjectGroup::ThreadWaitObjectGroup(
|
||||
ThreadWaitObject* threadWaitObject)
|
||||
{
|
||||
fWaitObjects.Add(threadWaitObject);
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObjectGroup::~ThreadWaitObjectGroup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Team
|
||||
|
||||
|
||||
@@ -47,7 +116,22 @@ Model::Thread::Thread(Team* team, const system_profiler_thread_added* event,
|
||||
fTeam(team),
|
||||
fCreationEvent(event),
|
||||
fCreationTime(time),
|
||||
fDeletionTime(-1)
|
||||
fDeletionTime(-1),
|
||||
fRuns(0),
|
||||
fTotalRunTime(0),
|
||||
fMinRunTime(-1),
|
||||
fMaxRunTime(-1),
|
||||
fLatencies(0),
|
||||
fTotalLatency(0),
|
||||
fMinLatency(-1),
|
||||
fMaxLatency(-1),
|
||||
fReruns(0),
|
||||
fTotalRerunTime(0),
|
||||
fMinRerunTime(-1),
|
||||
fMaxRerunTime(-1),
|
||||
fUnspecifiedWaitTime(0),
|
||||
fPreemptions(0),
|
||||
fWaitObjectGroups(20, true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -57,6 +141,111 @@ Model::Thread::~Thread()
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObjectGroup*
|
||||
Model::Thread::ThreadWaitObjectGroupFor(uint32 type, addr_t object) const
|
||||
{
|
||||
type_and_object key;
|
||||
key.type = type;
|
||||
key.object = object;
|
||||
|
||||
return fWaitObjectGroups.BinarySearchByKey(key,
|
||||
&ThreadWaitObjectGroup::CompareWithTypeObject);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::AddRun(bigtime_t runTime)
|
||||
{
|
||||
fRuns++;
|
||||
fTotalRunTime += runTime;
|
||||
|
||||
if (fMinRunTime < 0 || runTime < fMinRunTime)
|
||||
fMinRunTime = runTime;
|
||||
if (runTime > fMaxRunTime)
|
||||
fMaxRunTime = runTime;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::AddRerun(bigtime_t runTime)
|
||||
{
|
||||
fReruns++;
|
||||
fTotalRerunTime += runTime;
|
||||
|
||||
if (fMinRerunTime < 0 || runTime < fMinRerunTime)
|
||||
fMinRerunTime = runTime;
|
||||
if (runTime > fMaxRerunTime)
|
||||
fMaxRerunTime = runTime;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::AddLatency(bigtime_t latency)
|
||||
{
|
||||
fLatencies++;
|
||||
fTotalLatency += latency;
|
||||
|
||||
if (fMinLatency < 0 || latency < fMinLatency)
|
||||
fMinLatency = latency;
|
||||
if (latency > fMaxLatency)
|
||||
fMaxLatency = latency;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::AddPreemption(bigtime_t runTime)
|
||||
{
|
||||
fPreemptions++;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::AddUnspecifiedWait(bigtime_t waitTime)
|
||||
{
|
||||
fUnspecifiedWaitTime += waitTime;
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObject*
|
||||
Model::Thread::AddThreadWaitObject(WaitObject* waitObject,
|
||||
ThreadWaitObjectGroup** _threadWaitObjectGroup)
|
||||
{
|
||||
// create a thread wait object
|
||||
ThreadWaitObject* threadWaitObject
|
||||
= new(std::nothrow) ThreadWaitObject(waitObject);
|
||||
if (threadWaitObject == NULL)
|
||||
return NULL;
|
||||
|
||||
// find the thread wait object group
|
||||
ThreadWaitObjectGroup* threadWaitObjectGroup
|
||||
= ThreadWaitObjectGroupFor(waitObject->Type(), waitObject->Object());
|
||||
if (threadWaitObjectGroup == NULL) {
|
||||
// doesn't exist yet -- create
|
||||
threadWaitObjectGroup = new(std::nothrow) ThreadWaitObjectGroup(
|
||||
threadWaitObject);
|
||||
if (threadWaitObjectGroup == NULL) {
|
||||
delete threadWaitObject;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// add to the list
|
||||
if (!fWaitObjectGroups.BinaryInsert(threadWaitObjectGroup,
|
||||
&ThreadWaitObjectGroup::CompareByTypeObject)) {
|
||||
delete threadWaitObjectGroup;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// exists -- just add the object
|
||||
threadWaitObjectGroup->AddWaitObject(threadWaitObject);
|
||||
}
|
||||
|
||||
if (_threadWaitObjectGroup != NULL)
|
||||
*_threadWaitObjectGroup = threadWaitObjectGroup;
|
||||
|
||||
return threadWaitObject;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Model
|
||||
|
||||
|
||||
@@ -65,7 +254,8 @@ Model::Model(void* eventData, size_t eventDataSize)
|
||||
fEventData(eventData),
|
||||
fEventDataSize(eventDataSize),
|
||||
fTeams(20, true),
|
||||
fThreads(20, true)
|
||||
fThreads(20, true),
|
||||
fWaitObjectGroups(20, true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -176,3 +366,76 @@ Model::AddThread(const system_profiler_thread_added* event, bigtime_t time)
|
||||
threadDeleter.Detach();
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObject*
|
||||
Model::AddWaitObject(const system_profiler_wait_object_info* event,
|
||||
WaitObjectGroup** _waitObjectGroup)
|
||||
{
|
||||
// create a wait object
|
||||
WaitObject* waitObject = new(std::nothrow) WaitObject(event);
|
||||
if (waitObject == NULL)
|
||||
return NULL;
|
||||
|
||||
// find the wait object group
|
||||
WaitObjectGroup* waitObjectGroup
|
||||
= WaitObjectGroupFor(waitObject->Type(), waitObject->Object());
|
||||
if (waitObjectGroup == NULL) {
|
||||
// doesn't exist yet -- create
|
||||
waitObjectGroup = new(std::nothrow) WaitObjectGroup(waitObject);
|
||||
if (waitObjectGroup == NULL) {
|
||||
delete waitObject;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// add to the list
|
||||
if (!fWaitObjectGroups.BinaryInsert(waitObjectGroup,
|
||||
&WaitObjectGroup::CompareByTypeObject)) {
|
||||
delete waitObjectGroup;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// exists -- just add the object
|
||||
waitObjectGroup->AddWaitObject(waitObject);
|
||||
}
|
||||
|
||||
if (_waitObjectGroup != NULL)
|
||||
*_waitObjectGroup = waitObjectGroup;
|
||||
|
||||
return waitObject;
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObjectGroup*
|
||||
Model::WaitObjectGroupFor(uint32 type, addr_t object) const
|
||||
{
|
||||
type_and_object key;
|
||||
key.type = type;
|
||||
key.object = object;
|
||||
|
||||
return fWaitObjectGroups.BinarySearchByKey(key,
|
||||
&WaitObjectGroup::CompareWithTypeObject);
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObject*
|
||||
Model::AddThreadWaitObject(thread_id threadID, WaitObject* waitObject,
|
||||
ThreadWaitObjectGroup** _threadWaitObjectGroup)
|
||||
{
|
||||
Thread* thread = ThreadByID(threadID);
|
||||
if (thread == NULL)
|
||||
return NULL;
|
||||
|
||||
return thread->AddThreadWaitObject(waitObject, _threadWaitObjectGroup);
|
||||
}
|
||||
|
||||
|
||||
Model::ThreadWaitObjectGroup*
|
||||
Model::ThreadWaitObjectGroupFor(thread_id threadID, uint32 type, addr_t object) const
|
||||
{
|
||||
Thread* thread = ThreadByID(threadID);
|
||||
if (thread == NULL)
|
||||
return NULL;
|
||||
|
||||
return thread->ThreadWaitObjectGroupFor(type, object);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,17 @@
|
||||
#include <OS.h>
|
||||
|
||||
#include <system_profiler_defs.h>
|
||||
#include <util/SinglyLinkedList.h>
|
||||
|
||||
|
||||
class Model {
|
||||
public:
|
||||
struct creation_time_id;
|
||||
struct type_and_object;
|
||||
class WaitObjectGroup;
|
||||
class WaitObject;
|
||||
class ThreadWaitObject;
|
||||
class ThreadWaitObjectGroup;
|
||||
class Team;
|
||||
class Thread;
|
||||
|
||||
@@ -35,15 +41,32 @@ public:
|
||||
const system_profiler_thread_added* event,
|
||||
bigtime_t time);
|
||||
|
||||
WaitObject* AddWaitObject(
|
||||
const system_profiler_wait_object_info*
|
||||
event,
|
||||
WaitObjectGroup** _waitObjectGroup);
|
||||
WaitObjectGroup* WaitObjectGroupFor(uint32 type,
|
||||
addr_t object) const;
|
||||
|
||||
ThreadWaitObject* AddThreadWaitObject(thread_id threadID,
|
||||
WaitObject* waitObject,
|
||||
ThreadWaitObjectGroup**
|
||||
_threadWaitObjectGroup);
|
||||
ThreadWaitObjectGroup* ThreadWaitObjectGroupFor(
|
||||
thread_id threadID, uint32 type,
|
||||
addr_t object) const;
|
||||
|
||||
private:
|
||||
typedef BObjectList<Team> TeamList;
|
||||
typedef BObjectList<Thread> ThreadList;
|
||||
typedef BObjectList<WaitObjectGroup> WaitObjectGroupList;
|
||||
|
||||
private:
|
||||
void* fEventData;
|
||||
size_t fEventDataSize;
|
||||
TeamList fTeams; // sorted by ID
|
||||
ThreadList fThreads; // sorted by ID
|
||||
WaitObjectGroupList fWaitObjectGroups;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,6 +76,106 @@ struct Model::creation_time_id {
|
||||
};
|
||||
|
||||
|
||||
struct Model::type_and_object {
|
||||
uint32 type;
|
||||
addr_t object;
|
||||
};
|
||||
|
||||
|
||||
class Model::WaitObject : public SinglyLinkedListLinkImpl<WaitObject> {
|
||||
public:
|
||||
WaitObject(
|
||||
const system_profiler_wait_object_info*
|
||||
event);
|
||||
~WaitObject();
|
||||
|
||||
inline uint32 Type() const;
|
||||
inline addr_t Object() const;
|
||||
inline const char* Name() const;
|
||||
inline addr_t ReferencedObject();
|
||||
|
||||
static inline int CompareByTypeObject(const WaitObject* a,
|
||||
const WaitObject* b);
|
||||
static inline int CompareWithTypeObject(
|
||||
const type_and_object* key,
|
||||
const WaitObject* object);
|
||||
|
||||
private:
|
||||
const system_profiler_wait_object_info* fEvent;
|
||||
};
|
||||
|
||||
|
||||
class Model::WaitObjectGroup {
|
||||
public:
|
||||
WaitObjectGroup(WaitObject* waitObject);
|
||||
~WaitObjectGroup();
|
||||
|
||||
inline uint32 Type() const;
|
||||
inline addr_t Object() const;
|
||||
inline const char* Name() const;
|
||||
|
||||
inline WaitObject* MostRecentWaitObject() const;
|
||||
|
||||
inline void AddWaitObject(WaitObject* waitObject);
|
||||
|
||||
static inline int CompareByTypeObject(const WaitObjectGroup* a,
|
||||
const WaitObjectGroup* b);
|
||||
static inline int CompareWithTypeObject(
|
||||
const type_and_object* key,
|
||||
const WaitObjectGroup* group);
|
||||
|
||||
private:
|
||||
typedef SinglyLinkedList<WaitObject> WaitObjectList;
|
||||
|
||||
private:
|
||||
WaitObjectList fWaitObjects;
|
||||
};
|
||||
|
||||
|
||||
class Model::ThreadWaitObject
|
||||
: public SinglyLinkedListLinkImpl<ThreadWaitObject> {
|
||||
public:
|
||||
ThreadWaitObject(WaitObject* waitObject);
|
||||
~ThreadWaitObject();
|
||||
|
||||
inline WaitObject* GetWaitObject() const;
|
||||
|
||||
void AddWait(bigtime_t waitTime);
|
||||
|
||||
private:
|
||||
WaitObject* fWaitObject;
|
||||
int64 fWaits;
|
||||
bigtime_t fTotalWaitTime;
|
||||
};
|
||||
|
||||
|
||||
class Model::ThreadWaitObjectGroup {
|
||||
public:
|
||||
ThreadWaitObjectGroup(
|
||||
ThreadWaitObject* threadWaitObject);
|
||||
~ThreadWaitObjectGroup();
|
||||
|
||||
inline ThreadWaitObject* MostRecentThreadWaitObject() const;
|
||||
inline WaitObject* MostRecentWaitObject() const;
|
||||
|
||||
inline void AddWaitObject(
|
||||
ThreadWaitObject* threadWaitObject);
|
||||
|
||||
static inline int CompareByTypeObject(
|
||||
const ThreadWaitObjectGroup* a,
|
||||
const ThreadWaitObjectGroup* b);
|
||||
static inline int CompareWithTypeObject(
|
||||
const type_and_object* key,
|
||||
const ThreadWaitObjectGroup* group);
|
||||
|
||||
private:
|
||||
typedef SinglyLinkedList<ThreadWaitObject> ThreadWaitObjectList;
|
||||
|
||||
private:
|
||||
ThreadWaitObjectList fWaitObjects;
|
||||
};
|
||||
|
||||
|
||||
class Model::Team {
|
||||
public:
|
||||
Team(const system_profiler_team_added* event,
|
||||
@@ -96,8 +219,29 @@ public:
|
||||
inline bigtime_t CreationTime() const;
|
||||
inline bigtime_t DeletionTime() const;
|
||||
|
||||
inline int64 Runs() const;
|
||||
inline bigtime_t TotalRunTime() const;
|
||||
inline int64 Reruns() const;
|
||||
inline bigtime_t TotalRerunTime() const;
|
||||
inline int64 Latencies() const;
|
||||
inline bigtime_t TotalLatency() const;
|
||||
inline int64 Preemptions() const;
|
||||
|
||||
ThreadWaitObjectGroup* ThreadWaitObjectGroupFor(uint32 type,
|
||||
addr_t object) const;
|
||||
|
||||
inline void SetDeletionTime(bigtime_t time);
|
||||
|
||||
void AddRun(bigtime_t runTime);
|
||||
void AddRerun(bigtime_t runTime);
|
||||
void AddLatency(bigtime_t latency);
|
||||
void AddPreemption(bigtime_t runTime);
|
||||
void AddUnspecifiedWait(bigtime_t waitTime);
|
||||
|
||||
ThreadWaitObject* AddThreadWaitObject(WaitObject* waitObject,
|
||||
ThreadWaitObjectGroup**
|
||||
_threadWaitObjectGroup);
|
||||
|
||||
static inline int CompareByID(const Thread* a, const Thread* b);
|
||||
static inline int CompareWithID(const thread_id* id,
|
||||
const Thread* thread);
|
||||
@@ -108,14 +252,206 @@ public:
|
||||
const creation_time_id* key,
|
||||
const Thread* thread);
|
||||
|
||||
private:
|
||||
typedef BObjectList<ThreadWaitObjectGroup>
|
||||
ThreadWaitObjectGroupList;
|
||||
|
||||
private:
|
||||
Team* fTeam;
|
||||
const system_profiler_thread_added* fCreationEvent;
|
||||
bigtime_t fCreationTime;
|
||||
bigtime_t fDeletionTime;
|
||||
|
||||
int64 fRuns;
|
||||
bigtime_t fTotalRunTime;
|
||||
bigtime_t fMinRunTime;
|
||||
bigtime_t fMaxRunTime;
|
||||
|
||||
int64 fLatencies;
|
||||
bigtime_t fTotalLatency;
|
||||
bigtime_t fMinLatency;
|
||||
bigtime_t fMaxLatency;
|
||||
|
||||
int64 fReruns;
|
||||
bigtime_t fTotalRerunTime;
|
||||
bigtime_t fMinRerunTime;
|
||||
bigtime_t fMaxRerunTime;
|
||||
|
||||
bigtime_t fUnspecifiedWaitTime;
|
||||
|
||||
int64 fPreemptions;
|
||||
|
||||
ThreadWaitObjectGroupList fWaitObjectGroups;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - WaitObject
|
||||
|
||||
|
||||
uint32
|
||||
Model::WaitObject::Type() const
|
||||
{
|
||||
return fEvent->type;
|
||||
}
|
||||
|
||||
|
||||
addr_t
|
||||
Model::WaitObject::Object() const
|
||||
{
|
||||
return fEvent->object;
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Model::WaitObject::Name() const
|
||||
{
|
||||
return fEvent->name;
|
||||
}
|
||||
|
||||
|
||||
addr_t
|
||||
Model::WaitObject::ReferencedObject()
|
||||
{
|
||||
return fEvent->referenced_object;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::WaitObject::CompareByTypeObject(const WaitObject* a, const WaitObject* b)
|
||||
{
|
||||
type_and_object key;
|
||||
key.type = a->Type();
|
||||
key.object = a->Object();
|
||||
|
||||
return CompareWithTypeObject(&key, b);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::WaitObject::CompareWithTypeObject(const type_and_object* key,
|
||||
const WaitObject* object)
|
||||
{
|
||||
if (key->type == object->Type()) {
|
||||
if (key->object == object->Object())
|
||||
return 0;
|
||||
return key->object < object->Object() ? -1 : 1;
|
||||
}
|
||||
|
||||
return key->type < object->Type() ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - WaitObjectGroup
|
||||
|
||||
|
||||
uint32
|
||||
Model::WaitObjectGroup::Type() const
|
||||
{
|
||||
return MostRecentWaitObject()->Type();
|
||||
}
|
||||
|
||||
|
||||
addr_t
|
||||
Model::WaitObjectGroup::Object() const
|
||||
{
|
||||
return MostRecentWaitObject()->Object();
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Model::WaitObjectGroup::Name() const
|
||||
{
|
||||
return MostRecentWaitObject()->Name();
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObject*
|
||||
Model::WaitObjectGroup::MostRecentWaitObject() const
|
||||
{
|
||||
return fWaitObjects.Head();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::WaitObjectGroup::AddWaitObject(WaitObject* waitObject)
|
||||
{
|
||||
fWaitObjects.Add(waitObject);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::WaitObjectGroup::CompareByTypeObject(
|
||||
const WaitObjectGroup* a, const WaitObjectGroup* b)
|
||||
{
|
||||
return WaitObject::CompareByTypeObject(a->MostRecentWaitObject(),
|
||||
b->MostRecentWaitObject());
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::WaitObjectGroup::CompareWithTypeObject(const type_and_object* key,
|
||||
const WaitObjectGroup* group)
|
||||
{
|
||||
return WaitObject::CompareWithTypeObject(key,
|
||||
group->MostRecentWaitObject());
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ThreadWaitObject
|
||||
|
||||
|
||||
Model::WaitObject*
|
||||
Model::ThreadWaitObject::GetWaitObject() const
|
||||
{
|
||||
return fWaitObject;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ThreadWaitObjectGroup
|
||||
|
||||
|
||||
Model::ThreadWaitObject*
|
||||
Model::ThreadWaitObjectGroup::MostRecentThreadWaitObject() const
|
||||
{
|
||||
return fWaitObjects.Head();
|
||||
}
|
||||
|
||||
|
||||
Model::WaitObject*
|
||||
Model::ThreadWaitObjectGroup::MostRecentWaitObject() const
|
||||
{
|
||||
return MostRecentThreadWaitObject()->GetWaitObject();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::ThreadWaitObjectGroup::AddWaitObject(ThreadWaitObject* threadWaitObject)
|
||||
{
|
||||
fWaitObjects.Add(threadWaitObject);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::ThreadWaitObjectGroup::CompareByTypeObject(
|
||||
const ThreadWaitObjectGroup* a, const ThreadWaitObjectGroup* b)
|
||||
{
|
||||
return WaitObject::CompareByTypeObject(a->MostRecentWaitObject(),
|
||||
b->MostRecentWaitObject());
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
Model::ThreadWaitObjectGroup::CompareWithTypeObject(const type_and_object* key,
|
||||
const ThreadWaitObjectGroup* group)
|
||||
{
|
||||
return WaitObject::CompareWithTypeObject(key,
|
||||
group->MostRecentWaitObject());
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Team
|
||||
|
||||
|
||||
team_id
|
||||
Model::Team::ID() const
|
||||
{
|
||||
@@ -158,6 +494,9 @@ Model::Team::CompareWithID(const team_id* id, const Team* team)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Thread
|
||||
|
||||
|
||||
thread_id
|
||||
Model::Thread::ID() const
|
||||
{
|
||||
@@ -186,6 +525,55 @@ Model::Thread::DeletionTime() const
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
Model::Thread::Runs() const
|
||||
{
|
||||
return fRuns;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
Model::Thread::TotalRunTime() const
|
||||
{
|
||||
return fTotalRunTime;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
Model::Thread::Reruns() const
|
||||
{
|
||||
return fReruns;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
Model::Thread::TotalRerunTime() const
|
||||
{
|
||||
return fTotalRerunTime;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
Model::Thread::Latencies() const
|
||||
{
|
||||
return fLatencies;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
Model::Thread::TotalLatency() const
|
||||
{
|
||||
return fTotalLatency;
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
Model::Thread::Preemptions() const
|
||||
{
|
||||
return fPreemptions;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::Thread::SetDeletionTime(bigtime_t time)
|
||||
{
|
||||
|
||||
@@ -15,12 +15,46 @@
|
||||
#include <DebugEventStream.h>
|
||||
|
||||
#include <system_profiler_defs.h>
|
||||
#include <thread_defs.h>
|
||||
|
||||
#include "DataSource.h"
|
||||
#include "MessageCodes.h"
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
struct SimpleWaitObjectInfo : system_profiler_wait_object_info {
|
||||
SimpleWaitObjectInfo(uint32 type)
|
||||
{
|
||||
this->type = type;
|
||||
object = 0;
|
||||
referenced_object = 0;
|
||||
name[0] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo(
|
||||
THREAD_BLOCK_TYPE_SNOOZE);
|
||||
static const SimpleWaitObjectInfo kSignalWaitObjectInfo(
|
||||
THREAD_BLOCK_TYPE_SIGNAL);
|
||||
|
||||
|
||||
// #pragma mark - ThreadInfo
|
||||
|
||||
|
||||
ModelLoader::ThreadInfo::ThreadInfo(Model::Thread* thread)
|
||||
:
|
||||
thread(thread),
|
||||
state(UNKNOWN),
|
||||
lastTime(0),
|
||||
waitObject(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
inline void
|
||||
ModelLoader::_UpdateLastEventTime(bigtime_t time)
|
||||
{
|
||||
@@ -71,6 +105,11 @@ ModelLoader::StartLoading()
|
||||
if (fModel != NULL || fLoading || fDataSource == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// init the hash tables
|
||||
error = fThreads.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// spawn the loader thread
|
||||
fLoaderThread = spawn_thread(&_LoaderEntry, "main model loader",
|
||||
B_NORMAL_PRIORITY, this);
|
||||
@@ -133,6 +172,13 @@ ModelLoader::_Loader()
|
||||
// clean up and notify the target
|
||||
AutoLocker<BLocker> locker(fLock);
|
||||
|
||||
ThreadInfo* threadInfo = fThreads.Clear();
|
||||
while (threadInfo != NULL) {
|
||||
ThreadInfo* nextInfo = threadInfo->fNext;
|
||||
delete threadInfo;
|
||||
threadInfo = nextInfo;
|
||||
}
|
||||
|
||||
BMessage message;
|
||||
if (error == B_OK) {
|
||||
message.what = MSG_MODEL_LOADED_SUCCESSFULLY;
|
||||
@@ -181,6 +227,12 @@ ModelLoader::_Load()
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// add the snooze and signal wait objects to the model
|
||||
if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL
|
||||
|| fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) {
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
// process the events
|
||||
fBaseTime = -1;
|
||||
fLastEventTime = -1;
|
||||
@@ -294,91 +346,43 @@ ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer,
|
||||
{
|
||||
switch (event) {
|
||||
case B_SYSTEM_PROFILER_TEAM_ADDED:
|
||||
{
|
||||
system_profiler_team_added* event
|
||||
= (system_profiler_team_added*)buffer;
|
||||
|
||||
if (fModel->AddTeam(event, fLastEventTime) == NULL)
|
||||
return B_NO_MEMORY;
|
||||
return B_OK;
|
||||
}
|
||||
_HandleTeamAdded((system_profiler_team_added*)buffer);
|
||||
break;
|
||||
|
||||
case B_SYSTEM_PROFILER_TEAM_REMOVED:
|
||||
{
|
||||
system_profiler_team_removed* event
|
||||
= (system_profiler_team_removed*)buffer;
|
||||
|
||||
if (Model::Team* team = fModel->TeamByID(event->team)) {
|
||||
team->SetDeletionTime(fLastEventTime);
|
||||
} else {
|
||||
printf("Removed event for unknown team: %ld\n", event->team);
|
||||
}
|
||||
_HandleTeamRemoved((system_profiler_team_removed*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_TEAM_EXEC:
|
||||
_HandleTeamExec((system_profiler_team_exec*)buffer);
|
||||
break;
|
||||
|
||||
case B_SYSTEM_PROFILER_THREAD_ADDED:
|
||||
{
|
||||
system_profiler_thread_added* event
|
||||
= (system_profiler_thread_added*)buffer;
|
||||
|
||||
if (fModel->AddThread(event, fLastEventTime) == NULL)
|
||||
return B_NO_MEMORY;
|
||||
return B_OK;
|
||||
|
||||
_HandleThreadAdded((system_profiler_thread_added*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_THREAD_REMOVED:
|
||||
{
|
||||
system_profiler_thread_removed* event
|
||||
= (system_profiler_thread_removed*)buffer;
|
||||
|
||||
if (Model::Thread* thread = fModel->ThreadByID(event->thread)) {
|
||||
thread->SetDeletionTime(fLastEventTime);
|
||||
} else {
|
||||
printf("Removed event for unknown team: %ld\n", event->thread);
|
||||
}
|
||||
_HandleThreadRemoved((system_profiler_thread_removed*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
|
||||
{
|
||||
system_profiler_thread_scheduled* event
|
||||
= (system_profiler_thread_scheduled*)buffer;
|
||||
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
// TODO:...
|
||||
_HandleThreadScheduled((system_profiler_thread_scheduled*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
|
||||
{
|
||||
system_profiler_thread_enqueued_in_run_queue* event
|
||||
= (system_profiler_thread_enqueued_in_run_queue*)buffer;
|
||||
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
// TODO:...
|
||||
_HandleThreadEnqueuedInRunQueue(
|
||||
(thread_enqueued_in_run_queue*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
|
||||
{
|
||||
system_profiler_thread_removed_from_run_queue* event
|
||||
= (system_profiler_thread_removed_from_run_queue*)buffer;
|
||||
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
// TODO:...
|
||||
_HandleThreadRemovedFromRunQueue(
|
||||
(thread_removed_from_run_queue*)buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO:
|
||||
_HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unsupported event type %lu, size: %lu\n", event, size);
|
||||
return B_BAD_DATA;
|
||||
@@ -387,3 +391,267 @@ return B_BAD_DATA;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleTeamAdded(system_profiler_team_added* event)
|
||||
{
|
||||
if (fModel->AddTeam(event, fLastEventTime) == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event)
|
||||
{
|
||||
if (Model::Team* team = fModel->TeamByID(event->team))
|
||||
team->SetDeletionTime(fLastEventTime);
|
||||
else
|
||||
printf("Removed event for unknown team: %ld\n", event->team);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleTeamExec(system_profiler_team_exec* event)
|
||||
{
|
||||
// TODO:...
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event)
|
||||
{
|
||||
if (_AddThread(event) == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event)
|
||||
{
|
||||
if (Model::Thread* thread = fModel->ThreadByID(event->thread))
|
||||
thread->SetDeletionTime(fLastEventTime);
|
||||
else
|
||||
printf("Removed event for unknown team: %ld\n", event->thread);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event)
|
||||
{
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
ThreadInfo* thread = fThreads.Lookup(event->thread);
|
||||
if (thread == NULL) {
|
||||
printf("Schedule event for unknown thread: %ld\n", event->thread);
|
||||
return;
|
||||
}
|
||||
|
||||
bigtime_t diffTime = fLastEventTime - thread->lastTime;
|
||||
|
||||
if (thread->state == READY) {
|
||||
// thread scheduled after having been woken up
|
||||
thread->thread->AddLatency(diffTime);
|
||||
} else if (thread->state == PREEMPTED) {
|
||||
// thread scheduled after having been preempted before
|
||||
thread->thread->AddRerun(diffTime);
|
||||
}
|
||||
|
||||
if (thread->state == STILL_RUNNING) {
|
||||
// Thread was running and continues to run.
|
||||
thread->state = RUNNING;
|
||||
}
|
||||
|
||||
if (thread->state != RUNNING) {
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = RUNNING;
|
||||
}
|
||||
|
||||
// unscheduled thread
|
||||
|
||||
if (event->thread == event->previous_thread)
|
||||
return;
|
||||
|
||||
thread = fThreads.Lookup(event->previous_thread);
|
||||
if (thread == NULL) {
|
||||
printf("Schedule event for unknown previous thread: %ld\n",
|
||||
event->previous_thread);
|
||||
return;
|
||||
}
|
||||
|
||||
diffTime = fLastEventTime - thread->lastTime;
|
||||
|
||||
if (thread->state == STILL_RUNNING) {
|
||||
// thread preempted
|
||||
thread->thread->AddPreemption(diffTime);
|
||||
thread->thread->AddRun(diffTime);
|
||||
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = PREEMPTED;
|
||||
} else if (thread->state == RUNNING) {
|
||||
// thread starts waiting (it hadn't been added to the run
|
||||
// queue before being unscheduled)
|
||||
thread->thread->AddRun(diffTime);
|
||||
|
||||
if (event->previous_thread_state == B_THREAD_WAITING) {
|
||||
addr_t waitObject = event->previous_thread_wait_object;
|
||||
switch (event->previous_thread_wait_object_type) {
|
||||
case THREAD_BLOCK_TYPE_SNOOZE:
|
||||
case THREAD_BLOCK_TYPE_SIGNAL:
|
||||
waitObject = 0;
|
||||
break;
|
||||
case THREAD_BLOCK_TYPE_SEMAPHORE:
|
||||
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
|
||||
case THREAD_BLOCK_TYPE_MUTEX:
|
||||
case THREAD_BLOCK_TYPE_RW_LOCK:
|
||||
case THREAD_BLOCK_TYPE_OTHER:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_AddThreadWaitObject(thread,
|
||||
event->previous_thread_wait_object_type, waitObject);
|
||||
}
|
||||
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = WAITING;
|
||||
} else if (thread->state == UNKNOWN) {
|
||||
uint32 threadState = event->previous_thread_state;
|
||||
if (threadState == B_THREAD_WAITING
|
||||
|| threadState == B_THREAD_SUSPENDED) {
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = WAITING;
|
||||
} else if (threadState == B_THREAD_READY) {
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = PREEMPTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleThreadEnqueuedInRunQueue(
|
||||
thread_enqueued_in_run_queue* event)
|
||||
{
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
ThreadInfo* thread = fThreads.Lookup(event->thread);
|
||||
if (thread == NULL) {
|
||||
printf("Enqueued in run queue event for unknown thread: %ld\n",
|
||||
event->thread);
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread->state == RUNNING || thread->state == STILL_RUNNING) {
|
||||
// Thread was running and is reentered into the run queue. This
|
||||
// is done by the scheduler, if the thread remains ready.
|
||||
thread->state = STILL_RUNNING;
|
||||
} else {
|
||||
// Thread was waiting and is ready now.
|
||||
bigtime_t diffTime = fLastEventTime - thread->lastTime;
|
||||
if (thread->waitObject != NULL) {
|
||||
thread->waitObject->AddWait(diffTime);
|
||||
thread->waitObject = NULL;
|
||||
} else if (thread->state != UNKNOWN)
|
||||
thread->thread->AddUnspecifiedWait(diffTime);
|
||||
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = READY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleThreadRemovedFromRunQueue(
|
||||
thread_removed_from_run_queue* event)
|
||||
{
|
||||
_UpdateLastEventTime(event->time);
|
||||
|
||||
ThreadInfo* thread = fThreads.Lookup(event->thread);
|
||||
if (thread == NULL) {
|
||||
printf("Removed from run queue event for unknown thread: %ld\n",
|
||||
event->thread);
|
||||
return;
|
||||
}
|
||||
|
||||
// This really only happens when the thread priority is changed
|
||||
// while the thread is ready.
|
||||
|
||||
bigtime_t diffTime = fLastEventTime - thread->lastTime;
|
||||
if (thread->state == RUNNING) {
|
||||
// This should never happen.
|
||||
thread->thread->AddRun(diffTime);
|
||||
} else if (thread->state == READY || thread->state == PREEMPTED) {
|
||||
// Not really correct, but the case is rare and we keep it
|
||||
// simple.
|
||||
thread->thread->AddUnspecifiedWait(diffTime);
|
||||
}
|
||||
|
||||
thread->lastTime = fLastEventTime;
|
||||
thread->state = WAITING;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event)
|
||||
{
|
||||
if (fModel->AddWaitObject(event, NULL) == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
|
||||
ModelLoader::ThreadInfo*
|
||||
ModelLoader::_AddThread(system_profiler_thread_added* event)
|
||||
{
|
||||
// do we know the thread already?
|
||||
ThreadInfo* info = fThreads.Lookup(event->thread);
|
||||
if (info != NULL) {
|
||||
// TODO: ?
|
||||
return info;
|
||||
}
|
||||
|
||||
// add the thread to the model
|
||||
Model::Thread* thread = fModel->AddThread(event, fLastEventTime);
|
||||
if (thread == NULL)
|
||||
return NULL;
|
||||
|
||||
// create and add a ThreadInfo
|
||||
info = new(std::nothrow) ThreadInfo(thread);
|
||||
if (info == NULL)
|
||||
return NULL;
|
||||
|
||||
fThreads.Insert(info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ModelLoader::_AddThreadWaitObject(ThreadInfo* thread, uint32 type,
|
||||
addr_t object)
|
||||
{
|
||||
Model::WaitObjectGroup* waitObjectGroup
|
||||
= fModel->WaitObjectGroupFor(type, object);
|
||||
if (waitObjectGroup == NULL) {
|
||||
// The algorithm should prevent this case.
|
||||
printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type: %lu, "
|
||||
"object: %#lx\n", type, object);
|
||||
return;
|
||||
}
|
||||
|
||||
Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject();
|
||||
|
||||
Model::ThreadWaitObjectGroup* threadWaitObjectGroup
|
||||
= fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object);
|
||||
|
||||
if (threadWaitObjectGroup == NULL
|
||||
|| threadWaitObjectGroup->MostRecentWaitObject() != waitObject) {
|
||||
Model::ThreadWaitObject* threadWaitObject
|
||||
= fModel->AddThreadWaitObject(thread->ID(), waitObject,
|
||||
&threadWaitObjectGroup);
|
||||
if (threadWaitObject == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject();
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
#include <Locker.h>
|
||||
#include <Messenger.h>
|
||||
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
class BDataIO;
|
||||
class BDebugEventInputStream;
|
||||
class DataSource;
|
||||
class Model;
|
||||
struct system_profiler_event_header;
|
||||
struct system_profiler_thread_added;
|
||||
|
||||
|
||||
class ModelLoader {
|
||||
@@ -28,6 +31,52 @@ public:
|
||||
|
||||
Model* DetachModel();
|
||||
|
||||
private:
|
||||
enum ScheduleState {
|
||||
RUNNING,
|
||||
STILL_RUNNING,
|
||||
PREEMPTED,
|
||||
READY,
|
||||
WAITING,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
struct ThreadInfo : HashTableLink<ThreadInfo> {
|
||||
Model::Thread* thread;
|
||||
ScheduleState state;
|
||||
bigtime_t lastTime;
|
||||
Model::ThreadWaitObject* waitObject;
|
||||
|
||||
ThreadInfo(Model::Thread* thread);
|
||||
|
||||
thread_id ID() const { return thread->ID(); }
|
||||
};
|
||||
|
||||
struct ThreadTableDefinition {
|
||||
typedef thread_id KeyType;
|
||||
typedef ThreadInfo ValueType;
|
||||
|
||||
size_t HashKey(thread_id key) const
|
||||
{ return (size_t)key; }
|
||||
|
||||
size_t Hash(const ThreadInfo* value) const
|
||||
{ return (size_t)value->ID(); }
|
||||
|
||||
bool Compare(thread_id key, const ThreadInfo* value) const
|
||||
{ return key == value->ID(); }
|
||||
|
||||
HashTableLink<ThreadInfo>* GetLink(ThreadInfo* value) const
|
||||
{ return value; }
|
||||
};
|
||||
|
||||
typedef OpenHashTable<ThreadTableDefinition> ThreadTable;
|
||||
|
||||
// shorthands for the longish structure names
|
||||
typedef system_profiler_thread_enqueued_in_run_queue
|
||||
thread_enqueued_in_run_queue;
|
||||
typedef system_profiler_thread_removed_from_run_queue
|
||||
thread_removed_from_run_queue;
|
||||
|
||||
private:
|
||||
static status_t _LoaderEntry(void* data);
|
||||
status_t _Loader();
|
||||
@@ -39,6 +88,29 @@ private:
|
||||
|
||||
inline void _UpdateLastEventTime(bigtime_t time);
|
||||
|
||||
void _HandleTeamAdded(
|
||||
system_profiler_team_added* event);
|
||||
void _HandleTeamRemoved(
|
||||
system_profiler_team_removed* event);
|
||||
void _HandleTeamExec(
|
||||
system_profiler_team_exec* event);
|
||||
void _HandleThreadAdded(
|
||||
system_profiler_thread_added* event);
|
||||
void _HandleThreadRemoved(
|
||||
system_profiler_thread_removed* event);
|
||||
void _HandleThreadScheduled(
|
||||
system_profiler_thread_scheduled* event);
|
||||
void _HandleThreadEnqueuedInRunQueue(
|
||||
thread_enqueued_in_run_queue* event);
|
||||
void _HandleThreadRemovedFromRunQueue(
|
||||
thread_removed_from_run_queue* event);
|
||||
void _HandleWaitObjectInfo(
|
||||
system_profiler_wait_object_info* event);
|
||||
|
||||
ThreadInfo* _AddThread(system_profiler_thread_added* event);
|
||||
void _AddThreadWaitObject(ThreadInfo* thread,
|
||||
uint32 type, addr_t object);
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
Model* fModel;
|
||||
@@ -50,6 +122,7 @@ private:
|
||||
bool fAborted;
|
||||
bigtime_t fBaseTime;
|
||||
bigtime_t fLastEventTime;
|
||||
ThreadTable fThreads;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user