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 <winbase.h>
#include <winnt.h> #include <winnt.h>
#include <psapi.h> #include <psapi.h>
#include <tlhelp32.h>
#endif #endif
#ifndef BUILD_DFHACK_LIB #ifndef BUILD_DFHACK_LIB

@ -587,7 +587,7 @@ uint32_t API::InitReadBuildings(vector <string> &v_buildingtypes)
// read one building // 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); assert(d->buildingsInited);
uint32_t temp; 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); assert(d->constructionsInited);
t_construction_df40d c_40d; 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); assert(d->vegetationInited);
uint32_t temp; uint32_t temp;
@ -955,7 +955,37 @@ void API::Private::getLaborsByAddress(const uint32_t &address, vector<t_labor> &
labors.push_back(tempLabor); 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); assert(d->creaturesInited);
uint32_t temp; 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); MreadByte(temp + d->creature_sex_offset, furball.sex);
return true; return true;
} }
//FIXME: this just isn't enough //FIXME: this just isn't enough
void API::InitReadNameTables() void API::InitReadNameTables()
{ {
@ -1101,6 +1132,19 @@ bool API::isAttached()
return d->dm != NULL; 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) void API::ReadRaw (const uint32_t &offset, const uint32_t &size, uint8_t *target)
{ {
Mread(offset, size, target); Mread(offset, size, target);

@ -62,6 +62,10 @@ namespace DFHack
bool Attach(); bool Attach();
bool Detach(); bool Detach();
bool isAttached(); 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... * 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. * 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 * Buildings, constructions, plants, all pretty straighforward. InitReadBuildings returns all the building types as a mapping between a numeric values and strings
*/ */
uint32_t InitReadConstructions(); uint32_t InitReadConstructions();
bool ReadConstruction(const uint32_t &index, t_construction & construction); bool ReadConstruction(const int32_t &index, t_construction & construction);
void FinishReadConstructions(); void FinishReadConstructions();
uint32_t InitReadBuildings(vector <string> &v_buildingtypes); 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(); void FinishReadBuildings();
uint32_t InitReadVegetation(); 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(); void FinishReadVegetation();
uint32_t InitReadCreatures(); uint32_t InitReadCreatures();
bool ReadCreature(const uint32_t &index, t_creature & furball); bool ReadCreature(const int32_t &index, t_creature & furball);
void FinishReadCreatures(); void FinishReadCreatures();
void ReadRaw (const uint32_t &offset, const uint32_t &size, uint8_t *target); 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_handle = ph;
my_pid = pid; my_pid = pid;
attached = false; attached = false;
suspended = false;
} }
@ -70,36 +71,10 @@ void Process::setMemFile(const string & memf)
*/ */
/* bool Process::getThreadIDs(vector<uint32_t> & threads )
//TODO: rewrite this. really. It's ugly as hell.
bool isStopped(pid_t pid)
{ {
char filename[256]; return false;
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;
} }
*/
void Process::getMemRanges( vector<t_memrange> & ranges ) 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; int status;
//cout << "Attach: start" << endl; if(!attached)
// check if another process is attached return false;
if(g_pProcess != NULL) if(suspended)
return true;
if (kill(my_handle, SIGSTOP) == -1)
{ {
// no, we got an error
perror("kill SIGSTOP error");
return false; return false;
} }
/* while(true)
if(!isStopped(my_handle))
{ {
// kill (SIGSTOP) // we wait on the pid
status = kill(my_handle,SIGSTOP); pid_t w = waitpid(my_handle, &status, 0);
//cout << "sent SIGSTOP" << endl; if (w == -1)
if(status == -1)
{ {
perror("kill(SIGSTOP)"); // child died
perror("DF exited during suspend call");
return false; return false;
} }
// wait for the process to stop // stopped -> let's continue
while (!isStopped(my_handle)) if (WIFSTOPPED(status))
{ {
usleep(5000); break;
//cout << "wait step" << endl;
} }
} }
*/ suspended = true;
//usleep(10000); }
//cout << "Attach: after conditional stop" << endl;
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? // can we attach?
if (ptrace(PTRACE_ATTACH , my_handle, NULL, NULL) == -1) if (ptrace(PTRACE_ATTACH , my_handle, NULL, NULL) == -1)
{ {
@ -166,8 +167,6 @@ bool Process::attach()
cerr << "attach failed on pid " << my_handle << endl; cerr << "attach failed on pid " << my_handle << endl;
return false; return false;
} }
//usleep(10000);
//cout << "Attach: after ptrace" << endl;
while(true) while(true)
{ {
// we wait on the pid // we wait on the pid
@ -176,8 +175,6 @@ bool Process::attach()
{ {
// child died // child died
perror("wait inside attach()"); perror("wait inside attach()");
//LOGC << "child died?";
//exit(-1);
return false; return false;
} }
// stopped -> let's continue // stopped -> let's continue
@ -186,7 +183,7 @@ bool Process::attach()
break; break;
} }
} }
// cout << "Managed to attach to pid " << my_handle << endl; suspended = true;
int proc_pid_mem = open(memFile.c_str(),O_RDONLY); int proc_pid_mem = open(memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1) if(proc_pid_mem == -1)
@ -203,7 +200,6 @@ bool Process::attach()
g_ProcessHandle = my_handle; g_ProcessHandle = my_handle;
g_ProcessMemFile = proc_pid_mem; g_ProcessMemFile = proc_pid_mem;
//cout << "Attach: after opening /proc/"<< my_handle <<"/mem" << endl;
return true; // we are attached return true; // we are attached
} }
} }
@ -211,9 +207,10 @@ bool Process::attach()
bool Process::detach() bool Process::detach()
{ {
if(!attached) return false; if(!attached) return false;
if(!suspended) suspend();
int result = 0; int result = 0;
//cout << "detach: start" << endl; // close /proc/PID/mem
result = close(g_ProcessMemFile);// close /proc/PID/mem result = close(g_ProcessMemFile);
if(result == -1) if(result == -1)
{ {
cerr << "couldn't close /proc/"<< my_handle <<"/mem" << endl; cerr << "couldn't close /proc/"<< my_handle <<"/mem" << endl;
@ -222,7 +219,7 @@ bool Process::detach()
} }
else else
{ {
//cout << "detach: after closing /proc/"<< my_handle <<"/mem" << endl; // detach
g_ProcessMemFile = -1; g_ProcessMemFile = -1;
result = ptrace(PTRACE_DETACH, my_handle, NULL, NULL); result = ptrace(PTRACE_DETACH, my_handle, NULL, NULL);
if(result == -1) if(result == -1)
@ -233,15 +230,9 @@ bool Process::detach()
} }
else else
{ {
// cout << "detach: after detaching from "<< my_handle << endl;
attached = false; attached = false;
g_pProcess = NULL; g_pProcess = NULL;
g_ProcessHandle = 0; g_ProcessHandle = 0;
// continue, wait for it to recover
// kill(my_handle,SIGCONT);
// while (isStopped(my_handle));
//usleep(10000);
// we finish
return true; return true;
} }
} }
@ -260,7 +251,45 @@ void Process::freeResources()
* WINDOWS PART * 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() bool Process::attach()
{ {
@ -268,17 +297,12 @@ bool Process::attach()
{ {
return false; return false;
} }
/* attached = true;
if(DebugActiveProcess(my_pid)) g_pProcess = this;
{ g_ProcessHandle = my_handle;
*/ suspend();
attached = true;
g_pProcess = this;
g_ProcessHandle = my_handle;
return true; return true;
/*}
return false;*/
} }
@ -288,17 +312,44 @@ bool Process::detach()
{ {
return false; return false;
} }
//TODO: find a safer way to suspend processes resume();
/*if(DebugActiveProcessStop(my_pid)) attached = false;
{*/ g_pProcess = NULL;
attached = false; g_ProcessHandle = 0;
g_pProcess = NULL; return true;
g_ProcessHandle = 0; }
return true;
/*} bool Process::getThreadIDs(vector<uint32_t> & threads )
return false;*/ {
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 ) void Process::getMemRanges( vector<t_memrange> & ranges )
{ {
// code here is taken from hexsearch by Silas Dunmore. // code here is taken from hexsearch by Silas Dunmore.

@ -85,12 +85,21 @@ namespace DFHack
uint32_t my_pid; uint32_t my_pid;
string memFile; string memFile;
bool attached; bool attached;
bool suspended;
void freeResources(); void freeResources();
void setMemFile(const string & memf); void setMemFile(const string & memf);
public: public:
// Set up stuff so we can read memory // Set up stuff so we can read memory
bool attach(); bool attach();
bool detach(); 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 ); void getMemRanges( vector<t_memrange> & ranges );
// is the process still there? // is the process still there?
memory_info *getDescriptor(); memory_info *getDescriptor();

@ -509,76 +509,6 @@ struct t_creature
uint32_t money; uint32_t money;
int32_t squad_leader_id; int32_t squad_leader_id;
uint8_t sex; 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? // TODO: research this further? consult DF hacker wizards?

@ -42,7 +42,7 @@ TARGET_LINK_LIBRARIES(dfmaterialtest dfhack)
ADD_EXECUTABLE(dfposition position.cpp) ADD_EXECUTABLE(dfposition position.cpp)
TARGET_LINK_LIBRARIES(dfposition dfhack) 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) ADD_EXECUTABLE(dfincremental incrementalsearch.cpp)
TARGET_LINK_LIBRARIES(dfincremental dfhack) TARGET_LINK_LIBRARIES(dfincremental dfhack)
@ -60,6 +60,5 @@ dfbuildingsdump
dfposition dfposition
dfincremental dfincremental
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )
ENDIF(UNIX) ENDIF(UNIX)

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