ProcessEnumerator tracks processes properly now.

develop
Petr Mrázek 2010-05-28 05:18:32 +02:00
parent bd4456b5f6
commit ab40868b29
8 changed files with 401 additions and 312 deletions

@ -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)

@ -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 <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <time.h>
#include "shms.h"
using namespace DFHack;
class DFHack::ProcessEnumerator::Private
{
public:
Private(){};
MemInfoManager *meminfo;
std::vector<Process *> 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;
}

@ -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<Process *> 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;
}

@ -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<Process *> PROC_V;
typedef std::map<ProcessID, Process*> PID2PROC;
class DFHack::ProcessEnumerator::Private
{
public:
Private(){};
MemInfoManager *meminfo;
PROC_V Processes;
PID2PROC ProcMap;
Process *GetProcessObject(ProcessID ID);
void EnumPIDs (vector <ProcessID> &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 <ProcessID> &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 <ProcessID> &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 <ProcessID> 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;
}

@ -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
}

@ -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 <Process *> * invalidated_processes = 0);
bool findProcessess();
uint32_t size();
Process * operator[](uint32_t index);

@ -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")

@ -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 <iostream>
#include <climits>
#include <vector>
#include <ctime>
using namespace std;
#include <DFHack.h>
#include <dfhack/DFProcessEnumerator.h>
using namespace DFHack;
#ifndef LINUX_BUILD
#endif
int main (void)
{
vector<Process*> 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;
}