From 47b61a907d10f9fd9070bafa87923f312cf39d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 16 Nov 2009 16:47:22 +0000 Subject: [PATCH] suspend and continue calls, a method for reading creatures within a volume --- library/DFCommonInternal.h | 1 + library/DFHackAPI.cpp | 52 ++++++++- library/DFHackAPI.h | 12 ++- library/DFProcess.cpp | 213 +++++++++++++++++++++++-------------- library/DFProcessManager.h | 9 ++ library/DFTypes.h | 70 ------------ tools/CMakeLists.txt | 3 +- tools/attachtest.cpp | 17 ++- 8 files changed, 215 insertions(+), 162 deletions(-) diff --git a/library/DFCommonInternal.h b/library/DFCommonInternal.h index 0477e2465..da1654e09 100644 --- a/library/DFCommonInternal.h +++ b/library/DFCommonInternal.h @@ -69,6 +69,7 @@ using namespace std; #include #include #include + #include #endif #ifndef BUILD_DFHACK_LIB diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index c4ae4da67..f37705f3e 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -587,7 +587,7 @@ uint32_t API::InitReadBuildings(vector &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 & 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); diff --git a/library/DFHackAPI.h b/library/DFHackAPI.h index 5f76d5066..6055047cd 100644 --- a/library/DFHackAPI.h +++ b/library/DFHackAPI.h @@ -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 &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); diff --git a/library/DFProcess.cpp b/library/DFProcess.cpp index 0ff3361c3..479e5a04d 100644 --- a/library/DFProcess.cpp +++ b/library/DFProcess.cpp @@ -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 & 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 & ranges ) { @@ -128,36 +103,62 @@ void Process::getMemRanges( vector & 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 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 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 & 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 & ranges ) { // code here is taken from hexsearch by Silas Dunmore. diff --git a/library/DFProcessManager.h b/library/DFProcessManager.h index b8ff9fd7b..3ff527579 100644 --- a/library/DFProcessManager.h +++ b/library/DFProcessManager.h @@ -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 & threads ); void getMemRanges( vector & ranges ); // is the process still there? memory_info *getDescriptor(); diff --git a/library/DFTypes.h b/library/DFTypes.h index b11024a85..44f7aa9a3 100644 --- a/library/DFTypes.h +++ b/library/DFTypes.h @@ -509,76 +509,6 @@ struct t_creature uint32_t money; int32_t squad_leader_id; uint8_t sex; - /* - vector skills; - vector traits; - vector 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? diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 360ef25e7..e4e63d980 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/tools/attachtest.cpp b/tools/attachtest.cpp index 2797c28e2..e168b9c51 100644 --- a/tools/attachtest.cpp +++ b/tools/attachtest.cpp @@ -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();