diff --git a/examples/expbench.cpp b/examples/expbench.cpp index c2b7621d9..272e46021 100644 --- a/examples/expbench.cpp +++ b/examples/expbench.cpp @@ -47,9 +47,7 @@ int main (int numargs, char** args) uint32_t x_max,y_max,z_max; uint32_t num_blocks = 0; uint64_t bytes_read = 0; - uint16_t tiletypes[16][16]; - DFHack::t_designation designations[16][16]; - DFHack::t_occupancy occupancies[16][16]; + DFHack::mapblock40d Block; DFHack::API DF("Memory.xml"); if(!DF.Attach()) @@ -76,11 +74,9 @@ int main (int numargs, char** args) { if(DF.isValidBlock(x,y,z)) { - DF.ReadTileTypes(x,y,z, (uint16_t *) tiletypes); - DF.ReadDesignations(x,y,z, (uint32_t *) designations); - DF.ReadOccupancy(x,y,z, (uint32_t *) occupancies); + DF.ReadBlock40d(x, y, z, &Block); num_blocks ++; - bytes_read += sizeof(tiletypes) + sizeof(designations) + sizeof(occupancies); + bytes_read += sizeof(DFHack::mapblock40d); } } } diff --git a/examples/veinlook.cpp b/examples/veinlook.cpp index e26aa2950..30c9bf212 100644 --- a/examples/veinlook.cpp +++ b/examples/veinlook.cpp @@ -144,8 +144,6 @@ void clrscr() GRASS2, GRASS_DEAD, GRASS_DRY, -#include -#include DRIFTWOOD, HFS, MAGMA, @@ -272,9 +270,12 @@ main(int argc, char *argv[]) int x_max,y_max,z_max; uint32_t x_max_a,y_max_a,z_max_a; + /* uint16_t tiletypes[16][16]; DFHack::t_designation designations[16][16]; uint8_t regionoffsets[16]; + */ + mapblock40d Block; map materials; materials.clear(); vector stonetypes; @@ -300,7 +301,6 @@ main(int argc, char *argv[]) error = "Can't find a map to look at."; pDF = 0; finish(0); -#include } DF.getSize(x_max_a,y_max_a,z_max_a); @@ -337,6 +337,7 @@ main(int argc, char *argv[]) int filenum = 0; bool dirtybit = false; uint32_t blockaddr = 0; + uint32_t blockaddr2 = 0; // walk the map! for (;;) { @@ -401,30 +402,35 @@ main(int argc, char *argv[]) if(DF.isValidBlock(cursorX+i,cursorY+j,cursorZ)) { // read data + DF.ReadBlock40d(cursorX+i,cursorY+j,cursorZ, &Block); + /* DF.ReadTileTypes(cursorX+i,cursorY+j,cursorZ, (uint16_t *) tiletypes); DF.ReadDesignations(cursorX+i,cursorY+j,cursorZ, (uint32_t *) designations); + */ + for(int x = 0; x < 16; x++) { for(int y = 0; y < 16; y++) { if(dig) { - if(tileTypeTable[tiletypes[x][y]].c == WALL && tileTypeTable[tiletypes[x][y]].m == VEIN - || tileTypeTable[tiletypes[x][y]].c == TREE_OK || tileTypeTable[tiletypes[x][y]].c == TREE_DEAD) + TileClass tc = tileTypeTable[Block.tiletypes[x][y]].c; + TileMaterial tm = tileTypeTable[Block.tiletypes[x][y]].m; + if( tc == WALL && tm == VEIN || tc == TREE_OK || tc == TREE_DEAD) { - designations[x][y].bits.dig = designation_default; + Block.designaton[x][y].bits.dig = designation_default; } } int color = COLOR_BLACK; - color = pickColor(tiletypes[x][y]); - if(designations[x][y].bits.hidden) + color = pickColor(Block.tiletypes[x][y]); + if(Block.designaton[x][y].bits.hidden) { - puttile(x+(i+1)*16,y+(j+1)*16,tiletypes[x][y], color); + puttile(x+(i+1)*16,y+(j+1)*16,Block.tiletypes[x][y], color); } else { attron(A_STANDOUT); - puttile(x+(i+1)*16,y+(j+1)*16,tiletypes[x][y], color); + puttile(x+(i+1)*16,y+(j+1)*16,Block.tiletypes[x][y], color); attroff(A_STANDOUT); } } @@ -433,13 +439,15 @@ main(int argc, char *argv[]) if(i == 0 && j == 0) { blockaddr = DF.getBlockPtr(cursorX+i,cursorY+j,cursorZ); + blockaddr2 = Block.origin; if(dump) { - hexdump(DF,blockaddr,0x1E00/*0x1DB8*/,filenum); + hexdump(DF,blockaddr,0x1E00,filenum); filenum++; } + if(dig) - DF.WriteDesignations(cursorX+i,cursorY+j,cursorZ, (uint32_t *) designations); + DF.WriteDesignations(cursorX+i,cursorY+j,cursorZ, (uint32_t *) Block.designaton); DF.ReadDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); if(digbit) { @@ -449,6 +457,7 @@ main(int argc, char *argv[]) veinVector.clear(); IceVeinVector.clear(); DF.ReadVeins(cursorX+i,cursorY+j,cursorZ,veinVector,IceVeinVector); + } } } @@ -512,7 +521,7 @@ main(int argc, char *argv[]) } } gotoxy (0,52); - cprintf("block address 0x%x",blockaddr); + cprintf("block address 0x%x 0x%x",blockaddr, blockaddr2); gotoxy (0,53); cprintf("dirty bit: %d",dirtybit); gotoxy (0,54); diff --git a/library/DFError.h b/library/DFError.h index 42fe7bcfa..f30c5e0c5 100644 --- a/library/DFError.h +++ b/library/DFError.h @@ -210,6 +210,17 @@ namespace DFHack return s.str().c_str(); } }; + + class DFHACK_EXPORT SHMServerDisappeared : public std::exception + { + public: + SHMServerDisappeared(){} + virtual ~SHMServerDisappeared() throw(){}; + virtual const char* what() const throw() + { + return "The server process has disappeared"; + } + }; } } diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index 9298ce702..6e8f76cf5 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -23,6 +23,9 @@ distribution. */ #include "DFCommonInternal.h" +#include "../shmserver/shms.h" +#include "../shmserver/mod-core.h" +#include "../shmserver/mod-maps.h" using namespace DFHack; /* @@ -114,6 +117,7 @@ public: ProcessEnumerator* pm; Process* p; + char * shm_start; memory_info* offset_descriptor; vector v_geology[eBiomeCount]; string xml; @@ -130,6 +134,8 @@ public: bool hotkeyInited; bool settlementsInited; bool nameTablesInited; + + uint32_t maps_module; uint32_t tree_offset; DfVector *p_cre; @@ -175,6 +181,8 @@ API::API (const string path_to_xml) d->notesInited = false; d->hotkeyInited = false; d->pm = NULL; + d->shm_start = 0; + d->maps_module = 0; } API::~API() @@ -182,6 +190,11 @@ API::~API() delete d; } +#define SHMCMD ((shm_cmd *)d->shm_start)->pingpong +#define SHMHDR ((shm_core_hdr *)d->shm_start) +#define SHMMAPSHDR ((Maps::shm_maps_hdr *)d->shm_start) +#define SHMDATA(type) ((type *)(d->shm_start + SHM_HEADER)) + /*-----------------------------------* * Init the mapblock pointer array * *-----------------------------------*/ @@ -207,19 +220,43 @@ bool API::InitMap() d->offset_descriptor->resolveClassnameToVPtr("block_square_event_frozen_liquid", d->vein_ice_vptr); d->vein_mineral_vptr = 0; d->offset_descriptor->resolveClassnameToVPtr("block_square_event_mineral",d->vein_mineral_vptr); - + + /* + * --> SHM initialization (if possible) <-- + */ + g_pProcess->getModuleIndex("Maps",2,d->maps_module); + + if(d->maps_module) + { + // supply the module with offsets so it can work with them + Maps::maps_offsets *off = SHMDATA(Maps::maps_offsets); + off->biome_stuffs = d->biome_stuffs; + off->designation_offset = d->designation_offset; + off->map_offset = map_offset; + off->occupancy_offset = d->occupancy_offset; + off->tile_type_offset = d->tile_type_offset; + off->vein_ice_vptr = d->vein_ice_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< + off->vein_mineral_vptr = d->vein_mineral_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< + off->veinvector = d->veinvector; + off->x_count_offset = x_count_offset; + off->y_count_offset = y_count_offset; + off->z_count_offset = z_count_offset; + full_barrier + const uint32_t cmd = Maps::MAP_INIT + d->maps_module << 16; + SHMCMD = cmd; + g_pProcess->waitWhile(cmd); + //cerr << "Map acceleration enabled!" << endl; + } + // get the map pointer uint32_t x_array_loc = g_pProcess->readDWord (map_offset); - //FIXME: very inadequate if (!x_array_loc) { throw Error::NoMapLoaded(); - // bad stuffz happend - //return false; } - uint32_t mx, my, mz; - + // get the size + uint32_t mx, my, mz; mx = d->x_block_count = g_pProcess->readDWord (x_count_offset); my = d->y_block_count = g_pProcess->readDWord (y_count_offset); mz = d->z_block_count = g_pProcess->readDWord (z_count_offset); @@ -279,6 +316,43 @@ uint32_t API::getBlockPtr (uint32_t x, uint32_t y, uint32_t z) return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; } +bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) +{ + if(d->shm_start && d->maps_module) // ACCELERATE! + { + SHMMAPSHDR->x = x; + SHMMAPSHDR->y = y; + SHMMAPSHDR->z = z; + volatile uint32_t cmd = Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); + full_barrier + SHMCMD = cmd; + full_barrier + if(!g_pProcess->waitWhile(cmd)) + { + return false; + } + memcpy(buffer,SHMDATA(mapblock40d),sizeof(mapblock40d)); + return true; + } + else // plain old block read + { + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->read (addr + d->tile_type_offset, sizeof (buffer->tiletypes), (uint8_t *) buffer->tiletypes); + g_pProcess->read (addr + d->occupancy_offset, sizeof (buffer->occupancy), (uint8_t *) buffer->occupancy); + g_pProcess->read (addr + d->designation_offset, sizeof (buffer->designaton), (uint8_t *) buffer->designaton); + g_pProcess->read (addr + d->biome_stuffs, sizeof (buffer->biome_indices), (uint8_t *) buffer->biome_indices); + buffer->origin = addr; + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + buffer->dirty_dword = g_pProcess->readDWord(addr_of_struct); + return true; + } + return false; + } +} + + // 256 * sizeof(uint16_t) bool API::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, uint16_t *buffer) { @@ -734,7 +808,7 @@ bool API::ReadGeology (vector < vector >& assign) } assign.clear(); assign.reserve (eBiomeCount); - // TODO: clean this up +// // TODO: clean this up for (int i = 0; i < eBiomeCount;i++) { assign.push_back (d->v_geology[i]); @@ -1373,6 +1447,7 @@ bool API::Attach() //cerr << "couldn't attach to process" << endl; //return false; // couldn't attach to process, no go } + d->shm_start = d->p->getSHMStart(); d->offset_descriptor = d->p->getDescriptor(); // process is attached, everything went just fine... hopefully return true; @@ -1391,6 +1466,7 @@ bool API::Detach() } d->pm = NULL; d->p = NULL; + d->shm_start = 0; d->offset_descriptor = NULL; return true; } diff --git a/library/DFHackAPI.h b/library/DFHackAPI.h index f25bb5d7f..2bacd4348 100644 --- a/library/DFHackAPI.h +++ b/library/DFHackAPI.h @@ -135,6 +135,8 @@ namespace DFHack */ uint32_t getBlockPtr (uint32_t blockx, uint32_t blocky, uint32_t blockz); + bool ReadBlock40d(uint32_t blockx, uint32_t blocky, uint32_t blockz, mapblock40d * buffer); + bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint16_t *buffer); // 256 * sizeof(uint16_t) bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, uint16_t *buffer); // 256 * sizeof(uint16_t) diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 9bcda4ff2..1ae294355 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -28,6 +28,7 @@ distribution. #include #include #include "../shmserver/shms.h" +#include "../shmserver/mod-core.h" #include #include #include @@ -50,6 +51,7 @@ class SHMProcess::Private attached = false; suspended = false; identified = false; + useYield = false; }; ~Private(){}; memory_info * my_descriptor; @@ -57,32 +59,53 @@ class SHMProcess::Private pid_t my_pid; char *my_shm; int my_shmid; + Process* q; bool attached; bool suspended; bool identified; + bool useYield; bool validate(char* exe_file, uint32_t pid, std::vector< memory_info* >& known_versions); - bool waitWhile (DF_PINGPONG state); - bool DF_TestBridgeVersion(bool & ret); - bool DF_GetPID(pid_t & ret); + + bool Aux_Core_Attach(bool & versionOK, pid_t & PID); + bool waitWhile (uint32_t state); }; -bool SHMProcess::Private::waitWhile (DF_PINGPONG state) +// some helpful macros to keep the code bloat in check +#define SHMCMD ((shm_cmd *)my_shm)->pingpong +#define D_SHMCMD ((shm_cmd *)d->my_shm)->pingpong + +#define SHMHDR ((shm_core_hdr *)my_shm) +#define D_SHMHDR ((shm_core_hdr *)d->my_shm) + +#define SHMDATA(type) ((type *)(my_shm + SHM_HEADER)) +#define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER)) + +/* +Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) +we end up with this silly thing +*/ +bool SHMProcess::Private::waitWhile (uint32_t state) { uint32_t cnt = 0; struct shmid_ds descriptor; - while (((shm_cmd *)my_shm)->pingpong == state) + while (SHMCMD == state) { - if(cnt == 10000) + if(cnt == 10000)// check if the other process is still there { shmctl(my_shmid, IPC_STAT, &descriptor); - if(descriptor.shm_nattch == 1)// DF crashed? + if(descriptor.shm_nattch == 1)// DF crashed or exited - no way to tell? { - gcc_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_RUNNING; + //detach the shared memory + shmdt(my_shm); attached = suspended = false; + + // we aren't the current process anymore + g_pProcess = NULL; + + throw Error::SHMServerDisappeared(); return false; } else @@ -90,12 +113,15 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) cnt = 0; } } - SCHED_YIELD + if(useYield) + { + SCHED_YIELD + } cnt++; } - if(((shm_cmd *)my_shm)->pingpong == DFPP_SV_ERROR) + if(SHMCMD == CORE_ERROR) { - ((shm_cmd *)my_shm)->pingpong = DFPP_RUNNING; + SHMCMD = CORE_RUNNING; attached = suspended = false; cerr << "shm server error!" << endl; assert (false); @@ -104,31 +130,43 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) return true; } -bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) +/* +Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) +we end up with this silly thing +*/ +bool SHMProcess::waitWhile (uint32_t state) { - ((shm_cmd *)my_shm)->pingpong = DFPP_VERSION; - gcc_barrier - if(!waitWhile(DFPP_VERSION)) - return false; - gcc_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; - ret =( ((shm_retval *)my_shm)->value == PINGPONG_VERSION ); - return true; + return d->waitWhile(state); } -bool SHMProcess::Private::DF_GetPID(pid_t & ret) +uint32_t OS_getAffinity() { - ((shm_cmd *)my_shm)->pingpong = DFPP_PID; + cpu_set_t mask; + sched_getaffinity(0,sizeof(cpu_set_t),&mask); + // FIXME: truncation + uint32_t affinity = *(uint32_t *) &mask; + return affinity; +} + + +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) +{ + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); gcc_barrier - if(!waitWhile(DFPP_PID)) + SHMCMD = CORE_ATTACH; + if(!waitWhile(CORE_ATTACH)) return false; gcc_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; - ret = ((shm_retval *)my_shm)->value; + versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); + PID = SHMDATA(coreattach)->sv_PID; + useYield = SHMDATA(coreattach)->sv_useYield; + #ifdef DEBUG + if(useYield) cerr << "Using Yield!" << endl; + #endif return true; } -SHMProcess::SHMProcess(vector & known_versions) +SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) : d(new Private()) { char exe_link_name [256]; @@ -138,7 +176,7 @@ SHMProcess::SHMProcess(vector & known_versions) /* * Locate the segment. */ - if ((d->my_shmid = shmget(SHM_KEY, SHM_SIZE, 0666)) < 0) + if ((d->my_shmid = shmget(SHM_KEY + PID, SHM_SIZE, 0666)) < 0) { return; } @@ -164,43 +202,43 @@ SHMProcess::SHMProcess(vector & known_versions) } /* - * Test bridge version, will also detect when we connect to something that doesn't respond + * Test bridge version, get PID, sync Yield */ bool bridgeOK; - if(!d->DF_TestBridgeVersion(bridgeOK)) + if(!d->Aux_Core_Attach(bridgeOK,d->my_pid)) { fprintf(stderr,"DF terminated during reading\n"); + shmdt(d->my_shm); return; } if(!bridgeOK) { fprintf(stderr,"SHM bridge version mismatch\n"); + shmdt(d->my_shm); return; } - /* - * get the PID from DF - */ - if(d->DF_GetPID(d->my_pid)) + + // find the binary + sprintf(exe_link_name,"/proc/%d/exe", d->my_pid); + target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); + if (target_result == -1) { - // find its binary - sprintf(exe_link_name,"/proc/%d/exe", d->my_pid); - target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); - if (target_result == -1) - { - perror("readlink"); - return; - } - // make sure we have a null terminated string... - // see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html - target_name[target_result] = 0; - - // try to identify the DF version - d->validate(target_name, d->my_pid, known_versions); - d->my_window = new DFWindow(this); + perror("readlink"); + shmdt(d->my_shm); + return; } + + // make sure we have a null terminated string... + // see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html + target_name[target_result] = 0; + + // try to identify the DF version + d->validate(target_name, d->my_pid, known_versions); + d->my_window = new DFWindow(this); + gcc_barrier // at this point, DF is stopped and waiting for commands. make it run again - ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; + D_SHMCMD = CORE_RUNNING; shmdt(d->my_shm); // detach so we don't attach twice when attach() is called } @@ -319,8 +357,8 @@ bool SHMProcess::suspend() { return true; } - ((shm_cmd *)d->my_shm)->pingpong = DFPP_SUSPEND; - if(!d->waitWhile(DFPP_SUSPEND)) + D_SHMCMD = CORE_SUSPEND; + if(!waitWhile(CORE_SUSPEND)) { return false; } @@ -338,14 +376,14 @@ bool SHMProcess::asyncSuspend() { return true; } - if(((shm_cmd *)d->my_shm)->pingpong == DFPP_SUSPENDED) + if(D_SHMCMD == CORE_SUSPENDED) { d->suspended = true; return true; } else { - ((shm_cmd *)d->my_shm)->pingpong = DFPP_SUSPEND; + D_SHMCMD = CORE_SUSPEND; return false; } } @@ -361,7 +399,7 @@ bool SHMProcess::resume() return false; if(!d->suspended) return true; - ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; + D_SHMCMD = CORE_RUNNING; d->suspended = false; return true; } @@ -425,11 +463,11 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff // normal read under 1MB if(size <= SHM_BODY) { - ((shm_read *)d->my_shm)->address = src_address; - ((shm_read *)d->my_shm)->length = size; + D_SHMHDR->address = src_address; + D_SHMHDR->length = size; gcc_barrier - ((shm_read *)d->my_shm)->pingpong = DFPP_READ; - d->waitWhile(DFPP_READ); + D_SHMCMD = CORE_DFPP_READ; + waitWhile(CORE_DFPP_READ); memcpy (target_buffer, d->my_shm + SHM_HEADER,size); } // a big read, we pull data over the shm in iterations @@ -440,11 +478,11 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff while (size) { // read to_read bytes from src_cursor - ((shm_read *)d->my_shm)->address = src_address; - ((shm_read *)d->my_shm)->length = to_read; + D_SHMHDR->address = src_address; + D_SHMHDR->length = to_read; gcc_barrier - ((shm_read *)d->my_shm)->pingpong = DFPP_READ; - d->waitWhile(DFPP_READ); + D_SHMCMD = CORE_DFPP_READ; + waitWhile(CORE_DFPP_READ); memcpy (target_buffer, d->my_shm + SHM_HEADER,size); // decrease size by bytes read size -= to_read; @@ -459,55 +497,55 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff uint8_t SHMProcess::readByte (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; - d->waitWhile(DFPP_READ_BYTE); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_BYTE; + waitWhile(CORE_READ_BYTE); + return D_SHMHDR->value; } void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; - d->waitWhile(DFPP_READ_BYTE); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_BYTE; + waitWhile(CORE_READ_BYTE); + val = D_SHMHDR->value; } uint16_t SHMProcess::readWord (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; - d->waitWhile(DFPP_READ_WORD); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_WORD; + waitWhile(CORE_READ_WORD); + return D_SHMHDR->value; } void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; - d->waitWhile(DFPP_READ_WORD); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_WORD; + waitWhile(CORE_READ_WORD); + val = D_SHMHDR->value; } uint32_t SHMProcess::readDWord (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; - d->waitWhile(DFPP_READ_DWORD); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_DWORD; + waitWhile(CORE_READ_DWORD); + return D_SHMHDR->value; } void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; gcc_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; - d->waitWhile(DFPP_READ_DWORD); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_DWORD; + waitWhile(CORE_READ_DWORD); + val = D_SHMHDR->value; } /* @@ -516,30 +554,30 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; gcc_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_DWORD; - d->waitWhile(DFPP_WRITE_DWORD); + D_SHMCMD = CORE_WRITE_DWORD; + waitWhile(CORE_WRITE_DWORD); } // using these is expensive. void SHMProcess::writeWord (uint32_t offset, uint16_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; gcc_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_WORD; - d->waitWhile(DFPP_WRITE_WORD); + D_SHMCMD = CORE_WRITE_WORD; + waitWhile(CORE_WRITE_WORD); } void SHMProcess::writeByte (uint32_t offset, uint8_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; gcc_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_BYTE; - d->waitWhile(DFPP_WRITE_BYTE); + D_SHMCMD = CORE_WRITE_BYTE; + waitWhile(CORE_WRITE_BYTE); } void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) @@ -547,12 +585,12 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // normal write under 1MB if(size <= SHM_BODY) { - ((shm_write *)d->my_shm)->address = dst_address; - ((shm_write *)d->my_shm)->length = size; + D_SHMHDR->address = dst_address; + D_SHMHDR->length = size; memcpy(d->my_shm+SHM_HEADER,source_buffer, size); gcc_barrier - ((shm_write *)d->my_shm)->pingpong = DFPP_WRITE; - d->waitWhile(DFPP_WRITE); + D_SHMCMD = CORE_WRITE; + waitWhile(CORE_WRITE); } // a big write, we push this over the shm in iterations else @@ -562,12 +600,12 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf while (size) { // write to_write bytes to dst_cursor - ((shm_write *)d->my_shm)->address = dst_address; - ((shm_write *)d->my_shm)->length = to_write; + D_SHMHDR->address = dst_address; + D_SHMHDR->length = to_write; memcpy(d->my_shm+SHM_HEADER,source_buffer, to_write); gcc_barrier - ((shm_write *)d->my_shm)->pingpong = DFPP_WRITE; - d->waitWhile(DFPP_WRITE); + D_SHMCMD = CORE_WRITE; + waitWhile(CORE_WRITE); // decrease size by bytes written size -= to_write; // move the cursors @@ -615,21 +653,21 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) const std::string SHMProcess::readSTLString(uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_STL_STRING; - d->waitWhile(DFPP_READ_STL_STRING); + D_SHMCMD = CORE_READ_STL_STRING; + waitWhile(CORE_READ_STL_STRING); //int length = ((shm_retval *)d->my_shm)->value; return(string( (char *)d->my_shm+SHM_HEADER)); } size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_STL_STRING; - d->waitWhile(DFPP_READ_STL_STRING); - size_t length = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_STL_STRING; + waitWhile(CORE_READ_STL_STRING); + size_t length = D_SHMHDR->value; size_t fit = min(bufcapacity - 1, length); strncpy(buffer,(char *)d->my_shm+SHM_HEADER,fit); buffer[fit] = 0; @@ -638,11 +676,11 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { - ((shm_write_small *)d->my_shm)->address = address; + D_SHMHDR->address = address; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator full_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_STL_STRING; - d->waitWhile(DFPP_WRITE_STL_STRING); + D_SHMCMD = CORE_WRITE_STL_STRING; + waitWhile(CORE_WRITE_STL_STRING); } string SHMProcess::readClassName (uint32_t vptr) @@ -653,4 +691,37 @@ string SHMProcess::readClassName (uint32_t vptr) size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers size_t end = raw.length(); return raw.substr(start,end-start - 2); // trim the 'st' from the end +} + +// FIXME: having this around could lead to bad things in the hands of unsuspecting fools +// *!!DON'T BE AN UNSUSPECTING FOOL!!* +// the whole SHM thing works only because copying DWORDS is an atomic operation on i386 and x86_64 archs +// get module index by name and version. bool 1 = error +bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +{ + modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); + payload->version = version; + + strncpy(payload->name,name,255); + payload->name[255] = 0; + + full_barrier + + D_SHMCMD = CORE_ACQUIRE_MODULE; + if(!waitWhile(CORE_ACQUIRE_MODULE)) + { + return false; // FIXME: throw a fatal exception instead + } + if(D_SHMHDR->error) + { + return false; + } + //fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value); + OUTPUT = D_SHMHDR->value; + return true; +} + +char * SHMProcess::getSHMStart (void) +{ + return d->my_shm; } \ No newline at end of file diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index fcdee360b..8886c0e7c 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -23,6 +23,7 @@ distribution. */ #include "DFCommonInternal.h" #include "../shmserver/shms.h" +#include "../shmserver/mod-core.h" using namespace DFHack; // a full memory barrier! better be safe than sorry. @@ -38,6 +39,7 @@ class SHMProcess::Private attached = false; suspended = false; identified = false; + useYield = 0; DFSVMutex = 0; DFCLMutex = 0; }; @@ -52,13 +54,23 @@ class SHMProcess::Private bool attached; bool suspended; bool identified; + bool useYield; - bool waitWhile (DF_PINGPONG state); + bool waitWhile (uint32_t state); bool isValidSV(); - bool DF_TestBridgeVersion(bool & ret); - bool DF_GetPID(uint32_t & ret); + bool Aux_Core_Attach(bool & versionOK, uint32_t & PID); }; +// some helpful macros to keep the code bloat in check +#define SHMCMD ((shm_cmd *)my_shm)->pingpong +#define D_SHMCMD ((shm_cmd *)d->my_shm)->pingpong + +#define SHMHDR ((shm_core_hdr *)my_shm) +#define D_SHMHDR ((shm_core_hdr *)d->my_shm) + +#define SHMDATA(type) ((type *)(my_shm + SHM_HEADER)) +#define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER)) + // is the other side still there? bool SHMProcess::Private::isValidSV() { @@ -87,20 +99,29 @@ bool SHMProcess::Private::isValidSV() } } -bool SHMProcess::Private::waitWhile (DF_PINGPONG state) +bool SHMProcess::waitWhile (uint32_t state) +{ + return d->waitWhile(state); +} + +bool SHMProcess::Private::waitWhile (uint32_t state) { uint32_t cnt = 0; - SCHED_YIELD // yield the CPU, valid only on single-core CPUs - while (((shm_cmd *)my_shm)->pingpong == state) + while (SHMCMD == state) { + // yield the CPU, only on single-core CPUs + if(useYield) + { + SCHED_YIELD + } if(cnt == 10000) { if(!isValidSV())// DF not there anymore? { - full_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_RUNNING; attached = suspended = false; ReleaseMutex(DFCLMutex); + UnmapViewOfFile(my_shm); + throw Error::SHMServerDisappeared(); return false; } else @@ -110,79 +131,84 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) } cnt++; } - if(((shm_cmd *)my_shm)->pingpong == DFPP_SV_ERROR) + if(SHMCMD == CORE_ERROR) { - ((shm_cmd *)my_shm)->pingpong = DFPP_RUNNING; + SHMCMD = CORE_RUNNING; attached = suspended = false; cerr << "shm server error!" << endl; - assert (false); return false; } return true; } -bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) +uint32_t OS_getAffinity() { - ((shm_cmd *)my_shm)->pingpong = DFPP_VERSION; - full_barrier - if(!waitWhile(DFPP_VERSION)) - return false; - full_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; - ret =( ((shm_retval *)my_shm)->value == PINGPONG_VERSION ); - return true; + HANDLE hProcess = GetCurrentProcess(); + DWORD dwProcessAffinityMask, dwSystemAffinityMask; + GetProcessAffinityMask( hProcess, &dwProcessAffinityMask, &dwSystemAffinityMask ); + return dwProcessAffinityMask; } -bool SHMProcess::Private::DF_GetPID(uint32_t & ret) +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) { - ((shm_cmd *)my_shm)->pingpong = DFPP_PID; + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); full_barrier - if(!waitWhile(DFPP_PID)) + SHMCMD = CORE_ATTACH; + if(!waitWhile(CORE_ATTACH)) return false; full_barrier - ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; - ret = ((shm_retval *)my_shm)->value; + versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); + PID = SHMDATA(coreattach)->sv_PID; + useYield = SHMDATA(coreattach)->sv_useYield; + #ifdef DEBUG + if(useYield) cerr << "Using Yield!" << endl; + #endif return true; } -SHMProcess::SHMProcess(vector & known_versions) +SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) : d(new Private()) { + char svmutexname [256]; + sprintf(svmutexname,"DFSVMutex-%d",PID); + char clmutexname [256]; + sprintf(clmutexname,"DFCLMutex-%d",PID); + // get server and client mutex - d->DFSVMutex = OpenMutex(SYNCHRONIZE,false, "DFSVMutex"); + d->DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname); if(d->DFSVMutex == 0) { return; } - d->DFCLMutex = OpenMutex(SYNCHRONIZE,false, "DFCLMutex"); + d->DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname); if(d->DFCLMutex == 0) { return; } + d->my_pid = PID; + + // attach the SHM if(!attach()) { return; } - // All seems to be OK so far. Attached and connected to something that looks like DF - - // Test bridge version, will also detect when we connect to something that doesn't respond + // Test bridge version, get PID, sync Yield bool bridgeOK; - if(!d->DF_TestBridgeVersion(bridgeOK)) + bool error = 0; + if(!d->Aux_Core_Attach(bridgeOK,d->my_pid)) { fprintf(stderr,"DF terminated during reading\n"); - UnmapViewOfFile(d->my_shm); - ReleaseMutex(d->DFCLMutex); - CloseHandle(d->DFSVMutex); - d->DFSVMutex = 0; - CloseHandle(d->DFCLMutex); - d->DFCLMutex = 0; - return; + error = 1; } - if(!bridgeOK) + else if(!bridgeOK) { fprintf(stderr,"SHM bridge version mismatch\n"); - ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; + error = 1; + } + if(error) + { + D_SHMCMD = CORE_RUNNING; UnmapViewOfFile(d->my_shm); ReleaseMutex(d->DFCLMutex); CloseHandle(d->DFSVMutex); @@ -191,83 +217,78 @@ SHMProcess::SHMProcess(vector & known_versions) d->DFCLMutex = 0; return; } - /* - * get the PID from DF - */ - if(d->DF_GetPID(d->my_pid)) - { - // try to identify the DF version - do // glorified goto - { - IMAGE_NT_HEADERS32 pe_header; - IMAGE_SECTION_HEADER sections[16]; - HMODULE hmod = NULL; - DWORD junk; - HANDLE hProcess; - bool found = false; - d->identified = false; - // open process, we only need the process open - hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->my_pid ); - if (NULL == hProcess) - break; - - // try getting the first module of the process - if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) - { - CloseHandle(hProcess); - cout << "EnumProcessModules fail'd" << endl; - break; - } - // got base ;) - uint32_t base = (uint32_t)hmod; - - // read from this process - uint32_t pe_offset = readDWord(base+0x3C); - read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); - read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); - - // iterate over the list of memory locations - vector::iterator it; - for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) - { - uint32_t pe_timestamp; - try - { - pe_timestamp = (*it)->getHexValue("pe_timestamp"); - } - catch(Error::MissingMemoryDefinition& e) - { - continue; - } - if (pe_timestamp == pe_header.FileHeader.TimeDateStamp) - { - memory_info *m = new memory_info(**it); - m->RebaseAll(base); - d->my_descriptor = m; - d->identified = true; - cerr << "identified " << m->getVersion() << endl; - break; - } - } - CloseHandle(hProcess); - } while (0); // glorified goto end + + // try to identify the DF version + do // glorified goto + { + IMAGE_NT_HEADERS32 pe_header; + IMAGE_SECTION_HEADER sections[16]; + HMODULE hmod = NULL; + DWORD junk; + HANDLE hProcess; + bool found = false; + d->identified = false; + // open process, we only need the process open + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->my_pid ); + if (NULL == hProcess) + break; - if(d->identified) + // try getting the first module of the process + if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) { - d->my_window = new DFWindow(this); + CloseHandle(hProcess); + // cout << "EnumProcessModules fail'd" << endl; + break; } - else + // got base ;) + uint32_t base = (uint32_t)hmod; + + // read from this process + uint32_t pe_offset = readDWord(base+0x3C); + read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); + read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); + + // iterate over the list of memory locations + vector::iterator it; + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) { - ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; - UnmapViewOfFile(d->my_shm); - d->my_shm = 0; - ReleaseMutex(d->DFCLMutex); - CloseHandle(d->DFSVMutex); - d->DFSVMutex = 0; - CloseHandle(d->DFCLMutex); - d->DFCLMutex = 0; - return; + uint32_t pe_timestamp; + try + { + pe_timestamp = (*it)->getHexValue("pe_timestamp"); + } + catch(Error::MissingMemoryDefinition& e) + { + continue; + } + if (pe_timestamp == pe_header.FileHeader.TimeDateStamp) + { + memory_info *m = new memory_info(**it); + m->RebaseAll(base); + d->my_descriptor = m; + d->identified = true; + cerr << "identified " << m->getVersion() << endl; + break; + } } + CloseHandle(hProcess); + } while (0); // glorified goto end + + if(d->identified) + { + d->my_window = new DFWindow(this); + } + else + { + D_SHMCMD = CORE_RUNNING; + UnmapViewOfFile(d->my_shm); + d->my_shm = 0; + ReleaseMutex(d->DFCLMutex); + CloseHandle(d->DFSVMutex); + d->DFSVMutex = 0; + CloseHandle(d->DFCLMutex); + d->DFCLMutex = 0; + return; } full_barrier // at this point, DF is attached and suspended, make it run @@ -375,8 +396,8 @@ bool SHMProcess::suspend() cerr << "couldn't suspend, already suspended" << endl; return true; } - ((shm_cmd *)d->my_shm)->pingpong = DFPP_SUSPEND; - if(!d->waitWhile(DFPP_SUSPEND)) + D_SHMCMD = CORE_SUSPEND; + if(!d->waitWhile(CORE_SUSPEND)) { cerr << "couldn't suspend, DF not responding to commands" << endl; return false; @@ -395,14 +416,14 @@ bool SHMProcess::asyncSuspend() { return true; } - if(((shm_cmd *)d->my_shm)->pingpong == DFPP_SUSPENDED) + if(D_SHMCMD == CORE_SUSPENDED) { d->suspended = true; return true; } else { - ((shm_cmd *)d->my_shm)->pingpong = DFPP_SUSPEND; + D_SHMCMD = CORE_SUSPEND; return false; } } @@ -424,7 +445,7 @@ bool SHMProcess::resume() cerr << "couldn't resume because of not being suspended" << endl; return true; } - ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; + D_SHMCMD = CORE_RUNNING; d->suspended = false; return true; } @@ -454,8 +475,11 @@ bool SHMProcess::attach() return false; // we couldn't lock it } + char shmname [256]; + sprintf(shmname,"DFShm-%d",d->my_pid); + // now try getting and attaching the shared memory - HANDLE shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,"DFShm"); + HANDLE shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,shmname); if(!shmHandle) { ReleaseMutex(d->DFCLMutex); @@ -499,11 +523,11 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff // normal read under 1MB if(size <= SHM_BODY) { - ((shm_read *)d->my_shm)->address = src_address; - ((shm_read *)d->my_shm)->length = size; + D_SHMHDR->address = src_address; + D_SHMHDR->length = size; full_barrier - ((shm_read *)d->my_shm)->pingpong = DFPP_READ; - d->waitWhile(DFPP_READ); + D_SHMCMD = CORE_DFPP_READ; + d->waitWhile(CORE_DFPP_READ); memcpy (target_buffer, d->my_shm + SHM_HEADER,size); } // a big read, we pull data over the shm in iterations @@ -514,11 +538,11 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff while (size) { // read to_read bytes from src_cursor - ((shm_read *)d->my_shm)->address = src_address; - ((shm_read *)d->my_shm)->length = to_read; + D_SHMHDR->address = src_address; + D_SHMHDR->length = to_read; full_barrier - ((shm_read *)d->my_shm)->pingpong = DFPP_READ; - d->waitWhile(DFPP_READ); + D_SHMCMD = CORE_DFPP_READ; + d->waitWhile(CORE_DFPP_READ); memcpy (target_buffer, d->my_shm + SHM_HEADER,size); // decrease size by bytes read size -= to_read; @@ -533,55 +557,55 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff uint8_t SHMProcess::readByte (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; - d->waitWhile(DFPP_READ_BYTE); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_BYTE; + d->waitWhile(CORE_READ_BYTE); + return D_SHMHDR->value; } void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; - d->waitWhile(DFPP_READ_BYTE); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_BYTE; + d->waitWhile(CORE_READ_BYTE); + val = D_SHMHDR->value; } uint16_t SHMProcess::readWord (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; - d->waitWhile(DFPP_READ_WORD); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_WORD; + d->waitWhile(CORE_READ_WORD); + return D_SHMHDR->value; } void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; - d->waitWhile(DFPP_READ_WORD); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_WORD; + d->waitWhile(CORE_READ_WORD); + val = D_SHMHDR->value; } uint32_t SHMProcess::readDWord (const uint32_t offset) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; - d->waitWhile(DFPP_READ_DWORD); - return ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_DWORD; + d->waitWhile(CORE_READ_DWORD); + return D_SHMHDR->value; } void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; - d->waitWhile(DFPP_READ_DWORD); - val = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_DWORD; + d->waitWhile(CORE_READ_DWORD); + val = D_SHMHDR->value; } /* @@ -590,30 +614,30 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; full_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_DWORD; - d->waitWhile(DFPP_WRITE_DWORD); + D_SHMCMD = CORE_WRITE_DWORD; + d->waitWhile(CORE_WRITE_DWORD); } // using these is expensive. void SHMProcess::writeWord (uint32_t offset, uint16_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; full_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_WORD; - d->waitWhile(DFPP_WRITE_WORD); + D_SHMCMD = CORE_WRITE_WORD; + d->waitWhile(CORE_WRITE_WORD); } void SHMProcess::writeByte (uint32_t offset, uint8_t data) { - ((shm_write_small *)d->my_shm)->address = offset; - ((shm_write_small *)d->my_shm)->value = data; + D_SHMHDR->address = offset; + D_SHMHDR->value = data; full_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_BYTE; - d->waitWhile(DFPP_WRITE_BYTE); + D_SHMCMD = CORE_WRITE_BYTE; + d->waitWhile(CORE_WRITE_BYTE); } void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) @@ -621,12 +645,12 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // normal write under 1MB if(size <= SHM_BODY) { - ((shm_write *)d->my_shm)->address = dst_address; - ((shm_write *)d->my_shm)->length = size; + D_SHMHDR->address = dst_address; + D_SHMHDR->length = size; memcpy(d->my_shm+SHM_HEADER,source_buffer, size); full_barrier - ((shm_write *)d->my_shm)->pingpong = DFPP_WRITE; - d->waitWhile(DFPP_WRITE); + D_SHMCMD = CORE_WRITE; + d->waitWhile(CORE_WRITE); } // a big write, we push this over the shm in iterations else @@ -636,12 +660,12 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf while (size) { // write to_write bytes to dst_cursor - ((shm_write *)d->my_shm)->address = dst_address; - ((shm_write *)d->my_shm)->length = to_write; + D_SHMHDR->address = dst_address; + D_SHMHDR->length = to_write; memcpy(d->my_shm+SHM_HEADER,source_buffer, to_write); full_barrier - ((shm_write *)d->my_shm)->pingpong = DFPP_WRITE; - d->waitWhile(DFPP_WRITE); + D_SHMCMD = CORE_WRITE; + d->waitWhile(CORE_WRITE); // decrease size by bytes written size -= to_write; // move the cursors @@ -692,11 +716,11 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) const std::string SHMProcess::readSTLString(uint32_t offset) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_STL_STRING; - d->waitWhile(DFPP_READ_STL_STRING); - int length = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_STL_STRING; + d->waitWhile(CORE_READ_STL_STRING); + int length = D_SHMHDR->value; // char temp_c[256]; // strncpy(temp_c, d->my_shm+SHM_HEADER,length+1); // length + 1 for the null terminator return(string(d->my_shm+SHM_HEADER)); @@ -705,11 +729,11 @@ const std::string SHMProcess::readSTLString(uint32_t offset) size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 - ((shm_read_small *)d->my_shm)->address = offset; + D_SHMHDR->address = offset; full_barrier - ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_STL_STRING; - d->waitWhile(DFPP_READ_STL_STRING); - size_t length = ((shm_retval *)d->my_shm)->value; + D_SHMCMD = CORE_READ_STL_STRING; + d->waitWhile(CORE_READ_STL_STRING); + size_t length = D_SHMHDR->value; size_t real = min(length, bufcapacity - 1); strncpy(buffer, d->my_shm+SHM_HEADER,real); // length + 1 for the null terminator buffer[real] = 0; @@ -718,11 +742,11 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { - ((shm_write_small *)d->my_shm)->address = address/*-4*/; + D_SHMHDR->address = address/*-4*/; strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator full_barrier - ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_STL_STRING; - d->waitWhile(DFPP_WRITE_STL_STRING); + D_SHMCMD = CORE_WRITE_STL_STRING; + d->waitWhile(CORE_WRITE_STL_STRING); } string SHMProcess::readClassName (uint32_t vptr) @@ -732,4 +756,23 @@ string SHMProcess::readClassName (uint32_t vptr) string raw = readCString(typeinfo + 0xC); // skips the .?AV raw.resize(raw.length() - 4);// trim st@@ from end return raw; +} + +// get module index by name and version. bool 1 = error +bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +{ + modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); + payload->version = version; + strcpy(payload->name,name); + full_barrier + D_SHMCMD = CORE_ACQUIRE_MODULE; + d->waitWhile(CORE_ACQUIRE_MODULE); + if(D_SHMHDR->error) return false; + OUTPUT = D_SHMHDR->value; + return true; +} + +char * SHMProcess::getSHMStart (void) +{ + return d->my_shm; } \ No newline at end of file diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index 1479db1e1..271bf191c 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -69,7 +69,7 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) { CloseHandle(hProcess); - cout << "EnumProcessModules fail'd" << endl; + // cout << "EnumProcessModules fail'd" << endl; return; //if enumprocessModules fails, give up } diff --git a/library/DFProcess.h b/library/DFProcess.h index 7eac5fe18..878e68d37 100644 --- a/library/DFProcess.h +++ b/library/DFProcess.h @@ -119,6 +119,12 @@ namespace DFHack virtual DFWindow * getWindow() = 0; // get the DF Process ID virtual int getPID() = 0; + // get module index by name and version. bool 1 = error + virtual bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) = 0; + // get the SHM start if available + virtual char * getSHMStart (void) = 0; + // wait for a SHM state. returns 0 without the SHM + virtual bool waitWhile (uint32_t state) = 0; }; class DFHACK_EXPORT NormalProcess : virtual public Process @@ -171,6 +177,12 @@ namespace DFHack memory_info *getDescriptor(); DFWindow * getWindow(); int getPID(); + // get module index by name and version. bool 1 = error + bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; + // get the SHM start if available + char * getSHMStart (void){return 0;}; + // wait for a SHM state. returns 0 without the SHM + bool waitWhile (uint32_t state){return false;}; }; class DFHACK_EXPORT SHMProcess : virtual public Process @@ -181,7 +193,7 @@ namespace DFHack Private * const d; public: - SHMProcess(vector & known_versions); + SHMProcess(uint32_t PID, vector & known_versions); ~SHMProcess(); // Set up stuff so we can read memory bool attach(); @@ -224,6 +236,12 @@ namespace DFHack memory_info *getDescriptor(); DFWindow * getWindow(); int getPID(); + // get module index by name and version. bool 1 = error + bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT); + // get the SHM start if available + char * getSHMStart (void); + // wait for a SHM state. returns 0 without the SHM + bool waitWhile (uint32_t state); }; #ifdef LINUX_BUILD @@ -277,6 +295,12 @@ namespace DFHack memory_info *getDescriptor(); DFWindow * getWindow(); int getPID(); + // get module index by name and version. bool 1 = error + bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; + // get the SHM start if available + char * getSHMStart (void){return 0;}; + // wait for a SHM state. returns 0 without the SHM + bool waitWhile (uint32_t state){return false;}; }; #endif } diff --git a/library/DFProcessEnumerator-linux.cpp b/library/DFProcessEnumerator-linux.cpp index f4d02e2a1..c4eddd127 100644 --- a/library/DFProcessEnumerator-linux.cpp +++ b/library/DFProcessEnumerator-linux.cpp @@ -46,19 +46,6 @@ bool ProcessEnumerator::findProcessess() { DIR *dir_p; struct dirent *dir_entry_p; - - Process *p = 0; - p = new SHMProcess(d->meminfo->meminfo); - if(p->isIdentified()) - { - d->processes.push_back(p); - } - else - { - delete p; - p = 0; - } - // Open /proc/ directory dir_p = opendir("/proc/"); // Reading /proc/ entries @@ -69,6 +56,16 @@ bool ProcessEnumerator::findProcessess() { 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()) { @@ -89,6 +86,7 @@ bool ProcessEnumerator::findProcessess() { delete p3; } + } closedir(dir_p); // return value depends on if we found some DF processes diff --git a/library/DFProcessEnumerator-windows.cpp b/library/DFProcessEnumerator-windows.cpp index f77aabb2d..7e3c095e8 100644 --- a/library/DFProcessEnumerator-windows.cpp +++ b/library/DFProcessEnumerator-windows.cpp @@ -67,21 +67,7 @@ bool ProcessEnumerator::findProcessess() { // Get the list of process identifiers. DWORD ProcArray[2048], memoryNeeded, numProccesses; - { - Process * p = new SHMProcess(d->meminfo->meminfo); - if(p->isIdentified()) - { - d->processes.push_back(p); - return true; - } - else - { - delete p; - p = 0; - } - } - - EnableDebugPriv(); + //EnableDebugPriv(); if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) { cout << "EnumProcesses fail'd" << endl; @@ -90,19 +76,32 @@ bool ProcessEnumerator::findProcessess() // Calculate how many process identifiers were returned. numProccesses = memoryNeeded / sizeof(DWORD); - + EnableDebugPriv(); + // iterate through processes for ( int i = 0; i < (int)numProccesses; i++ ) { - Process *q = new NormalProcess(ProcArray[i],d->meminfo->meminfo); - if(q->isIdentified()) + 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(q); + d->processes.push_back(p); + continue; } else { - delete q; - q = 0; + delete p; + p = 0; } } if(d->processes.size()) diff --git a/library/DFTypes.h b/library/DFTypes.h index 0c0b157a4..d478bcb4a 100644 --- a/library/DFTypes.h +++ b/library/DFTypes.h @@ -769,6 +769,17 @@ union t_occupancy naked_occupancy_grouped unibits; }; +typedef struct +{ + int16_t tiletypes [16][16]; + DFHack::t_designation designaton [16][16]; + DFHack::t_occupancy occupancy [16][16]; + // really a '7', but I use 8 to make it neater :) + uint8_t biome_indices [8]; + uint32_t origin; // the address where it came from + uint32_t dirty_dword; // bit 1 set means that the block is to be included in job checks +} mapblock40d; + struct t_viewscreen { int32_t type; diff --git a/shmserver/CMakeLists.txt b/shmserver/CMakeLists.txt index b8a80e15b..cc4b8cc57 100644 --- a/shmserver/CMakeLists.txt +++ b/shmserver/CMakeLists.txt @@ -2,10 +2,13 @@ SET(PROJECT_HDRS shms.h +mod-core.h +mod-maps.h ) SET(PROJECT_SRCS -shms-proto.cpp +mod-core.cpp +mod-maps.cpp ) SET(PROJECT_HDRS_LINUX @@ -35,15 +38,16 @@ SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE ) LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) -IF(UNIX) - add_definitions(-DLINUX_BUILD) - SET(PROJECT_LIBS rt) - SET(CMAKE_CXX_FLAGS "-fvisibility=hidden") - ADD_LIBRARY(dfconnect SHARED ${PROJECT_SRCS}) - TARGET_LINK_LIBRARIES(dfconnect ${PROJECT_LIBS}) -ELSE(UNIX) - # SET(PROJECT_LIBS psapi) - ADD_LIBRARY(SDL SHARED ${PROJECT_SRCS}) - TARGET_LINK_LIBRARIES(SDL ${PROJECT_LIBS}) -ENDIF(UNIX) - +IF(CMAKE_SIZEOF_VOID_P EQUAL 4) + IF(UNIX) + add_definitions(-DLINUX_BUILD) + SET(PROJECT_LIBS rt) + SET(CMAKE_CXX_FLAGS "-fvisibility=hidden") + ADD_LIBRARY(dfconnect SHARED ${PROJECT_SRCS}) + TARGET_LINK_LIBRARIES(dfconnect ${PROJECT_LIBS}) + ELSE(UNIX) + # SET(PROJECT_LIBS psapi) + ADD_LIBRARY(SDL SHARED ${PROJECT_SRCS}) + TARGET_LINK_LIBRARIES(SDL ${PROJECT_LIBS}) + ENDIF(UNIX) +ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 4) diff --git a/shmserver/mod-core.cpp b/shmserver/mod-core.cpp new file mode 100644 index 000000000..cd3137d62 --- /dev/null +++ b/shmserver/mod-core.cpp @@ -0,0 +1,292 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix) + +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. +*/ + +/** + * This is the source for the DF <-> dfhack shm bridge's core module. + */ + +#include +#include "../library/integers.h" +#include +#include +#include +#include + +#define SHM_INTERNAL // for things only visible to the SHM + +#include "shms.h" +#include "mod-core.h" +#include "mod-maps.h" + +std::vector module_registry; + +// various crud +extern int errorstate; +extern char *shm; +extern int shmid; +bool useYield = 0; + +#define SHMHDR ((shm_core_hdr *)shm) +#define SHMCMD ((shm_cmd *)shm)->pingpong +#define SHMDATA(type) ((type *)(shm + SHM_HEADER)) + +void ReadRaw (void * data) +{ + memcpy(shm + SHM_HEADER, (void *) SHMHDR->address,SHMHDR->length); +} + +void ReadDWord (void * data) +{ + SHMHDR->value = *((uint32_t*) SHMHDR->address); +} + +void ReadWord (void * data) +{ + SHMHDR->value = *((uint16_t*) SHMHDR->address); +} + +void ReadByte (void * data) +{ + SHMHDR->value = *((uint8_t*) SHMHDR->address); +} + +void WriteRaw (void * data) +{ + memcpy((void *)SHMHDR->address, shm + SHM_HEADER,SHMHDR->length); +} + +void WriteDWord (void * data) +{ + (*(uint32_t*)SHMHDR->address) = SHMHDR->value; +} + +void WriteWord (void * data) +{ + (*(uint16_t*)SHMHDR->address) = SHMHDR->value; +} + +void WriteByte (void * data) +{ + (*(uint8_t*)SHMHDR->address) = SHMHDR->value; +} + +void ReadSTLString (void * data) +{ + std::string * myStringPtr = (std::string *) SHMHDR->address; + unsigned int l = myStringPtr->length(); + SHMHDR->value = l; + // FIXME: there doesn't have to be a null terminator! + strncpy(shm+SHM_HEADER,myStringPtr->c_str(),l+1); +} + +void WriteSTLString (void * data) +{ + std::string * myStringPtr = (std::string *) SHMHDR->address; + // here we DO expect a 0 terminator + myStringPtr->assign((const char *) (shm + SHM_HEADER)); +} + +// MIT HAKMEM bitcount +int bitcount(uint32_t n) +{ + register uint32_t tmp; + + tmp = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111); + return ((tmp + (tmp >> 3)) & 030707070707) % 63; +} + +// get local and remote affinity, set up yield if required (single core available) +void CoreAttach (void * data) +{ + // sync affinity + uint32_t local = OS_getAffinity(); + uint32_t remote = SHMDATA(coreattach)->cl_affinity; + uint32_t pool = local | remote; + SHMDATA(coreattach)->sv_useYield = useYield = (bitcount(pool) == 1); + // return our PID + SHMDATA(coreattach)->sv_PID = OS_getPID(); + // return core version + SHMDATA(coreattach)->sv_version = module_registry[0].version; +} + +void FindModule (void * data) +{ + bool found = false; + modulelookup * payload = (modulelookup *) (shm + SHM_HEADER); + std::string test = payload->name; + uint32_t version = payload->version; + for(unsigned int i = 0; i < module_registry.size();i++) + { + if(module_registry[i].name == test && module_registry[i].version == version) + { + // gotcha + SHMHDR->value = i; + found = true; + break; + } + } + SHMHDR->error = !found; +} + +void FindCommand (void * data) +{ + bool found = false; + commandlookup * payload = SHMDATA(commandlookup); + std::string modname = payload->module; + std::string cmdname = payload->name; + uint32_t version = payload->version; + for(unsigned int i = 0; i < module_registry.size();i++) + { + if(module_registry[i].name == modname && module_registry[i].version == version) + { + for(unsigned int j = 0 ; j < module_registry[i].commands.size();j++) + { + if(module_registry[i].commands[j].name == cmdname) + { + // gotcha + SHMHDR->value = j + (i << 16); + SHMHDR->error = false; + return; + } + } + } + } + SHMHDR->error = true; +} + +DFPP_module InitCore(void) +{ + DFPP_module core; + core.name = "Core"; + core.version = CORE_VERSION; + core.modulestate = 0; // this one is dumb and has no real state + + core.reserve(NUM_CORE_CMDS); + // basic states + core.set_command(CORE_RUNNING, CANCELLATION, "Running"); + core.set_command(CORE_SUSPEND, CLIENT_WAIT, "Suspend", 0 , CORE_SUSPENDED); + core.set_command(CORE_SUSPENDED, CLIENT_WAIT, "Suspended"); + core.set_command(CORE_ERROR, CANCELLATION, "Error"); + + // utility commands + core.set_command(CORE_ATTACH, FUNCTION,"Core attach",CoreAttach, CORE_SUSPENDED); + core.set_command(CORE_ACQUIRE_MODULE, FUNCTION, "Module lookup", FindModule, CORE_SUSPENDED); + core.set_command(CORE_ACQUIRE_COMMAND, FUNCTION, "Command lookup", FindCommand, CORE_SUSPENDED); + + // raw reads + core.set_command(CORE_DFPP_READ, FUNCTION,"Raw read",ReadRaw, CORE_SUSPENDED); + core.set_command(CORE_READ_DWORD, FUNCTION,"Read DWORD",ReadDWord, CORE_SUSPENDED); + core.set_command(CORE_READ_WORD, FUNCTION,"Read WORD",ReadWord, CORE_SUSPENDED); + core.set_command(CORE_READ_BYTE, FUNCTION,"Read BYTE",ReadByte, CORE_SUSPENDED); + + // raw writes + core.set_command(CORE_WRITE, FUNCTION, "Raw write", WriteRaw, CORE_SUSPENDED); + core.set_command(CORE_WRITE_DWORD, FUNCTION, "Write DWORD", WriteDWord, CORE_SUSPENDED); + core.set_command(CORE_WRITE_WORD, FUNCTION, "Write WORD", WriteWord, CORE_SUSPENDED); + core.set_command(CORE_WRITE_BYTE, FUNCTION, "Write BYTE", WriteByte, CORE_SUSPENDED); + + // stl string commands + core.set_command(CORE_READ_STL_STRING, FUNCTION, "Read STL string", ReadSTLString, CORE_SUSPENDED); + core.set_command(CORE_READ_C_STRING, CLIENT_WAIT, "RESERVED"); + core.set_command(CORE_WRITE_STL_STRING, FUNCTION, "Write STL string", WriteSTLString, CORE_SUSPENDED); + return core; +} + +void InitModules (void) +{ + // create the core module + module_registry.push_back(InitCore()); + module_registry.push_back(InitMaps()); + // TODO: dynamic module init +} + +void KillModules (void) +{ + for(unsigned int i = 0; i < module_registry.size();i++) + { + if(module_registry[i].modulestate) + free(module_registry[i].modulestate); + } + module_registry.clear(); +} + +void SHM_Act (void) +{ + if(errorstate) + { + return; + } + uint32_t numwaits = 0; + check_again: // goto target!!! + if(numwaits == 10000) + { + // this tests if there's a process on the other side + if(isValidSHM()) + { + numwaits = 0; + } + else + { + full_barrier + SHMCMD = CORE_RUNNING; + fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n"); + } + } + + // this is very important! copying two words separately from the command variable leads to inconsistency. + // Always copy the thing in one go. + // Also, this whole SHM thing probably only works on intel processors + + volatile shm_cmd atomic = SHMHDR->cmd; + full_barrier + DFPP_module & mod = module_registry[atomic.parts.module]; + DFPP_command & cmd = mod.commands[atomic.parts.command]; + full_barrier + /* + fprintf(stderr, "Called %x\0", cmd._function); + fprintf(stderr, "Client invoked %d:%d = ",atomic.parts.module,atomic.parts.command); + fprintf(stderr, "%s\n",cmd.name.c_str()); + */ + full_barrier + if(cmd._function) + { + cmd._function(mod.modulestate); + } + full_barrier + if(cmd.nextState != -1) + { + SHMCMD = cmd.nextState; + } + full_barrier + if(cmd.type != CANCELLATION) + { + if(useYield) + { + SCHED_YIELD + } + numwaits ++; // watchdog timeout + goto check_again; + } +} + diff --git a/shmserver/mod-core.h b/shmserver/mod-core.h new file mode 100644 index 000000000..37717cb6f --- /dev/null +++ b/shmserver/mod-core.h @@ -0,0 +1,94 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix) + +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. +*/ + +#ifndef SHMS_CORE_H +#define SHMS_CORE_H + +// increment on every core change +#define CORE_VERSION 7 + +typedef struct +{ + shm_cmd cmd; + uint32_t address; + uint32_t value; + uint32_t length; + uint32_t error; +} shm_core_hdr; + +typedef struct +{ + uint32_t version; + char name[256]; +} modulelookup; + +typedef struct +{ + uint32_t version; + char module[256]; + char name[256]; +} commandlookup; + +typedef struct +{ + uint32_t sv_version; // output + uint32_t cl_affinity; // input + uint32_t sv_PID; // output + uint32_t sv_useYield; // output +} coreattach; + +enum CORE_COMMAND +{ + // basic states + CORE_RUNNING = 0, // no command, normal server execution + CORE_SUSPEND, // client notifies server to wait for commands (server is stalled in busy wait) + CORE_SUSPENDED, // response to WAIT, server is stalled in busy wait + CORE_ERROR, // there was a server error + + // utility commands + CORE_ATTACH, // compare affinity, get core version and process ID + CORE_ACQUIRE_MODULE, // get index of a loaded module by name and version + CORE_ACQUIRE_COMMAND, // get module::command callsign by module name, command name and module version + + // raw reads + CORE_DFPP_READ, // cl -> sv, read some data + CORE_READ_DWORD, // cl -> sv, read a dword + CORE_READ_WORD, // cl -> sv, read a word + CORE_READ_BYTE, // cl -> sv, read a byte + + // raw writes + CORE_WRITE,// client writes to server + CORE_WRITE_DWORD,// client writes a DWORD to server + CORE_WRITE_WORD,// client writes a WORD to server + CORE_WRITE_BYTE,// client writes a BYTE to server + + // string functions + CORE_READ_STL_STRING,// client requests contents of STL string at address + CORE_READ_C_STRING,// client requests contents of a C string at address, max length (0 means zero terminated) + CORE_WRITE_STL_STRING,// client wants to set STL string at address to something + + // total commands + NUM_CORE_CMDS +}; +#endif \ No newline at end of file diff --git a/shmserver/mod-maps.cpp b/shmserver/mod-maps.cpp new file mode 100644 index 000000000..54fe1cb57 --- /dev/null +++ b/shmserver/mod-maps.cpp @@ -0,0 +1,121 @@ +#include +#include +#include + +#include "shms.h" +#include "mod-core.h" +#include "mod-maps.h" +#include +using namespace DFHack; +using namespace DFHack::Maps; + +#include +#include + +extern char *shm; + +//TODO: circular buffer streaming primitives required +//TODO: commands can fail without the proper offsets. Hot to handle that? + +#define SHMHDR ((shm_maps_hdr *)shm) +#define SHMCMD ((shm_cmd *)shm)->pingpong +#define SHMDATA ((char *)(shm + SHM_HEADER)) + +void NullCommand (void* data) +{ +}; + +void InitOffsets (void* data) +{ + maps_modulestate * state = (maps_modulestate *) data; + memcpy((void *) &(state->offsets), SHMDATA, sizeof(maps_offsets)); + ((maps_modulestate *) data)->inited = true; +} + +void GetMapSize (void *data) +{ + maps_modulestate * state = (maps_modulestate *) data; + if(state->inited) + { + SHMHDR->x = *(uint32_t *) (state->offsets.x_count_offset); + SHMHDR->y = *(uint32_t *) (state->offsets.y_count_offset); + SHMHDR->z = *(uint32_t *) (state->offsets.z_count_offset); + SHMHDR->error = false; + } + else + { + SHMHDR->error = true; + } +} + +struct mblock +{ + uint32_t * ptr_to_dirty; +}; + +#define SHMBLOCK ((mapblock40d *)(shm + SHM_HEADER)) + +inline void ReadBlockByAddress (void * data) +{ + maps_modulestate * state = (maps_modulestate *) data; + maps_offsets & offsets = state->offsets; + mblock * block = (mblock *) SHMHDR->address; + if(block) + { + memcpy(&(SHMBLOCK->tiletypes), ((char *) block) + offsets.tile_type_offset, sizeof(SHMBLOCK->tiletypes)); + memcpy(&(SHMBLOCK->designaton), ((char *) block) + offsets.designation_offset, sizeof(SHMBLOCK->designaton)); + memcpy(&(SHMBLOCK->occupancy), ((char *) block) + offsets.occupancy_offset, sizeof(SHMBLOCK->occupancy)); + memcpy(&(SHMBLOCK->biome_indices), ((char *) block) + offsets.biome_stuffs, sizeof(SHMBLOCK->biome_indices)); + SHMBLOCK->dirty_dword = *block->ptr_to_dirty; + + SHMBLOCK->origin = (uint32_t)block; + SHMHDR->error = false; + } + else + { + SHMHDR->error = true; + } +} + +void ReadBlockByCoords (void * data) +{ + maps_modulestate * state = (maps_modulestate *) data; + maps_offsets & offsets = state->offsets; + /* map_offset is a pointer to + a pointer to + an X block of pointers to + an Y blocks of pointers to + a Z blocks of pointers to + map blocks + only Z blocks can have NULL pointers? TODO: verify + */ + mblock * *** mapArray = *(mblock * ****)offsets.map_offset; + SHMHDR->address = (uint32_t) mapArray[SHMHDR->x][SHMHDR->y][SHMHDR->z]; + ReadBlockByAddress(data); // I wonder... will this inline properly? +} + +DFPP_module InitMaps( void ) +{ + DFPP_module maps; + maps.name = "Maps"; + maps.version = MAPS_VERSION; + // freed by the core + maps.modulestate = malloc(sizeof(maps_modulestate)); // we store a flag + memset(maps.modulestate,0,sizeof(maps_modulestate)); + + maps.reserve(NUM_MAPS_CMDS); + + // client sends a maps_offsets struct -> inited = true; + maps.set_command(MAP_INIT, FUNCTION, "Supply the module with offsets",InitOffsets,CORE_SUSPENDED); + maps.set_command(MAP_GET_SIZE, FUNCTION, "Get map size in 16x16x1 tile blocks", GetMapSize, CORE_SUSPENDED); + maps.set_command(MAP_READ_BLOCK_BY_COORDS, FUNCTION, "Read the whole block with specified coords", ReadBlockByCoords, CORE_SUSPENDED); + maps.set_command(MAP_READ_BLOCK_BY_ADDRESS, FUNCTION, "Read the whole block from an address", ReadBlockByAddress, CORE_SUSPENDED); + + // will it fit into 1MB? We shouldn't assume this is the case + maps.set_command(MAP_READ_BLOCKTREE, FUNCTION,"Get the tree of block pointers as a single structure", NullCommand, CORE_SUSPENDED); + + // really doesn't fit into 1MB, there should be a streaming variant to better utilize context switches + maps.set_command(MAP_READ_BLOCKS_3D, FUNCTION, "Read a range of blocks between two sets of coords", NullCommand, CORE_SUSPENDED); + + return maps; +} \ No newline at end of file diff --git a/shmserver/mod-maps.h b/shmserver/mod-maps.h new file mode 100644 index 000000000..5ff5f0f2e --- /dev/null +++ b/shmserver/mod-maps.h @@ -0,0 +1,102 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix) + +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. +*/ + +#ifndef MOD_MAPS_H +#define MOD_MAPS_H + +// increment on every change +#include + +namespace DFHack +{ + namespace Maps + { + +#define MAPS_VERSION 2 +typedef struct +{ + uint32_t map_offset;// = d->offset_descriptor->getAddress ("map_data"); + uint32_t x_count_offset;// = d->offset_descriptor->getAddress ("x_count"); + uint32_t y_count_offset;// = d->offset_descriptor->getAddress ("y_count"); + uint32_t z_count_offset;// = d->offset_descriptor->getAddress ("z_count"); + uint32_t tile_type_offset;// = d->offset_descriptor->getOffset ("type"); + uint32_t designation_offset;// = d->offset_descriptor->getOffset ("designation"); + uint32_t occupancy_offset;// = d->offset_descriptor->getOffset ("occupancy"); + uint32_t biome_stuffs;// = d->offset_descriptor->getOffset ("biome_stuffs"); + uint32_t veinvector;// = d->offset_descriptor->getOffset ("v_vein"); + uint32_t vein_mineral_vptr; + uint32_t vein_ice_vptr; + /* + GEOLOGY + uint32_t region_x_offset;// = minfo->getAddress ("region_x"); + uint32_t region_y_offset;// = minfo->getAddress ("region_y"); + uint32_t region_z_offset;// = minfo->getAddress ("region_z"); + uint32_t world_offset;// = minfo->getAddress ("world"); + uint32_t world_regions_offset;// = minfo->getOffset ("w_regions_arr"); + uint32_t region_size;// = minfo->getHexValue ("region_size"); + uint32_t region_geo_index_offset;// = minfo->getOffset ("region_geo_index_off"); + uint32_t world_geoblocks_offset;// = minfo->getOffset ("w_geoblocks"); + uint32_t world_size_x;// = minfo->getOffset ("world_size_x"); + uint32_t world_size_y;// = minfo->getOffset ("world_size_y"); + uint32_t geolayer_geoblock_offset;// = minfo->getOffset ("geolayer_geoblock_offset"); + */ +} maps_offsets; + +typedef struct +{ + bool inited; + maps_offsets offsets; +} maps_modulestate; + +typedef struct +{ + shm_cmd cmd; + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t x2; + uint32_t y2; + uint32_t z2; + uint32_t address; + uint32_t error; +} shm_maps_hdr; + +enum MAPS_COMMAND +{ + MAP_INIT = 0, // initialization + MAP_PROBE, // check if the map is still there + MAP_GET_SIZE, // get the map size in 16x16x1 blocks + MAP_READ_BLOCKTREE, // read the structure of pointers to blocks + MAP_READ_BLOCK_BY_COORDS, // read block by cords + MAP_READ_BLOCK_BY_ADDRESS, // read block by address + MAP_WRITE_BLOCK, + MAP_READ_BLOCKS_3D, // read blocks between two coords (volumetric) + MAP_READ_ALL_BLOCKS, // read the entire map + MAP_REVEAL, // reveal the whole map + NUM_MAPS_CMDS, +}; + } +} + +#endif \ No newline at end of file diff --git a/shmserver/shms-linux.cpp b/shmserver/shms-linux.cpp index f78bf7419..79e185203 100644 --- a/shmserver/shms-linux.cpp +++ b/shmserver/shms-linux.cpp @@ -34,12 +34,11 @@ distribution. #include #include #include +#include +#include #include "shms.h" -#include -#include -#include -#include -#include +#include "mod-core.h" +#include #define DFhackCExport extern "C" __attribute__ ((visibility("default"))) @@ -62,11 +61,20 @@ bool isValidSHM() //fprintf(stderr,"ID %d, attached: %d\n",shmid, descriptor.shm_nattch); return (descriptor.shm_nattch == 2); } -uint32_t getPID() +uint32_t OS_getPID() { return getpid(); } +uint32_t OS_getAffinity() +{ + cpu_set_t mask; + sched_getaffinity(0,sizeof(cpu_set_t),&mask); + // FIXME: truncation + uint32_t affinity = *(uint32_t *) &mask; + return affinity; +} + void SHM_Init ( void ) { // check that we do this only once per process @@ -77,8 +85,8 @@ void SHM_Init ( void ) } inited = true; - // name for the segment - key_t key = 123466; + // name for the segment, an accident waiting to happen + key_t key = SHM_KEY + OS_getPID(); // find previous segment, check if it's used by some processes. // if it isn't, kill it with fire @@ -109,13 +117,15 @@ void SHM_Init ( void ) } full_barrier // make sure we don't stall or do crazy stuff - ((shm_cmd *)shm)->pingpong = DFPP_RUNNING; + ((shm_cmd *)shm)->pingpong = CORE_RUNNING; + InitModules(); } void SHM_Destroy ( void ) { if(inited && !errorstate) { + KillModules(); shmid_ds descriptor; shmctl(shmid, IPC_STAT, &descriptor); shmdt(shm); @@ -144,7 +154,7 @@ DFhackCExport void SDL_GL_SwapBuffers(void) { if(_SDL_GL_SwapBuffers) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) { SHM_Act(); } @@ -158,7 +168,7 @@ DFhackCExport int SDL_Flip(void * some_ptr) { if(_SDL_Flip) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) { SHM_Act(); } @@ -216,7 +226,7 @@ DFhackCExport int refresh (void) { if(_refresh) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) { SHM_Act(); } diff --git a/shmserver/shms-proto.cpp b/shmserver/shms-proto.cpp deleted file mode 100644 index ded5a4959..000000000 --- a/shmserver/shms-proto.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* -www.sourceforge.net/projects/dfhack -Copyright (c) 2009 Petr Mrázek (peterix) - -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. -*/ - -/** - * This is the source for the DF <-> dfhack shm bridge, server protocol part - */ -#include -#include "../library/integers.h" -#include -#include -#include -//#include -#include "shms.h" -// various crud -extern int errorstate; -extern char *shm; -extern int shmid; - -void SHM_Act (void) -{ - if(errorstate) - { - return; - } - uint32_t numwaits = 0; - uint32_t length; - uint32_t address; - std::string * myStringPtr; - check_again: // goto target!!! - SCHED_YIELD // yield the CPU, valid only on single-core CPUs - if(numwaits == 10000) - { - // this tests if there's a process on the other side - if(isValidSHM()) - { - numwaits = 0; - } - else - { - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_RUNNING; - fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n"); - //MessageBox(0,"Broke out of loop, other process disappeared.","FUN", MB_OK); - } - } - switch (((shm_cmd *)shm)->pingpong) - { - case DFPP_RET_VERSION: - case DFPP_RET_DATA: - case DFPP_RET_DWORD: - case DFPP_RET_WORD: - case DFPP_RET_BYTE: - case DFPP_RET_STRING: - case DFPP_SUSPENDED: - case DFPP_RET_PID: - case DFPP_SV_ERROR: - numwaits++; - goto check_again; - case DFPP_SUSPEND: - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - /* - case DFPP_BOUNCE: - length = ((shm_bounce *)shm)->length; - memcpy(BigFat,shm + SHM_HEADER,length); - memcpy(shm + SHM_HEADER,BigFat,length); - ((shm_cmd *)shm)->pingpong = DFPP_RET_DATA; - goto check_again; - */ - case DFPP_PID: - ((shm_retval *)shm)->value = getPID(); - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_PID; - goto check_again; - - case DFPP_VERSION: - ((shm_retval *)shm)->value = PINGPONG_VERSION; - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_VERSION; - goto check_again; - - case DFPP_READ: - length = ((shm_read *)shm)->length; - address = ((shm_read *)shm)->address; - memcpy(shm + SHM_HEADER, (void *) address,length); - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_RET_DATA; - goto check_again; - - case DFPP_READ_DWORD: - address = ((shm_read_small *)shm)->address; - ((shm_retval *)shm)->value = *((uint32_t*) address); - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_DWORD; - goto check_again; - - case DFPP_READ_WORD: - address = ((shm_read_small *)shm)->address; - ((shm_retval *)shm)->value = *((uint16_t*) address); - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_WORD; - goto check_again; - - case DFPP_READ_BYTE: - address = ((shm_read_small *)shm)->address; - ((shm_retval *)shm)->value = *((uint8_t*) address); - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_BYTE; - goto check_again; - - case DFPP_WRITE: - address = ((shm_write *)shm)->address; - length = ((shm_write *)shm)->length; - memcpy((void *)address, shm + SHM_HEADER,length); - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - - case DFPP_WRITE_DWORD: - (*(uint32_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - - case DFPP_WRITE_WORD: - (*(uint16_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - - case DFPP_WRITE_BYTE: - (*(uint8_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - - case DFPP_CL_ERROR: - case DFPP_RUNNING: - //fprintf(stderr, "no. of waits: %d\n", numwaits); - //MessageBox(0,"Broke out of loop properly","FUN", MB_OK); - break; - - case DFPP_READ_STL_STRING: - myStringPtr = (std::string *) ((shm_read_small *)shm)->address; - ((shm_retval *)shm)->value = myStringPtr->length(); - strncpy(shm+SHM_HEADER,myStringPtr->c_str(),myStringPtr->length()+1);// length + 1 for the null terminator - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_RET_STRING; - goto check_again; - - case DFPP_WRITE_STL_STRING: - myStringPtr = (std::string *) ((shm_write *)shm)->address; - myStringPtr->assign((const char *) (shm + SHM_HEADER)); - full_barrier - ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; - goto check_again; - - - default: - ((shm_retval *)shm)->value = DFEE_INVALID_COMMAND; - full_barrier - ((shm_retval *)shm)->pingpong = DFPP_SV_ERROR; - break; - } -} diff --git a/shmserver/shms-windows.cpp b/shmserver/shms-windows.cpp index 5a43f885e..9581ad68f 100644 --- a/shmserver/shms-windows.cpp +++ b/shmserver/shms-windows.cpp @@ -34,7 +34,10 @@ distribution. #define DFhackCExport extern "C" __declspec(dllexport) #include "../library/integers.h" +#include +#include #include "shms.h" +#include "mod-core.h" #include int errorstate = 0; char *shm = 0; @@ -53,21 +56,28 @@ void SHM_Init ( void ) } inited = true; + char svmutexname [256]; + sprintf(svmutexname,"DFSVMutex-%d",OS_getPID()); + char clmutexname [256]; + sprintf(clmutexname,"DFCLMutex-%d",OS_getPID()); + char shmname [256]; + sprintf(shmname,"DFShm-%d",OS_getPID()); + // create or open mutexes - DFSVMutex = CreateMutex( 0, 1, "DFSVMutex"); + DFSVMutex = CreateMutex( 0, 1, svmutexname); if(DFSVMutex == 0) { - DFSVMutex = OpenMutex(SYNCHRONIZE,false, "DFSVMutex"); + DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname); if(DFSVMutex == 0) { errorstate = 1; return; } } - DFCLMutex = CreateMutex( 0, 0, "DFCLMutex"); + DFCLMutex = CreateMutex( 0, 0, clmutexname); if(DFCLMutex == 0) { - DFCLMutex = OpenMutex(SYNCHRONIZE,false, "DFCLMutex"); + DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname); if(DFCLMutex == 0) { CloseHandle(DFSVMutex); @@ -107,7 +117,7 @@ void SHM_Init ( void ) } // create virtual memory mapping - shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_SIZE,"DFShm"); + shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_SIZE,shmname); // if can't create or already exists -> nothing happens if(GetLastError() == ERROR_ALREADY_EXISTS) { @@ -131,8 +141,7 @@ void SHM_Init ( void ) shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); if(shm) { - ((shm_cmd *)shm)->pingpong = DFPP_RUNNING; - //MessageBox(0,"Sucessfully mapped SHM","FUN", MB_OK); + ((shm_cmd *)shm)->pingpong = CORE_RUNNING; } else { @@ -142,22 +151,35 @@ void SHM_Init ( void ) CloseHandle(DFSVMutex); CloseHandle(DFCLMutex); } + InitModules(); } void SHM_Destroy ( void ) { if(errorstate) return; + KillModules(); ReleaseMutex(DFSVMutex); CloseHandle(DFSVMutex); CloseHandle(DFCLMutex); } -uint32_t getPID() +uint32_t OS_getPID() { return GetCurrentProcessId(); } +// TODO: move to some utils file +uint32_t OS_getAffinity() +{ + HANDLE hProcess = GetCurrentProcess(); + DWORD dwProcessAffinityMask, dwSystemAffinityMask; + GetProcessAffinityMask( hProcess, &dwProcessAffinityMask, &dwSystemAffinityMask ); + return dwProcessAffinityMask; +} + + + // is the other side still there? bool isValidSHM() { @@ -665,7 +687,7 @@ DFhackCExport void SDL_Quit(void) static void (*_SDL_GL_SwapBuffers)(void) = 0; DFhackCExport void SDL_GL_SwapBuffers(void) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) { SHM_Act(); } @@ -678,7 +700,7 @@ DFhackCExport int SDL_Flip(void * some_ptr) { if(_SDL_Flip) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) { SHM_Act(); } diff --git a/shmserver/shms.h b/shmserver/shms.h index a056c8a41..174614c8e 100644 --- a/shmserver/shms.h +++ b/shmserver/shms.h @@ -1,10 +1,9 @@ #ifndef DFCONNECT_H #define DFCONNECT_H -#define PINGPONG_VERSION 2 #define SHM_KEY 123466 -#define SHM_HEADER 1024 -#define SHM_BODY 1024*1024 +#define SHM_HEADER 1024 // 1kB reserved for a header +#define SHM_BODY 1024*1024 // 1MB reserved for bulk data transfer #define SHM_SIZE SHM_HEADER+SHM_BODY @@ -12,16 +11,14 @@ #ifdef LINUX_BUILD // a full memory barrier! better be safe than sorry. #define full_barrier asm volatile("" ::: "memory"); __sync_synchronize(); - #define SCHED_YIELD sched_yield(); // slow but allows the SHM to work on single-core - // #define SCHED_YIELD usleep(0); // extremely slow - // #define SCHED_YIELD // works only on multi-core + #define SCHED_YIELD sched_yield(); // a requirement for single-core #else // we need windows.h for Sleep() #define _WIN32_WINNT 0x0501 // needed for INPUT struct #define WINVER 0x0501 // OpenThread(), PSAPI, Toolhelp32 #define WIN32_LEAN_AND_MEAN #include - #define SCHED_YIELD Sleep(0); // slow on single-core, but avoids infinite lockup + #define SCHED_YIELD Sleep(0); // avoids infinite lockup on single core // FIXME: detect MSVC here and use the right barrier magic #ifdef __MINGW32__ #define full_barrier asm volatile("" ::: "memory"); @@ -32,124 +29,76 @@ #endif #endif - -/* - * read - parameters are address and length - * write - parameters are address, length and the actual data to write - * wait - sent to DF so that it waits for more commands - * end - sent to DF for breaking out of the wait -*/ - -enum DF_SHM_ERRORSTATE +enum DFPP_CmdType { - SHM_OK, // all OK - SHM_CANT_GET_SHM, // getting the SHM ID failed for some reason - SHM_CANT_ATTACH, // we can't attach the shm for some reason - SHM_SECOND_DF // we are a second DF process, can't use SHM at all + CANCELLATION, // we should jump out of the Act() + CLIENT_WAIT, // we are waiting for the client + FUNCTION, // we call a function as a result of the command }; -enum DF_PINGPONG +struct DFPP_command { - DFPP_RUNNING = 0, // no command, normal server execution - - DFPP_VERSION, // protocol version query - DFPP_RET_VERSION, // return the protocol version - - DFPP_PID, // query for the process ID - DFPP_RET_PID, // return process ID - - // version 1 stuff below - DFPP_READ, // cl -> sv, read some data - DFPP_RET_DATA, // sv -> cl, returned data - - DFPP_READ_DWORD, // cl -> sv, read a dword - DFPP_RET_DWORD, // sv -> cl, returned dword - - DFPP_READ_WORD, // cl -> sv, read a word - DFPP_RET_WORD, // sv -> cl, returned word - - DFPP_READ_BYTE, // cl -> sv, read a byte - DFPP_RET_BYTE, // sv -> cl, returned byte - - DFPP_SV_ERROR, // there was a server error - DFPP_CL_ERROR, // there was a client error - - DFPP_WRITE,// client writes to server - DFPP_WRITE_DWORD,// client writes a DWORD to server - DFPP_WRITE_WORD,// client writes a WORD to server - DFPP_WRITE_BYTE,// client writes a BYTE to server - - DFPP_SUSPEND, // client notifies server to wait for commands (server is stalled in busy wait) - DFPP_SUSPENDED, // response to WAIT, server is stalled in busy wait - - // all strings capped at 1MB - DFPP_READ_STL_STRING,// client requests contents of STL string at address - DFPP_READ_C_STRING,// client requests contents of a C string at address, max length (0 means zero terminated) - DFPP_RET_STRING, // sv -> cl length + string contents - DFPP_WRITE_STL_STRING,// client wants to set STL string at address to something - - // vector elements > 1MB are not supported because they don't fit into the shared memory - DFPP_READ_ENTIRE_VECTOR, // read an entire vector (parameters are address of vector object and size of items) - DFPP_RET_VECTOR_BODY, // a part of a vector is returned - no. of elements returned, no. of elements total, elements - - NUM_DFPP + void (*_function)(void *); + DFPP_CmdType type:32; // force the enum to 32 bits for compatibility reasons + std::string name; + uint32_t nextState; }; - -enum DF_ERROR +struct DFPP_module { - DFEE_INVALID_COMMAND, - DFEE_BUFFER_OVERFLOW + DFPP_module() + { + name = "Uninitialized module"; + version = 0; + modulestate = 0; + } + // ALERT: the structures share state + DFPP_module(const DFPP_module & orig) + { + commands = orig.commands; + name = orig.name; + modulestate = orig.modulestate; + version = orig.version; + } + inline void set_command(const unsigned int index, const DFPP_CmdType type, const char * name, void (*_function)(void *) = 0,uint32_t nextState = -1) + { + commands[index].type = type; + commands[index].name = name; + commands[index]._function = _function; + commands[index].nextState = nextState; + } + inline void reserve (unsigned int numcommands) + { + commands.clear(); + DFPP_command cmd = {0,CANCELLATION,"",0}; + commands.resize(numcommands,cmd); + } + std::string name; + uint32_t version; // version + std::vector commands; + void * modulestate; }; -typedef struct -{ - volatile uint32_t pingpong; // = 0 -} shm_cmd; - -typedef struct -{ - volatile uint32_t pingpong; - uint32_t address; - uint32_t length; -} shm_read; - -typedef shm_read shm_write; -typedef shm_read shm_bounce; - -typedef struct -{ - volatile uint32_t pingpong; -} shm_ret_data; - -typedef struct +typedef union { + struct + { + volatile uint16_t command; + volatile uint16_t module; + } parts; volatile uint32_t pingpong; - uint32_t address; -} shm_read_small; - -typedef struct -{ - volatile uint32_t pingpong; - uint32_t address; - uint32_t value; -} shm_write_small; - -typedef struct -{ - volatile uint32_t pingpong; - uint32_t value; -} shm_retval; - -typedef struct -{ - volatile uint32_t pingpong; - uint32_t length; -} shm_retstr; - + inline void set(uint16_t module, uint16_t command) + { + pingpong = module + command << 16; + } +} shm_cmd; void SHM_Act (void); +void InitModules (void); +void KillModules (void); bool isValidSHM(); -uint32_t getPID(); +uint32_t OS_getPID(); +DFPP_module InitMaps(void); +uint32_t OS_getAffinity(); // limited to 32 processors. Silly, eh? #endif