suspend and continue calls, a method for reading creatures within a volume

develop
Petr Mrázek 2009-11-16 16:47:22 +00:00
parent b8c9a36897
commit 47b61a907d
8 changed files with 215 additions and 162 deletions

@ -69,6 +69,7 @@ using namespace std;
#include <winbase.h>
#include <winnt.h>
#include <psapi.h>
#include <tlhelp32.h>
#endif
#ifndef BUILD_DFHACK_LIB

@ -587,7 +587,7 @@ uint32_t API::InitReadBuildings(vector <string> &v_buildingtypes)
// read one building
bool API::ReadBuilding(const uint32_t &index, t_building & building)
bool API::ReadBuilding(const int32_t &index, t_building & building)
{
assert(d->buildingsInited);
uint32_t temp;
@ -637,7 +637,7 @@ uint32_t API::InitReadConstructions()
}
bool API::ReadConstruction(const uint32_t &index, t_construction & construction)
bool API::ReadConstruction(const int32_t &index, t_construction & construction)
{
assert(d->constructionsInited);
t_construction_df40d c_40d;
@ -678,7 +678,7 @@ uint32_t API::InitReadVegetation()
}
bool API::ReadVegetation(const uint32_t &index, t_tree_desc & shrubbery)
bool API::ReadVegetation(const int32_t &index, t_tree_desc & shrubbery)
{
assert(d->vegetationInited);
uint32_t temp;
@ -955,7 +955,37 @@ void API::Private::getLaborsByAddress(const uint32_t &address, vector<t_labor> &
labors.push_back(tempLabor);
}
}*/
bool API::ReadCreature(const uint32_t &index, t_creature & furball)
// returns index of creature actually read or -1 if no creature can be found
int32_t API::ReadCreatureInBox(int32_t index, t_creature & furball,
const uint16_t &x1, const uint16_t &y1,const uint16_t &z1,
const uint16_t &x2, const uint16_t &y2,const uint16_t &z2)
{
uint16_t coords[3];
assert(d->creaturesInited);
uint32_t temp;
uint32_t size = d->p_cre->getSize();
while (index < size)
{
// read pointer from vector at position
d->p_cre->read(index,(uint8_t *)&temp);
Mread(temp + d->creature_pos_offset, 3 * sizeof(uint16_t), (uint8_t *) &coords);
if(coords[0] >= x1 && coords[0] < x2)
{
if(coords[1] >= y1 && coords[1] < y2)
{
if(coords[2] >= z1 && coords[2] < z2)
{
ReadCreature(index, furball);
return index;
}
}
}
index++;
}
}
bool API::ReadCreature(const int32_t &index, t_creature & furball)
{
assert(d->creaturesInited);
uint32_t temp;
@ -1010,6 +1040,7 @@ bool API::ReadCreature(const uint32_t &index, t_creature & furball)
MreadByte(temp + d->creature_sex_offset, furball.sex);
return true;
}
//FIXME: this just isn't enough
void API::InitReadNameTables()
{
@ -1101,6 +1132,19 @@ bool API::isAttached()
return d->dm != NULL;
}
bool API::Suspend()
{
return d->p->suspend();
}
bool API::Resume()
{
return d->p->resume();
}
bool API::isSuspended()
{
return d->p->isSuspended();
}
void API::ReadRaw (const uint32_t &offset, const uint32_t &size, uint8_t *target)
{
Mread(offset, size, target);

@ -62,6 +62,10 @@ namespace DFHack
bool Attach();
bool Detach();
bool isAttached();
bool Suspend();
bool Resume();
bool isSuspended();
/**
* Matgloss. next four methods look very similar. I could use two and move the processing one level up...
* I'll keep it like this, even with the code duplication as it will hopefully get more features and separate data types later.
@ -140,19 +144,19 @@ namespace DFHack
* Buildings, constructions, plants, all pretty straighforward. InitReadBuildings returns all the building types as a mapping between a numeric values and strings
*/
uint32_t InitReadConstructions();
bool ReadConstruction(const uint32_t &index, t_construction & construction);
bool ReadConstruction(const int32_t &index, t_construction & construction);
void FinishReadConstructions();
uint32_t InitReadBuildings(vector <string> &v_buildingtypes);
bool ReadBuilding(const uint32_t &index, t_building & building);
bool ReadBuilding(const int32_t &index, t_building & building);
void FinishReadBuildings();
uint32_t InitReadVegetation();
bool ReadVegetation(const uint32_t &index, t_tree_desc & shrubbery);
bool ReadVegetation(const int32_t &index, t_tree_desc & shrubbery);
void FinishReadVegetation();
uint32_t InitReadCreatures();
bool ReadCreature(const uint32_t &index, t_creature & furball);
bool ReadCreature(const int32_t &index, t_creature & furball);
void FinishReadCreatures();
void ReadRaw (const uint32_t &offset, const uint32_t &size, uint8_t *target);

@ -31,6 +31,7 @@ Process::Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid
my_handle = ph;
my_pid = pid;
attached = false;
suspended = false;
}
@ -70,36 +71,10 @@ void Process::setMemFile(const string & memf)
*/
/*
//TODO: rewrite this. really. It's ugly as hell.
bool isStopped(pid_t pid)
bool Process::getThreadIDs(vector<uint32_t> & threads )
{
char filename[256];
sprintf(filename, "/proc/%d/status", pid);
// evil mess, that's a fitting name for this thing
FILE* evil = fopen(filename,"rb");
if(evil)
{
// zlo means evil in czech.
char zlo[64];
char test;
// read first line, ignore
fgets(zlo,64,evil);
// read second line
fgets(zlo,64,evil);
sscanf(zlo,"State: %c",&test);
fclose(evil);
if(test == 'T')
{
return true;
}
return false;
}
cerr << "couldn't open file " << filename << "assuming process stopped" << endl;
return true;
return false;
}
*/
void Process::getMemRanges( vector<t_memrange> & ranges )
{
@ -128,36 +103,62 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
}
}
bool Process::attach()
bool Process::suspend()
{
int status;
//cout << "Attach: start" << endl;
// check if another process is attached
if(g_pProcess != NULL)
if(!attached)
return false;
if(suspended)
return true;
if (kill(my_handle, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
/*
if(!isStopped(my_handle))
while(true)
{
// kill (SIGSTOP)
status = kill(my_handle,SIGSTOP);
//cout << "sent SIGSTOP" << endl;
if(status == -1)
// we wait on the pid
pid_t w = waitpid(my_handle, &status, 0);
if (w == -1)
{
perror("kill(SIGSTOP)");
// child died
perror("DF exited during suspend call");
return false;
}
// wait for the process to stop
while (!isStopped(my_handle))
// stopped -> let's continue
if (WIFSTOPPED(status))
{
usleep(5000);
//cout << "wait step" << endl;
break;
}
}
*/
//usleep(10000);
//cout << "Attach: after conditional stop" << endl;
suspended = true;
}
bool Process::resume()
{
int status;
if(!attached)
return false;
if(!suspended)
return true;
if (ptrace(PTRACE_CONT, my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
suspended = false;
}
bool Process::attach()
{
int status;
if(g_pProcess != NULL)
{
return false;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , my_handle, NULL, NULL) == -1)
{
@ -166,8 +167,6 @@ bool Process::attach()
cerr << "attach failed on pid " << my_handle << endl;
return false;
}
//usleep(10000);
//cout << "Attach: after ptrace" << endl;
while(true)
{
// we wait on the pid
@ -176,8 +175,6 @@ bool Process::attach()
{
// child died
perror("wait inside attach()");
//LOGC << "child died?";
//exit(-1);
return false;
}
// stopped -> let's continue
@ -186,7 +183,7 @@ bool Process::attach()
break;
}
}
// cout << "Managed to attach to pid " << my_handle << endl;
suspended = true;
int proc_pid_mem = open(memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
@ -203,7 +200,6 @@ bool Process::attach()
g_ProcessHandle = my_handle;
g_ProcessMemFile = proc_pid_mem;
//cout << "Attach: after opening /proc/"<< my_handle <<"/mem" << endl;
return true; // we are attached
}
}
@ -211,9 +207,10 @@ bool Process::attach()
bool Process::detach()
{
if(!attached) return false;
if(!suspended) suspend();
int result = 0;
//cout << "detach: start" << endl;
result = close(g_ProcessMemFile);// close /proc/PID/mem
// close /proc/PID/mem
result = close(g_ProcessMemFile);
if(result == -1)
{
cerr << "couldn't close /proc/"<< my_handle <<"/mem" << endl;
@ -222,7 +219,7 @@ bool Process::detach()
}
else
{
//cout << "detach: after closing /proc/"<< my_handle <<"/mem" << endl;
// detach
g_ProcessMemFile = -1;
result = ptrace(PTRACE_DETACH, my_handle, NULL, NULL);
if(result == -1)
@ -233,15 +230,9 @@ bool Process::detach()
}
else
{
// cout << "detach: after detaching from "<< my_handle << endl;
attached = false;
g_pProcess = NULL;
g_ProcessHandle = 0;
// continue, wait for it to recover
// kill(my_handle,SIGCONT);
// while (isStopped(my_handle));
//usleep(10000);
// we finish
return true;
}
}
@ -260,7 +251,45 @@ void Process::freeResources()
* WINDOWS PART
*/
//FIXME: should support stopping and resuming the process
bool Process::suspend()
{
if(!attached)
return false;
if(suspended)
{
return true;
}
vector<uint32_t> threads;
getThreadIDs( threads );
for(int i = 0; i < /*threads.size()*/ 1; i++)
{
HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[i]);
SuspendThread(thread_handle);
CloseHandle(thread_handle);
}
suspended = true;
return true;
}
bool Process::resume()
{
if(!attached)
return false;
if(!suspended)
{
return true;
}
vector<uint32_t> threads;
getThreadIDs( threads );
for(int i = 0; i < /* threads.size() */ 1; i++)
{
HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[i]);
ResumeThread(thread_handle);
CloseHandle(thread_handle);
}
suspended = false;
return true;
}
bool Process::attach()
{
@ -268,17 +297,12 @@ bool Process::attach()
{
return false;
}
/*
if(DebugActiveProcess(my_pid))
{
*/
attached = true;
g_pProcess = this;
g_ProcessHandle = my_handle;
attached = true;
g_pProcess = this;
g_ProcessHandle = my_handle;
suspend();
return true;
/*}
return false;*/
return true;
}
@ -288,17 +312,44 @@ bool Process::detach()
{
return false;
}
//TODO: find a safer way to suspend processes
/*if(DebugActiveProcessStop(my_pid))
{*/
attached = false;
g_pProcess = NULL;
g_ProcessHandle = 0;
return true;
/*}
return false;*/
resume();
attached = false;
g_pProcess = NULL;
g_ProcessHandle = 0;
return true;
}
bool Process::getThreadIDs(vector<uint32_t> & threads )
{
HANDLE AllThreads = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( AllThreads == INVALID_HANDLE_VALUE )
{
return false;
}
te32.dwSize = sizeof(THREADENTRY32 );
if( !Thread32First( AllThreads, &te32 ) )
{
CloseHandle( AllThreads );
return false;
}
do
{
if( te32.th32OwnerProcessID == my_pid )
{
threads.push_back(te32.th32ThreadID);
}
} while( Thread32Next(AllThreads, &te32 ) );
CloseHandle( AllThreads );
return true;
}
void Process::getMemRanges( vector<t_memrange> & ranges )
{
// code here is taken from hexsearch by Silas Dunmore.

@ -85,12 +85,21 @@ namespace DFHack
uint32_t my_pid;
string memFile;
bool attached;
bool suspended;
void freeResources();
void setMemFile(const string & memf);
public:
// Set up stuff so we can read memory
bool attach();
bool detach();
bool suspend();
bool resume();
bool isSuspended() {return suspended;};
bool isAttached() {return attached;};
bool getThreadIDs(vector<uint32_t> & threads );
void getMemRanges( vector<t_memrange> & ranges );
// is the process still there?
memory_info *getDescriptor();

@ -509,76 +509,6 @@ struct t_creature
uint32_t money;
int32_t squad_leader_id;
uint8_t sex;
/*
vector <t_skill> skills;
vector <t_trait> traits;
vector <t_labor> labors;
t_creature() {
x=y=z=0;
type=happiness=id=agility=strength=toughness=money=0;
squad_leader_id = -1;
sex=0;
}
t_creature(const t_creature & b)
{
x = b.x;
y = b.y;
z = b.z;
type = b.type;
flags1 = b.flags1;
flags2 = b.flags2;
first_name = b.first_name;
nick_name = b.nick_name;
//string last_name;
trans_name = b.trans_name;
generic_name = b.generic_name;
generic_squad_name = b.generic_squad_name;
trans_squad_name = b.trans_squad_name;
profession = b.profession;
custom_profession = b.custom_profession;
current_job = b.current_job;
happiness = b.happiness;
id = b.id;
agility = b.agility;
strength = b.strength;
toughness = b.toughness;
money = b.money;
squad_leader_id = b.squad_leader_id;
sex = b.sex;
skills = b.skills;
traits = b.traits;
labors = b.labors;
}
t_creature & operator=(const t_creature &b)
{
x = b.x;
y = b.y;
z = b.z;
type = b.type;
flags1 = b.flags1;
flags2 = b.flags2;
first_name = b.first_name;
nick_name = b.nick_name;
//string last_name;
trans_name = b.trans_name;
generic_name = b.generic_name;
generic_squad_name = b.generic_squad_name;
trans_squad_name = b.trans_squad_name;
profession = b.profession;
custom_profession = b.custom_profession;
current_job = b.current_job;
happiness = b.happiness;
id = b.id;
agility = b.agility;
strength = b.strength;
toughness = b.toughness;
money = b.money;
squad_leader_id = b.squad_leader_id;
sex = b.sex;
skills = b.skills;
traits = b.traits;
return *this;
}*/
};
// TODO: research this further? consult DF hacker wizards?

@ -42,7 +42,7 @@ TARGET_LINK_LIBRARIES(dfmaterialtest dfhack)
ADD_EXECUTABLE(dfposition position.cpp)
TARGET_LINK_LIBRARIES(dfposition dfhack)
# materialtest - just list the first material of each type
# incremental - incremental memory search tool, a foreshadowing of the future direction of dfhack
ADD_EXECUTABLE(dfincremental incrementalsearch.cpp)
TARGET_LINK_LIBRARIES(dfincremental dfhack)
@ -60,6 +60,5 @@ dfbuildingsdump
dfposition
dfincremental
RUNTIME DESTINATION bin
)
ENDIF(UNIX)

@ -30,7 +30,7 @@ int main (void)
cout << "Testing attach/detach" << endl;
time(&start);
bool all_ok = true;
for (int i = 0; i < 1000; i++)
for (int i = 0; i < 100; i++)
{
cout << "Try " << i << endl;
if(DF.Attach())
@ -61,6 +61,21 @@ int main (void)
time_diff = difftime(end, start);
cout << "attach tests done in " << time_diff << " seconds." << endl;
cout << "Testing suspend/resume" << endl;
DF.Attach();
time(&start);
for (int i = 0; i < 100; i++)
{
DF.Suspend();
cout << "Try " << i << endl;
DF.Resume();
}
time(&end);
DF.Detach();
time_diff = difftime(end, start);
cout << "suspend tests done in " << time_diff << " seconds." << endl;
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();