/* In order to manage code coverage for embedded projects, VisualGDB requires a build of the GCovServer made using a MinGW GCC of the same gcc version that was used to build the embedded code. E.g. if the target is built with GCC 9.2 for arm-eabi, VisualGDB will need a version of GCovServer built with GCC 9.2 for MinGW32. Minor GCC releases might be compatible with each other, however using exactly the same version is recommended for the best results. You can build the GCovServer with the following g++ command line: g++.exe GCovServer.cpp -o GCovServer-VERSION.exe -O3 -static-libgcc -lgcov -s Then place the built executable into this directory and add it to index.xml. When analyzing the code coverage, VisualGDB will pick the first record from index.xml that matches the gcc version used in the project, i.e.: [Actual version] >= MinVersion && [Actual version] <= MaxVersion You can also copy this entire directory to %LOCALAPPDATA%\VisualGDB\GCovServer and add your GCov server builds there. They will override any builds shipped with VisualGDB. If you don't have a ready-to-use MinGW GCC, you can build one from sources by installing the binutils-mingw-w64-i686 and g++-mingw-w64-i686 packages on your Linux system and then configuring GCC with the following command line: ../configure --host=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix= --enable-languages=c,c++ --disable-nls Then run "make -j16 && make install" and deploy the built GCC binaries (including libgcov) into your MinGW environment. */ #include #include #include using namespace std; struct OutParameterBlock { OutParameterBlock *Self; uint32_t ServerVersion; uint32_t Reserved[14]; void *FunctionTable[1024 - 16]; }; static_assert(sizeof(OutParameterBlock) == 4096, "Invalid parameter block size"); struct InParameterBlock { uint32_t Shutdown; int GcovInfoCount; struct gcov_info **GcovInfoVector; }; struct SharedMemoryBlock { OutParameterBlock Out; InParameterBlock In; }; enum { kGCovServerVersion = 1 }; extern "C" { void __gcov_merge_add(); void __gcov_dump_one(struct gcov_root *root); #include } struct gcov_info { uint32_t version; struct gcov_info *next; }; struct gcov_root { struct gcov_info *list; unsigned dumped : 1; /* counts have been dumped. */ unsigned run_counted : 1; /* run has been accounted for. */ struct gcov_root *next; struct gcov_root *prev; }; static struct gcov_root s_LocalGcovRoot; int main(int argc, char *argv[]) { if (argc < 4) { printf("Usage: GCovServer [mapping] [in semaphore] [out semaphore]\n"); return 1; } HANDLE hMapping = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, argv[1]); HANDLE hInSemaphore = OpenSemaphoreA(EVENT_ALL_ACCESS, FALSE, argv[2]); HANDLE hOutSemaphore = OpenSemaphoreA(EVENT_ALL_ACCESS, FALSE, argv[3]); if (hMapping == INVALID_HANDLE_VALUE || hInSemaphore == INVALID_HANDLE_VALUE || hOutSemaphore == INVALID_HANDLE_VALUE) { printf("Failed to open control structures\n"); return 1; } SharedMemoryBlock *pBlock = reinterpret_cast(MapViewOfFile(hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0)); if (!pBlock) { printf("Failed to map shared memory block\n"); return 1; } pBlock->Out.Self = &pBlock->Out; pBlock->Out.ServerVersion = kGCovServerVersion; pBlock->Out.FunctionTable[0] = (void *)&__gcov_merge_add; ReleaseSemaphore(hOutSemaphore, 1, nullptr); for (;;) { WaitForSingleObject(hInSemaphore, INFINITE); if (pBlock->In.Shutdown) return 0; memset(&s_LocalGcovRoot, 0, sizeof(s_LocalGcovRoot)); for (int i = 0; i < pBlock->In.GcovInfoCount; i++) { if (!pBlock->In.GcovInfoVector[i]) continue; pBlock->In.GcovInfoVector[i]->next = s_LocalGcovRoot.list; s_LocalGcovRoot.list = pBlock->In.GcovInfoVector[i]; } __gcov_dump_one(&s_LocalGcovRoot); ReleaseSemaphore(hOutSemaphore, 1, nullptr); } return 0; }