diff --git a/configure.ac b/configure.ac index 48687872a33..3cb8833c09b 100644 --- a/configure.ac +++ b/configure.ac @@ -546,7 +546,7 @@ dnl Define kernel version dnl -gap_kernel_major_version=5 +gap_kernel_major_version=6 gap_kernel_minor_version=0 AC_SUBST([gap_kernel_major_version]) AC_SUBST([gap_kernel_minor_version]) diff --git a/doc/ref/debug.xml b/doc/ref/debug.xml index d604b5567c4..21fb4909cbc 100644 --- a/doc/ref/debug.xml +++ b/doc/ref/debug.xml @@ -791,6 +791,11 @@ limitations which users should be aware of: is most obvious when looking at code coverage examples, which will appear to miss lines of code in files not in a function. + + If the current GAP is forked, using the IO_fork function in the IO package, + a new profile output file will be created for the new child process, with + the process ID of the child attached to the end of the filename. + Profiles are transformed into a human-readable form with diff --git a/src/profile.c b/src/profile.c index 198be4b8046..d9b22115ff5 100644 --- a/src/profile.c +++ b/src/profile.c @@ -22,11 +22,14 @@ #include "modules.h" #include "plist.h" #include "stringobj.h" +#include "sysfiles.h" #include "vars.h" #include "hpc/thread.h" #include // for gettimeofday +#include +#include #ifdef HAVE_SYS_RESOURCE_H #include // definition of 'struct rusage' #endif @@ -114,6 +117,8 @@ struct ProfileState { // C steam we are writing to FILE* Stream; + // Filename we are writing to + char filename[GAP_PATH_MAX]; // Did we use 'popen' to open the stream (matters when closing) int StreamWasPopened; // Are we currently outputting repeats (false=code coverage) @@ -337,6 +342,26 @@ static void fcloseMaybeCompressed(struct ProfileState* ps) ps->Stream = 0; } +// When a child is forked off, we force profile information to be stored +// in a new file for the child, to avoid corruption +void InformProfilingThatThisIsAForkedGAP(void) +{ + HashLock(&profileState); + if (profileState_Active) { + char filenamecpy[GAP_PATH_MAX + 20]; + if (endsWithgz(profileState.filename)) { + snprintf(filenamecpy, sizeof(filenamecpy), "%s.%d.gz", + profileState.filename, getpid()); + } + else { + snprintf(filenamecpy, sizeof(filenamecpy), "%s.%d", + profileState.filename, getpid()); + } + fcloseMaybeCompressed(&profileState); + fopenMaybeCompressed(filenamecpy, &profileState); + } +} + static inline Int8 CPUmicroseconds(void) { #ifdef HAVE_GETRUSAGE @@ -576,6 +601,8 @@ void enableAtStartup(char * filename, Int repeats, TickMethod tickMethod) exit(1); } + strlcpy(profileState.filename, filename, GAP_PATH_MAX); + ActivateHooks(&profileHooks); profileState_Active = 1; @@ -706,6 +733,8 @@ Obj FuncACTIVATE_PROFILING(Obj self, fopenMaybeCompressed(CONST_CSTR_STRING(filename), &profileState); + strlcpy(profileState.filename, CONST_CSTR_STRING(filename), GAP_PATH_MAX); + if(profileState.Stream == 0) { HashUnlock(&profileState); return Fail; diff --git a/src/profile.h b/src/profile.h index ddfd79ba66b..908938be204 100644 --- a/src/profile.h +++ b/src/profile.h @@ -19,6 +19,11 @@ *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * * */ +// When a child is forked off, we force profile information to be stored +// in a new file for the child, to avoid corruption. +// This function is for use by the IO package +void InformProfilingThatThisIsAForkedGAP(void); + /**************************************************************************** **