ContextManager and ProcessEnumerator have the basic functionality done.

Created a test program to validate them and show how to use them.
develop
Petr Mrázek 2010-05-29 21:34:36 +02:00
parent ab40868b29
commit 7300e61f8a
5 changed files with 348 additions and 71 deletions

@ -41,23 +41,90 @@ distribution.
using namespace DFHack; using namespace DFHack;
namespace DFHack namespace DFHack
{ {
class DFContextMgrPrivate class ContextManager::Private
{ {
public: public:
DFContextMgrPrivate(){}; Private(){};
~DFContextMgrPrivate(){}; ~Private(){};
string xml; // path to xml string xml; // path to xml
vector <Context *> contexts; vector <Context *> contexts;
ProcessEnumerator * pEnum; ProcessEnumerator * pEnum;
}; };
} }
class DFHack::BadContexts::Private
{
public:
Private(){};
vector <Context *> bad;
};
BadContexts::BadContexts():d(new Private()){}
BadContexts::~BadContexts()
{
clear();
delete d;
}
bool BadContexts::Contains(Process* p)
{
for(int i = 0; i < d->bad.size(); i++)
{
if((d->bad[i])->getProcess() == p)
return true;
}
return false;
}
bool BadContexts::Contains(Context* c)
{
for(int i = 0; i < d->bad.size(); i++)
{
if(d->bad[i] == c)
return true;
}
return false;
}
uint32_t BadContexts::size()
{
return d->bad.size();
}
void BadContexts::clear()
{
for(int i = 0; i < d->bad.size(); i++)
{
// delete both Process and Context!
// process has to be deleted after context, because Context does some
// cleanup on delete (detach, etc.)
Process * to_kill = d->bad[i]->getProcess();
delete d->bad[i];
delete to_kill;
}
d->bad.clear();
}
void BadContexts::push_back(Context* c)
{
if(c)
d->bad.push_back(c);
}
Context * BadContexts::operator[](uint32_t index)
{
if(index < d->bad.size())
return d->bad[index];
return 0;
}
ContextManager::ContextManager (const string path_to_xml) : d (new DFContextMgrPrivate()) ContextManager::ContextManager (const string path_to_xml) : d (new Private())
{ {
d->pEnum = 0;
d->xml = QUOT (MEMXML_DATA_PATH); d->xml = QUOT (MEMXML_DATA_PATH);
d->xml += "/"; d->xml += "/";
d->xml += path_to_xml; d->xml += path_to_xml;
d->pEnum = new ProcessEnumerator(d->xml);
} }
ContextManager::~ContextManager() ContextManager::~ContextManager()
@ -66,26 +133,76 @@ ContextManager::~ContextManager()
delete d; delete d;
} }
uint32_t ContextManager::Refresh() uint32_t ContextManager::Refresh( BadContexts* bad_contexts )
{ {
purge(); // handle expired processes, remove stale Contexts
if(d->pEnum != 0)
d->pEnum = new ProcessEnumerator(d->xml);
else
{ {
delete d->pEnum; BadProcesses expired;
d->pEnum = new ProcessEnumerator(d->xml); // get new list od living and expired Process objects
d->pEnum->Refresh(&expired);
// scan expired, kill contexts if necessary
vector <Context*>::iterator it = d->contexts.begin();;
while(it != d->contexts.end())
{
Process * test = (*it)->getProcess();
if(expired.Contains(test))
{
// ok. we have an expired context here.
if(!bad_contexts)
{
// with nowhere to put the context, we have to destroy it
delete *it;
// stop tracking it and advance the iterator
it = d->contexts.erase(it);
continue;
}
else
{
// we stuff the context into bad_contexts
bad_contexts->push_back(*it);
// stop tracking it and advance the iterator
it = d->contexts.erase(it);
// remove process from the 'expired' container, it is tracked by bad_contexts now
// (which is responsible for freeing it).
expired.excise(test);
continue;
}
}
else it++; // not expired, just advance to next one
}
// no expired contexts are in the d->contexts vector now
// all processes remaining in 'expired' are now destroyed along with it
} }
d->pEnum->purge();
d->pEnum->findProcessess();
int numProcesses = d->pEnum->size(); int numProcesses = d->pEnum->size();
int numContexts = d->contexts.size();
vector <Context *> newContexts;
// enumerate valid processes
for(int i = 0; i < numProcesses; i++) for(int i = 0; i < numProcesses; i++)
{ {
Context * c = new Context(d->pEnum->operator[](i)); Process * test = d->pEnum->operator[](i);
d->contexts.push_back(c); bool exists = false;
// scan context vector for this process
for(int j = 0; j < numContexts; j++)
{
if((d->contexts[j])->getProcess() == test)
{
// already have that one, skip
exists = true;
}
}
if(!exists)
{
// new process needs a new context
Context * c = new Context(d->pEnum->operator[](i));
newContexts.push_back(c);
}
} }
d->contexts.insert(d->contexts.end(), newContexts.begin(), newContexts.end());
return d->contexts.size(); return d->contexts.size();
} }
uint32_t ContextManager::size() uint32_t ContextManager::size()
{ {
return d->contexts.size(); return d->contexts.size();

@ -44,7 +44,76 @@ class DFHack::ProcessEnumerator::Private
void EnumPIDs (vector <ProcessID> &PIDs); void EnumPIDs (vector <ProcessID> &PIDs);
}; };
#ifdef LINUX_BUILD class DFHack::BadProcesses::Private
{
public:
Private(){};
PROC_V bad;
};
BadProcesses::BadProcesses():d(new Private()){}
BadProcesses::~BadProcesses()
{
clear();
delete d;
}
bool BadProcesses::Contains(Process* p)
{
for(int i = 0; i < d->bad.size(); i++)
{
if(d->bad[i] == p)
return true;
}
return false;
}
bool BadProcesses::excise(Process* p)
{
vector<Process*>::iterator it = d->bad.begin();
while(it != d->bad.end())
{
if((*it) == p)
{
d->bad.erase(it);
return true;
}
else
{
it++;
}
}
return false;
}
uint32_t BadProcesses::size()
{
return d->bad.size();
}
void BadProcesses::clear()
{
for(int i = 0; i < d->bad.size(); i++)
{
delete d->bad[i];
}
d->bad.clear();
}
void BadProcesses::push_back(Process* p)
{
if(p)
d->bad.push_back(p);
}
Process * BadProcesses::operator[](uint32_t index)
{
if(index < d->bad.size())
return d->bad[index];
return 0;
}
//FIXME: wasteful //FIXME: wasteful
Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID)
{ {
@ -54,21 +123,23 @@ Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID)
return p1; return p1;
else else
delete p1; delete p1;
Process *p2 = new NormalProcess(ID.pid,meminfo->meminfo); Process *p2 = new NormalProcess(ID.pid,meminfo->meminfo);
if(p2->isIdentified()) if(p2->isIdentified())
return p2; return p2;
else else
delete p2; delete p2;
#ifdef LINUX_BUILD
Process *p3 = new WineProcess(ID.pid,meminfo->meminfo); Process *p3 = new WineProcess(ID.pid,meminfo->meminfo);
if(p3->isIdentified()) if(p3->isIdentified())
return p3; return p3;
else else
delete p3; delete p3;
return 0; return 0;
#endif
} }
#ifdef LINUX_BUILD
void ProcessEnumerator::Private::EnumPIDs (vector <ProcessID> &PIDs) void ProcessEnumerator::Private::EnumPIDs (vector <ProcessID> &PIDs)
{ {
DIR *dir_p; DIR *dir_p;
@ -104,22 +175,6 @@ void ProcessEnumerator::Private::EnumPIDs (vector <ProcessID> &PIDs)
#endif #endif
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID)
{
Process *p1 = new SHMProcess(ID.pid,meminfo->meminfo);
if(p1->isIdentified())
return p1;
else
delete p1;
Process *p2 = new NormalProcess(ID.pid,meminfo->meminfo);
if(p2->isIdentified())
return p2;
else
delete p2;
return 0;
}
// some magic - will come in handy when we start doing debugger stuff on Windows // some magic - will come in handy when we start doing debugger stuff on Windows
bool EnableDebugPriv() bool EnableDebugPriv()
{ {
@ -199,19 +254,19 @@ void ProcessEnumerator::Private::EnumPIDs (vector <ProcessID> &PIDs)
} }
#endif #endif
bool ProcessEnumerator::Refresh( PROC_V * invalidated_processes) bool ProcessEnumerator::Refresh( BadProcesses* invalidated_processes)
{ {
// PIDs to process // PIDs to process
vector <ProcessID> PIDs; vector <ProcessID> PIDs;
// this will be the new process map // this will be the new process map
PID2PROC temporary; PID2PROC temporary;
// clear the vector other access // clear the vector
d->Processes.clear(); d->Processes.clear();
if(invalidated_processes) if(invalidated_processes)
invalidated_processes->clear(); invalidated_processes->clear();
d->EnumPIDs(PIDs); d->EnumPIDs(PIDs);
for(uint64_t i = 0; i < PIDs.size();i++) for(uint64_t i = 0; i < PIDs.size();i++)
{ {
ProcessID & PID = PIDs[i]; ProcessID & PID = PIDs[i];

@ -35,18 +35,36 @@ distribution.
namespace DFHack namespace DFHack
{ {
class Context; class Context;
class DFContextMgrPrivate; class BadContexts;
class Process;
class DFHACK_EXPORT ContextManager class DFHACK_EXPORT ContextManager
{ {
DFContextMgrPrivate * const d; class Private;
Private * const d;
public: public:
ContextManager(const std::string path_to_xml); ContextManager(const std::string path_to_xml);
~ContextManager(); ~ContextManager();
uint32_t Refresh(); uint32_t Refresh(BadContexts* bad_contexts = 0);
uint32_t size(); uint32_t size();
Context * operator[](uint32_t index); Context * operator[](uint32_t index);
Context * getSingleContext(); Context * getSingleContext();
void purge(void); void purge(void);
}; };
class DFHACK_EXPORT BadContexts
{
class Private;
Private * const d;
void push_back(Context * c);
friend class ContextManager;
public:
BadContexts();
~BadContexts();
bool Contains(Context* c);
bool Contains(Process* p);
uint32_t size();
void clear();
Context * operator[](uint32_t index);
};
} // namespace DFHack } // namespace DFHack
#endif // CONTEXTMANAGER_H_INCLUDED #endif // CONTEXTMANAGER_H_INCLUDED

@ -32,6 +32,7 @@ namespace DFHack
{ {
class memory_info; class memory_info;
class Process; class Process;
class BadProcesses;
/* /*
* Process manager * Process manager
*/ */
@ -42,11 +43,26 @@ namespace DFHack
public: public:
ProcessEnumerator( string path_to_xml ); ProcessEnumerator( string path_to_xml );
~ProcessEnumerator(); ~ProcessEnumerator();
bool Refresh(vector <Process *> * invalidated_processes = 0); bool Refresh(BadProcesses * invalidated_processes = 0);
bool findProcessess(); bool findProcessess();
uint32_t size(); uint32_t size();
Process * operator[](uint32_t index); Process * operator[](uint32_t index);
void purge(void); void purge(void);
}; };
class DFHACK_EXPORT BadProcesses
{
class Private;
Private * const d;
void push_back(Process * p);
friend class ProcessEnumerator;
public:
BadProcesses();
~BadProcesses();
bool Contains(Process* p);
bool excise (Process* p);
uint32_t size();
void clear();
Process * operator[](uint32_t index);
};
} }
#endif // PROCESSMANAGER_H_INCLUDED #endif // PROCESSMANAGER_H_INCLUDED

@ -13,42 +13,113 @@ using namespace std;
using namespace DFHack; using namespace DFHack;
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
#endif #endif
void printhelp ()
{
cout << "enter empty line for next try." << endl;
cout << "enter 'next' or 'n' for next test." << endl;
cout << "enter 'help' to show this text again." << endl;
}
int inputwait (const char * prompt)
{
inputwait_reset:
string command = "";
cout <<"[" << prompt << "]# ";
getline(cin, command);
if(command == "help")
{
printhelp();
goto inputwait_reset;
}
else if(command == "")
{
return 1;
}
else if(command == "next")
{
return 0;
}
else
{
cout << "Command not recognized. Try 'help' for a list of valid commands." << endl;
goto inputwait_reset;
}
return 0;
}
int main (void) int main (void)
{ {
vector<Process*> inval; printhelp();
ProcessEnumerator Penum("Memory.xml"); cout << endl;
memory_info * mem; // first test ProcessEnumerator and BadProcesses
for(int cnt = 0; cnt < 100; cnt++)
{ {
// make the ProcessEnumerator update its list of Processes cout << "Testing ProcessEnumerator" << endl;
// by passing the pointer to 'inval', we make it export expired ProcessEnumerator Penum("Memory.xml");
// processes instead of destroying them outright memory_info * mem;
// (processes expire when the OS kills them for whatever reason) do
Penum.Refresh(&inval);
int nProc = Penum.size();
int nInval = inval.size();
cout << "Processes:" << endl;
for(int i = 0; i < nProc; i++)
{ {
mem = Penum[i]->getDescriptor(); // make the ProcessEnumerator update its list of Processes
cout << "DF instance: " << Penum[i]->getPID() // by passing the pointer to 'inval', we make it export expired
<< ", " << mem->getVersion() << endl; // processes instead of destroying them outright
} // (processes expire when the OS kills them for whatever reason)
BadProcesses inval;
Penum.Refresh(&inval);
int nProc = Penum.size();
int nInval = inval.size();
cout << "Processes:" << endl;
for(int i = 0; i < nProc; i++)
{
mem = Penum[i]->getDescriptor();
cout << "DF instance: " << Penum[i]->getPID()
<< ", " << mem->getVersion() << endl;
}
cout << "Invalidated:" << endl; cout << "Invalidated:" << endl;
for(int i = 0; i < nInval; i++) for(int i = 0; i < nInval; i++)
{
mem = inval[i]->getDescriptor();
cout << "DF instance: " << inval[i]->getPID()
<< ", " << mem->getVersion() << endl;
}
}
while(inputwait("ProcessEnumerator"));
}
// next test ContextManager and BadContexts
{
cout << "Testing ProcessEnumerator" << endl;
ContextManager Cman("Memory.xml");
memory_info * mem;
do
{ {
mem = inval[i]->getDescriptor(); // make the ProcessEnumerator update its list of Processes
cout << "DF instance: " << inval[i]->getPID() // by passing the pointer to 'inval', we make it export expired
<< ", " << mem->getVersion() << endl; // processes instead of destroying them outright
// we own the expired process, we must take care of freeing its resources // (processes expire when the OS kills them for whatever reason)
delete inval[i]; BadContexts inval;
Cman.Refresh(&inval);
int nCont = Cman.size();
int nInval = inval.size();
cout << "Contexts:" << endl;
for(int i = 0; i < nCont; i++)
{
mem = Cman[i]->getMemoryInfo();
cout << "DF instance: " << Cman[i]->getProcess()->getPID()
<< ", " << mem->getVersion() << endl;
}
cout << "Invalidated:" << endl;
for(int i = 0; i < nInval; i++)
{
mem = inval[i]->getMemoryInfo();
cout << "DF instance: " << inval[i]->getProcess()->getPID()
<< ", " << mem->getVersion() << endl;
}
} }
cout << "<-* Press Enter to refresh *->" << endl << endl; while(inputwait("ContextManager"));
cin.ignore();
} }
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl; cout << "Done. Press any key to continue" << endl;
cin.ignore(); cin.ignore();