From ab40868b2905ae4905595cd346d68f6365273194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 28 May 2010 05:18:32 +0200 Subject: [PATCH] ProcessEnumerator tracks processes properly now. --- library/CMakeLists.txt | 3 +- library/DFProcessEnumerator-linux.cpp | 135 -------- library/DFProcessEnumerator-windows.cpp | 147 --------- library/DFProcessEnumerator.cpp | 318 +++++++++++++++++++ library/include/dfhack/DFProcess.h | 43 ++- library/include/dfhack/DFProcessEnumerator.h | 6 +- tools/examples/CMakeLists.txt | 4 + tools/examples/processenum.cpp | 57 ++++ 8 files changed, 401 insertions(+), 312 deletions(-) delete mode 100644 library/DFProcessEnumerator-linux.cpp delete mode 100644 library/DFProcessEnumerator-windows.cpp create mode 100644 library/DFProcessEnumerator.cpp create mode 100644 tools/examples/processenum.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index ce44079cf..fc267e789 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -57,6 +57,7 @@ DFMemInfo.cpp DFMemInfoManager.cpp DFContextManager.cpp DFContext.cpp +DFProcessEnumerator.cpp ContextShared.cpp DFContext_C.cpp DFTypes_C.cpp @@ -105,14 +106,12 @@ DFProcess-linux.cpp DFProcess-linux-SHM.cpp DFProcess-linux-wine.cpp modules/WindowIO-linux.cpp -DFProcessEnumerator-linux.cpp ) SET(PROJECT_SRCS_WINDOWS DFProcess-windows.cpp DFProcess-windows-SHM.cpp modules/WindowIO-windows.cpp -DFProcessEnumerator-windows.cpp ) IF(UNIX) diff --git a/library/DFProcessEnumerator-linux.cpp b/library/DFProcessEnumerator-linux.cpp deleted file mode 100644 index 3ee14ef80..000000000 --- a/library/DFProcessEnumerator-linux.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* -www.sourceforge.net/projects/dfhack -Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "Internal.h" -#include "dfhack/DFProcessEnumerator.h" -#include "dfhack/DFProcess.h" -#include "dfhack/DFMemInfo.h" -#include "dfhack/DFMemInfoManager.h" -#include -#include -#include -#include -#include "shms.h" - -using namespace DFHack; - -class DFHack::ProcessEnumerator::Private -{ - public: - Private(){}; - MemInfoManager *meminfo; - std::vector processes; -}; - -bool ProcessEnumerator::findProcessess() -{ - DIR *dir_p; - struct dirent *dir_entry_p; - // Open /proc/ directory - dir_p = opendir("/proc/"); - // Reading /proc/ entries - while(NULL != (dir_entry_p = readdir(dir_p))) - { - // Only PID folders (numbers) - if (strspn(dir_entry_p->d_name, "0123456789") != strlen(dir_entry_p->d_name)) - { - continue; - } - Process *p1 = new SHMProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); - if(p1->isIdentified()) - { - d->processes.push_back(p1); - continue; - } - else - { - delete p1; - } - Process *p2 = new NormalProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); - if(p2->isIdentified()) - { - d->processes.push_back(p2); - continue; - } - else - { - delete p2; - } - Process *p3 = new WineProcess(atoi(dir_entry_p->d_name),d->meminfo->meminfo); - if(p3->isIdentified()) - { - d->processes.push_back(p3); - continue; - } - else - { - delete p3; - } - - } - closedir(dir_p); - // return value depends on if we found some DF processes - if(d->processes.size()) - { - return true; - } - return false; -} - -uint32_t ProcessEnumerator::size() -{ - return d->processes.size(); -} - - -Process * ProcessEnumerator::operator[](uint32_t index) -{ - assert(index < d->processes.size()); - return d->processes[index]; -} - - -ProcessEnumerator::ProcessEnumerator( string path_to_xml ) -: d(new Private()) -{ - d->meminfo = new MemInfoManager(path_to_xml); -} - -void ProcessEnumerator::purge() -{ - for(uint32_t i = 0;i < d->processes.size();i++) - { - delete d->processes[i]; - } - d->processes.clear(); -} - -ProcessEnumerator::~ProcessEnumerator() -{ - // delete all processes - purge(); - delete d->meminfo; - delete d; -} diff --git a/library/DFProcessEnumerator-windows.cpp b/library/DFProcessEnumerator-windows.cpp deleted file mode 100644 index bec67361d..000000000 --- a/library/DFProcessEnumerator-windows.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* -www.sourceforge.net/projects/dfhack -Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "Internal.h" -#include "dfhack/DFProcessEnumerator.h" -#include "dfhack/DFProcess.h" -#include "dfhack/DFMemInfo.h" -#include "dfhack/DFMemInfoManager.h" -using namespace DFHack; - -class DFHack::ProcessEnumerator::Private -{ - public: - Private(){}; - MemInfoManager *meminfo; - std::vector processes; -}; - -// some magic - will come in handy when we start doing debugger stuff on Windows -bool EnableDebugPriv() -{ - bool bRET = FALSE; - TOKEN_PRIVILEGES tp; - HANDLE hToken; - - if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) - { - if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - { - if (hToken != INVALID_HANDLE_VALUE) - { - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - tp.PrivilegeCount = 1; - if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, 0, 0)) - { - bRET = TRUE; - } - CloseHandle(hToken); - } - } - } - return bRET; -} - -// WINDOWS version of the process finder -bool ProcessEnumerator::findProcessess() -{ - // Get the list of process identifiers. - DWORD ProcArray[2048], memoryNeeded, numProccesses; - //EnableDebugPriv(); - if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) - { - cout << "EnumProcesses fail'd" << endl; - return false; - } - - // Calculate how many process identifiers were returned. - numProccesses = memoryNeeded / sizeof(DWORD); - EnableDebugPriv(); - - // iterate through processes - for ( int i = 0; i < (int)numProccesses; i++ ) - { - Process *p = new SHMProcess(ProcArray[i],d->meminfo->meminfo); - if(p->isIdentified()) - { - d->processes.push_back(p); - continue; - } - else - { - delete p; - p = 0; - } - p = new NormalProcess(ProcArray[i],d->meminfo->meminfo); - if(p->isIdentified()) - { - d->processes.push_back(p); - continue; - } - else - { - delete p; - p = 0; - } - } - if(d->processes.size()) - return true; - return false; -} - -uint32_t ProcessEnumerator::size() -{ - return d->processes.size(); -}; - - -Process * ProcessEnumerator::operator[](uint32_t index) -{ - assert(index < d->processes.size()); - return d->processes[index]; -}; - - -ProcessEnumerator::ProcessEnumerator( string path_to_xml ) -: d(new Private()) -{ - d->meminfo = new MemInfoManager(path_to_xml); -} - -void ProcessEnumerator::purge() -{ - for(uint32_t i = 0;i < d->processes.size();i++) - { - delete d->processes[i]; - } - d->processes.clear(); -} - -ProcessEnumerator::~ProcessEnumerator() -{ - // delete all processes - purge(); - delete d->meminfo; - delete d; -} diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp new file mode 100644 index 000000000..67132917d --- /dev/null +++ b/library/DFProcessEnumerator.cpp @@ -0,0 +1,318 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" +#include "dfhack/DFProcessEnumerator.h" +#include "dfhack/DFProcess.h" +#include "dfhack/DFMemInfo.h" +#include "dfhack/DFMemInfoManager.h" + +using namespace DFHack; + +typedef std::vector PROC_V; +typedef std::map PID2PROC; + +class DFHack::ProcessEnumerator::Private +{ + public: + Private(){}; + MemInfoManager *meminfo; + PROC_V Processes; + PID2PROC ProcMap; + Process *GetProcessObject(ProcessID ID); + void EnumPIDs (vector &PIDs); +}; + +#ifdef LINUX_BUILD +//FIXME: wasteful +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; + + Process *p3 = new WineProcess(ID.pid,meminfo->meminfo); + if(p3->isIdentified()) + return p3; + else + delete p3; + return 0; +} + +void ProcessEnumerator::Private::EnumPIDs (vector &PIDs) +{ + DIR *dir_p; + struct dirent *dir_entry_p; + struct stat st; + char fullname[512]; + fullname[0] = 0; + PIDs.clear(); // make sure the vector is clear + + // Open /proc/ directory + dir_p = opendir("/proc/"); + // Reading /proc/ entries + while(NULL != (dir_entry_p = readdir(dir_p))) + { + // Only PID folders (numbers) + if (strspn(dir_entry_p->d_name, "0123456789") != strlen(dir_entry_p->d_name)) + { + continue; + } + sprintf(fullname, "/proc/%s", dir_entry_p->d_name); + int ierr = stat (fullname, &st); + if (ierr != 0) + { + printf("Cannot stat %s: ierr= %d\n", fullname, ierr); + continue; + } + uint64_t Pnum = atoi(dir_entry_p->d_name); + uint64_t ctime = st.st_ctime; + PIDs.push_back(ProcessID(ctime,Pnum)); + } + closedir(dir_p); +} +#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() +{ + bool bRET = FALSE; + TOKEN_PRIVILEGES tp; + HANDLE hToken; + + if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) + { + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + if (hToken != INVALID_HANDLE_VALUE) + { + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tp.PrivilegeCount = 1; + if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, 0, 0)) + { + bRET = TRUE; + } + CloseHandle(hToken); + } + } + } + return bRET; +} + +// Convert Windows FileTime structs to POSIX timestamp +// from http://frenk.wordpress.com/2009/12/14/convert-filetime-to-unix-timestamp/ +uint64_t FileTime_to_POSIX(FILETIME ft) +{ + // takes the last modified date + LARGE_INTEGER date, adjust; + date.HighPart = ft.dwHighDateTime; + date.LowPart = ft.dwLowDateTime; + + // 100-nanoseconds = milliseconds * 10000 + adjust.QuadPart = 11644473600000 * 10000; + + // removes the diff between 1970 and 1601 + date.QuadPart -= adjust.QuadPart; + + // converts back from 100-nanoseconds to seconds + return date.QuadPart / 10000000; +} + +void ProcessEnumerator::Private::EnumPIDs (vector &PIDs) +{ + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + PIDs.clear(); // make sure the vector is clear + + // Get the list of process identifiers. + DWORD ProcArray[2048], memoryNeeded, numProccesses; + //EnableDebugPriv(); + if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) + { + cout << "EnumProcesses fail'd" << endl; + return; + } + // Calculate how many process identifiers were returned. + numProccesses = memoryNeeded / sizeof(DWORD); + EnableDebugPriv(); + // iterate through processes + for ( int i = 0; i < (int)numProccesses; i++ ) + { + HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION, false, ProcArray[i]); + if(!proc) + continue; + if(GetProcessTimes(proc, &ftCreate, &ftExit, &ftKernel, &ftUser)) + { + uint64_t ctime = FileTime_to_POSIX(ftCreate); + uint64_t Pnum = ProcArray[i]; + PIDs.push_back(ProcessID(ctime,Pnum)); + } + CloseHandle(proc); + } +} +#endif + +bool ProcessEnumerator::Refresh( PROC_V * invalidated_processes) +{ + // PIDs to process + vector PIDs; + // this will be the new process map + PID2PROC temporary; + // clear the vector other access + 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]; + // check if we know about the OS process already + PID2PROC::iterator found= d->ProcMap.find(PID); + if( found != d->ProcMap.end()) + { + // we do + // check if it does have a DFHack Process object associated with it + Process * p = (*found).second; + if(p) + { + // add it back to the vector we export + d->Processes.push_back(p); + } + // remove the OS Process from ProcMap + d->ProcMap.erase(found); + // add the OS Process to what will be the new ProcMap + temporary[PID] = p; + } + else + { + // an OS process we don't know yet! + // try to make a DFHack Process object for it + if(Process*p = d->GetProcessObject(PID)) + { + // allright. this is something that can be used + d->Processes.push_back(p); + temporary[PID] = p; + } + else + { + // just a process. we track it anyway. Why not. + temporary[PID] = 0; + } + } + } + // now the vector we export is filled again and a temporary map with valid processes is created. + // we iterate over the old Process map and destroy all the processes that are dead. + for(PID2PROC::const_iterator idx = d->ProcMap.begin(); idx != d->ProcMap.end();++idx) + { + Process * p = (*idx).second; + if(p) + { + if(invalidated_processes) + { + invalidated_processes->push_back(p); + } + else + { + delete p; + } + } + } + d->ProcMap.swap(temporary); + // return value depends on if we found some DF processes + if(d->Processes.size()) + { + return true; + } + return false; +} + +bool ProcessEnumerator::findProcessess() +{ + return Refresh(); +} + +uint32_t ProcessEnumerator::size() +{ + return d->Processes.size(); +} + + +Process * ProcessEnumerator::operator[](uint32_t index) +{ + assert(index < d->Processes.size()); + return d->Processes[index]; +} + + +ProcessEnumerator::ProcessEnumerator( string path_to_xml ) +: d(new Private()) +{ + d->meminfo = new MemInfoManager(path_to_xml); +} + +void ProcessEnumerator::purge() +{ + for(uint32_t i = 0;i < d->Processes.size();i++) + { + delete d->Processes[i]; + } + d->ProcMap.clear(); + d->Processes.clear(); +} + +ProcessEnumerator::~ProcessEnumerator() +{ + // delete all processes + purge(); + delete d->meminfo; + delete d; +} diff --git a/library/include/dfhack/DFProcess.h b/library/include/dfhack/DFProcess.h index 686d7cc40..2fc2620d0 100644 --- a/library/include/dfhack/DFProcess.h +++ b/library/include/dfhack/DFProcess.h @@ -35,6 +35,25 @@ namespace DFHack class Process; class Window; + struct ProcessID + { + ProcessID(const uint64_t _time, const uint64_t _pid): time(_time), pid(_pid){}; + bool operator==(const ProcessID &other) const + { + return (other.time == time && other.pid == pid); + } + bool operator< (const ProcessID& ms) const + { + if (time < ms.time) + return true; + else if(time == ms.time) + return pid < ms.pid ; + return false; + } + uint64_t time; + uint64_t pid; + }; + // structure describing a memory range struct DFHACK_EXPORT t_memrange { @@ -130,12 +149,6 @@ namespace DFHack virtual char * getSHMStart (void) = 0; // set a SHM command and wait for a response, return 0 on error or throw exception virtual bool SetAndWait (uint32_t state) = 0; - /* - // wait while SHM command == state. returns 0 without the SHM - virtual bool waitWhile (uint32_t state) = 0; - // set SHM command. - virtual void setCmd (uint32_t newstate) = 0; - */ }; class DFHACK_EXPORT NormalProcess : virtual public Process @@ -200,12 +213,6 @@ namespace DFHack char * getSHMStart (void){return 0;}; // set a SHM command and wait for a response bool SetAndWait (uint32_t state){return false;}; - /* - // wait for a SHM state. returns 0 without the SHM - bool waitWhile (uint32_t state){return false;}; - // set SHM command. - void setCmd (uint32_t newstate){}; - */ }; class DFHACK_EXPORT SHMProcess : virtual public Process @@ -270,12 +277,6 @@ namespace DFHack // get the SHM start if available char * getSHMStart (void); bool SetAndWait (uint32_t state); - /* - // wait for a SHM state. returns 0 without the SHM - bool waitWhile (uint32_t state); - // set SHM command. - void setCmd (uint32_t newstate); - */ }; #ifdef LINUX_BUILD @@ -340,12 +341,6 @@ namespace DFHack // get the SHM start if available char * getSHMStart (void){return 0;}; bool SetAndWait (uint32_t state){return false;}; - /* - // wait for a SHM state. returns 0 without the SHM - bool waitWhile (uint32_t state){return false;}; - // set SHM command. - void setCmd (uint32_t newstate){}; - */ }; #endif } diff --git a/library/include/dfhack/DFProcessEnumerator.h b/library/include/dfhack/DFProcessEnumerator.h index c40253c15..0ef9b98ba 100644 --- a/library/include/dfhack/DFProcessEnumerator.h +++ b/library/include/dfhack/DFProcessEnumerator.h @@ -28,13 +28,10 @@ distribution. #include "DFPragma.h" #include "DFExport.h" -class TiXmlElement; - namespace DFHack { class memory_info; class Process; - /* * Process manager */ @@ -43,8 +40,9 @@ namespace DFHack class Private; Private * const d; public: - ProcessEnumerator( string path_to_xml); + ProcessEnumerator( string path_to_xml ); ~ProcessEnumerator(); + bool Refresh(vector * invalidated_processes = 0); bool findProcessess(); uint32_t size(); Process * operator[](uint32_t index); diff --git a/tools/examples/CMakeLists.txt b/tools/examples/CMakeLists.txt index 3662181b4..190dff3cc 100644 --- a/tools/examples/CMakeLists.txt +++ b/tools/examples/CMakeLists.txt @@ -47,6 +47,10 @@ TARGET_LINK_LIBRARIES(dftreedump dfhack) ADD_EXECUTABLE(dfspatterdump spatterdump.cpp) TARGET_LINK_LIBRARIES(dfspatterdump dfhack) +# processenum - demonstrates the use of ProcessEnumerator +ADD_EXECUTABLE(dfprocessenum processenum.cpp) +TARGET_LINK_LIBRARIES(dfprocessenum dfhack) + IF(UNIX) SET(CURSES_NEED_WIDE "YES") SET(CURSES_NEED_NCURSES "NO") diff --git a/tools/examples/processenum.cpp b/tools/examples/processenum.cpp new file mode 100644 index 000000000..1be065da7 --- /dev/null +++ b/tools/examples/processenum.cpp @@ -0,0 +1,57 @@ +// Demonstrates the use of ProcessEnumerator +// queries the Enumerator for all DF Processes on user input. Prints them to the terminal. +// also tracks processes that were invalidated + +#include +#include +#include +#include +using namespace std; + +#include +#include +using namespace DFHack; +#ifndef LINUX_BUILD +#endif +int main (void) +{ + vector inval; + ProcessEnumerator Penum("Memory.xml"); + memory_info * mem; + for(int cnt = 0; cnt < 100; cnt++) + { + // 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++) + { + mem = Penum[i]->getDescriptor(); + cout << "DF instance: " << Penum[i]->getPID() + << ", " << mem->getVersion() << endl; + } + + cout << "Invalidated:" << endl; + for(int i = 0; i < nInval; i++) + { + 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]; + } + cout << "<-* Press Enter to refresh *->" << endl << endl; + cin.ignore(); + } + + #ifndef LINUX_BUILD + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + #endif + return 0; +}