diff --git a/library/DFContextManager.cpp b/library/DFContextManager.cpp index 636325bdc..605a240ba 100644 --- a/library/DFContextManager.cpp +++ b/library/DFContextManager.cpp @@ -41,23 +41,90 @@ distribution. using namespace DFHack; namespace DFHack { - class DFContextMgrPrivate + class ContextManager::Private { public: - DFContextMgrPrivate(){}; - ~DFContextMgrPrivate(){}; + Private(){}; + ~Private(){}; string xml; // path to xml vector contexts; ProcessEnumerator * pEnum; }; } +class DFHack::BadContexts::Private +{ + public: + Private(){}; + vector 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 += "/"; d->xml += path_to_xml; + d->pEnum = new ProcessEnumerator(d->xml); } ContextManager::~ContextManager() @@ -66,26 +133,76 @@ ContextManager::~ContextManager() delete d; } -uint32_t ContextManager::Refresh() +uint32_t ContextManager::Refresh( BadContexts* bad_contexts ) { - purge(); - if(d->pEnum != 0) - d->pEnum = new ProcessEnumerator(d->xml); - else + // handle expired processes, remove stale Contexts { - delete d->pEnum; - d->pEnum = new ProcessEnumerator(d->xml); + BadProcesses expired; + // get new list od living and expired Process objects + d->pEnum->Refresh(&expired); + + // scan expired, kill contexts if necessary + vector ::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 numContexts = d->contexts.size(); + vector newContexts; + // enumerate valid processes for(int i = 0; i < numProcesses; i++) { - Context * c = new Context(d->pEnum->operator[](i)); - d->contexts.push_back(c); + Process * test = d->pEnum->operator[](i); + 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(); } + uint32_t ContextManager::size() { return d->contexts.size(); diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index 67132917d..8c537c13b 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -44,7 +44,76 @@ class DFHack::ProcessEnumerator::Private void EnumPIDs (vector &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::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 Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) { @@ -54,21 +123,23 @@ Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) return p1; else delete p1; - + Process *p2 = new NormalProcess(ID.pid,meminfo->meminfo); if(p2->isIdentified()) return p2; else delete p2; - +#ifdef LINUX_BUILD Process *p3 = new WineProcess(ID.pid,meminfo->meminfo); if(p3->isIdentified()) return p3; else delete p3; return 0; +#endif } +#ifdef LINUX_BUILD void ProcessEnumerator::Private::EnumPIDs (vector &PIDs) { DIR *dir_p; @@ -104,22 +175,6 @@ void ProcessEnumerator::Private::EnumPIDs (vector &PIDs) #endif #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 bool EnableDebugPriv() { @@ -199,19 +254,19 @@ void ProcessEnumerator::Private::EnumPIDs (vector &PIDs) } #endif -bool ProcessEnumerator::Refresh( PROC_V * invalidated_processes) +bool ProcessEnumerator::Refresh( BadProcesses* invalidated_processes) { // PIDs to process vector PIDs; // this will be the new process map PID2PROC temporary; - // clear the vector other access + // clear the vector d->Processes.clear(); if(invalidated_processes) invalidated_processes->clear(); - + d->EnumPIDs(PIDs); - + for(uint64_t i = 0; i < PIDs.size();i++) { ProcessID & PID = PIDs[i]; diff --git a/library/include/dfhack/DFContextManager.h b/library/include/dfhack/DFContextManager.h index 3337755d7..acf738bca 100644 --- a/library/include/dfhack/DFContextManager.h +++ b/library/include/dfhack/DFContextManager.h @@ -35,18 +35,36 @@ distribution. namespace DFHack { class Context; - class DFContextMgrPrivate; + class BadContexts; + class Process; + class DFHACK_EXPORT ContextManager { - DFContextMgrPrivate * const d; + class Private; + Private * const d; public: ContextManager(const std::string path_to_xml); ~ContextManager(); - uint32_t Refresh(); + uint32_t Refresh(BadContexts* bad_contexts = 0); uint32_t size(); Context * operator[](uint32_t index); Context * getSingleContext(); 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 #endif // CONTEXTMANAGER_H_INCLUDED diff --git a/library/include/dfhack/DFProcessEnumerator.h b/library/include/dfhack/DFProcessEnumerator.h index 0ef9b98ba..4e618ae27 100644 --- a/library/include/dfhack/DFProcessEnumerator.h +++ b/library/include/dfhack/DFProcessEnumerator.h @@ -32,6 +32,7 @@ namespace DFHack { class memory_info; class Process; + class BadProcesses; /* * Process manager */ @@ -42,11 +43,26 @@ namespace DFHack public: ProcessEnumerator( string path_to_xml ); ~ProcessEnumerator(); - bool Refresh(vector * invalidated_processes = 0); + bool Refresh(BadProcesses * invalidated_processes = 0); bool findProcessess(); uint32_t size(); Process * operator[](uint32_t index); 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 diff --git a/tools/examples/processenum.cpp b/tools/examples/processenum.cpp index 1be065da7..1910ca733 100644 --- a/tools/examples/processenum.cpp +++ b/tools/examples/processenum.cpp @@ -13,42 +13,113 @@ using namespace std; using namespace DFHack; #ifndef LINUX_BUILD #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) { - vector inval; - ProcessEnumerator Penum("Memory.xml"); - memory_info * mem; - for(int cnt = 0; cnt < 100; cnt++) + printhelp(); + cout << endl; + // first test ProcessEnumerator and BadProcesses { - // make the ProcessEnumerator update its list of Processes - // by passing the pointer to 'inval', we make it export expired - // processes instead of destroying them outright - // (processes expire when the OS kills them for whatever reason) - Penum.Refresh(&inval); - int nProc = Penum.size(); - int nInval = inval.size(); - - cout << "Processes:" << endl; - for(int i = 0; i < nProc; i++) + cout << "Testing ProcessEnumerator" << endl; + ProcessEnumerator Penum("Memory.xml"); + memory_info * mem; + do { - mem = Penum[i]->getDescriptor(); - cout << "DF instance: " << Penum[i]->getPID() - << ", " << mem->getVersion() << endl; - } + // make the ProcessEnumerator update its list of Processes + // by passing the pointer to 'inval', we make it export expired + // 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; - for(int i = 0; i < nInval; i++) + cout << "Invalidated:" << endl; + 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(); - cout << "DF instance: " << inval[i]->getPID() - << ", " << mem->getVersion() << endl; - // we own the expired process, we must take care of freeing its resources - delete inval[i]; + // make the ProcessEnumerator update its list of Processes + // by passing the pointer to 'inval', we make it export expired + // processes instead of destroying them outright + // (processes expire when the OS kills them for whatever reason) + 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; - cin.ignore(); + while(inputwait("ContextManager")); } - #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore();