staticstruct ProfileState
{ // Is profiling currently active
ProfileActiveEnum status; // 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) int OutputRepeats;
// Used to generate 'X' statements, to make sure we correctly // attach each function call to the line it was executed on struct StatementLocation lastNotOutputted;
// Record last executed statement, to avoid repeats struct StatementLocation lastOutputted; int lastOutputtedExec;
Int8 lastOutputtedTime;
TickMethod tickMethod;
int minimumProfileTick; #ifdef HPCGAP int profiledThread; #endif
// Have we previously profiled this execution of GAP? We need this because // code coverage doesn't work more than once, as we use a bit in each Stat // to mark if we previously executed this statement, which we can't // clear
UInt profiledPreviously;
Int LongJmpOccurred;
// We store the value of RecursionDepth each time we enter a function. // This is the only way to detect if GAP has left a function by performing // a longjmp. // We need to store the actual values, as RecursionDepth can increase // by more than one when a GAP function is called
Obj visitedDepths;
} profileState;
// Some GAP functionality (such as syntaxtree) evaluates expressions, which makes // them appear executed in profiles. The functions pauseProfiling and unpauseProfiling // temporarily enable and disable profiling to avoid this problem. void pauseProfiling(void)
{ if (profileState.status == Profile_Active) {
profileState.status = Profile_Paused;
}
}
// Output information about how this profile was configured staticvoid outputVersionInfo(void)
{ constchar timeTypeNames[3][10] = { "WallTime", "CPUTime", "Memory" };
fprintf(profileState.Stream, "{ \"Type\": \"_\", \"Version\":1, \"IsCover\": %s, " " \"TimeType\": \"%s\"}\n",
profileState.OutputRepeats ? "false" : "true",
timeTypeNames[profileState.tickMethod]); // Explicitly flush, so this information is in the file // even if GAP crashes
fflush(profileState.Stream);
}
// This function is called when we detect a longjmp occurred, and // outputs a 'return' into the profile for any function which was // jumped over. // It is fine for this function to be called when a longjmp has not // occurred, or when no function was longjmped over. staticvoid CheckLeaveFunctionsAfterLongjmp(void)
{ if (!profileState.LongJmpOccurred) return;
#ifdef HPCGAP if (profileState.profiledThread != TLS(threadID)) return; #endif
profileState.LongJmpOccurred = 0;
Int pos = LEN_PLIST(profileState.visitedDepths); Int depth = GetRecursionDepth();
while (pos > 0 && INT_INTOBJ(ELM_PLIST(profileState.visitedDepths, pos)) > depth) { // Give dummy values if we do not know
fprintf(profileState.Stream, "{\"Type\":\"O\",\"Fun\":\"nameless\",\"Line\":-1," "\"EndLine\":-1,\"File\":\"<missing filename>\"," "\"FileId\":-1}\n");
PopPlist(profileState.visitedDepths);
pos--;
}
}
// Escape a string for serialization in a JSON file, following the // escaping rules laid out at json.org. static Obj JsonEscapeString(Obj param)
{ Int lenString = LEN_LIST(param);
// Allocate an output string with twice the size of the input // string, as in the worst case every single has to be escaped.
Obj copy = NEW_STRING(lenString * 2);
UChar * in = CHARS_STRING(param);
UChar * base = CHARS_STRING(copy);
UChar * out = base;
for (Int i = 0; i < lenString; ++i) {
UChar u = in[i]; switch (u) { case'\\': case'"': case'/':
out[0] = '\\';
out[1] = u;
out += 2; break; #define ESCAPE_CASE(x, y) \ case x: \
out[0] = '\\'; \
out[1] = y; \
out += 2; \ break;
ESCAPE_CASE('\b', 'b');
ESCAPE_CASE('\t', 't');
ESCAPE_CASE('\n', 'n');
ESCAPE_CASE('\f', 'f');
ESCAPE_CASE('\r', 'r'); #undef ESCAPE_CASE default:
*(out++) = u;
}
}
staticinlinevoid outputFilenameIdIfRequired(UInt id)
{ if (id == 0) { return;
} if (LEN_PLIST(OutputtedFilenameList) < id ||
ELM_PLIST(OutputtedFilenameList, id) != True) {
AssPlist(OutputtedFilenameList, id, True);
fprintf(profileState.Stream, "{\"Type\":\"S\",\"File\":\"%s\",\"FileId\":%d}\n",
CONST_CSTR_STRING(JsonEscapeString(GetCachedFilename(id))),
(int)id);
}
}
// This function checks gets the filenameId of the current function. staticinline UInt getFilenameIdOfCurrentFunction(void)
{
Obj func = CURR_FUNC();
Obj body = BODY_FUNC(func); return GET_GAPNAMEID_BODY(body);
}
// We output 'File' here for compatibility with // profiling v1.3.0 and earlier, FileId provides the same information // in a more useful and compact form.
fprintf(profileState.Stream, "{\"Type\":\"%c\",\"Fun\":\"%s\",\"Line\":%" "d,\"EndLine\":%d,\"File\":\"%s\"," "\"FileId\":%d}\n",
type, name_c, (int)startline, (int)endline, filename_c,
(int)fileid);
}
HashUnlock(&profileState);
}
staticvoid leaveFunction(Obj func)
{ #ifdef HPCGAP if (profileState.profiledThread != TLS(threadID)) return; #endif // Do not crash if we exit the function in which // Profile was originally called. The profiling // package can handle such profiles. if (LEN_PLIST(profileState.visitedDepths) > 0) {
PopPlist(profileState.visitedDepths);
}
CheckLeaveFunctionsAfterLongjmp();
// 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.status == Profile_Active) { char filenamecpy[GAP_PATH_MAX]; // Allow 20 characters to allow space for .%d.gz constint SUPPORTED_PATH_LEN = GAP_PATH_MAX - 20; if(strlen(profileState.filename) > SUPPORTED_PATH_LEN) {
Panic("Filename can be at most %d character when forking", SUPPORTED_PATH_LEN);
} if (endsWithgz(profileState.filename)) {
snprintf(filenamecpy, sizeof(filenamecpy), "%.*s.%d.gz",
SUPPORTED_PATH_LEN, profileState.filename, getpid());
} else {
snprintf(filenamecpy, sizeof(filenamecpy), "%.*s.%d",
SUPPORTED_PATH_LEN, (char*)profileState.filename, getpid());
}
fcloseMaybeCompressed(&profileState);
fopenMaybeCompressed(filenamecpy, &profileState);
outputVersionInfo(); // Need to flush list of outputted files, as we will start a fresh file
OutputtedFilenameList = NEW_PLIST(T_PLIST, 0);
}
HashUnlock(&profileState);
}
// type : the type of the statement // exec : are we executing this statement // visit: Was this statement previously visited (that is, executed) staticinlinevoid
outputStat(Int fileid, int line, int type, BOOL exec, BOOL visited)
{ // Explicitly skip these two cases, as they are often specially handled // and also aren't really interesting statements (something else will // be executed whenever they are). if (type == EXPR_TRUE || type == EXPR_FALSE) { return;
}
CheckLeaveFunctionsAfterLongjmp();
// Catch the case we arrive here and profiling is already disabled if (profileState.status != Profile_Active) { return;
}
outputFilenameIdIfRequired(fileid);
// Statement not attached to a file if (fileid == 0) { return;
}
printOutput(fileid, line, exec, visited);
}
staticinlinevoid outputInterpretedStat(int fileid, int line, BOOL exec)
{
CheckLeaveFunctionsAfterLongjmp();
// Catch the case we arrive here and profiling is already disabled if (profileState.status != Profile_Active) { return;
}
outputFilenameIdIfRequired(fileid);
// Statement not attached to a file if (fileid == 0) { return;
}
staticvoid
enableAtStartup(constchar * filename, Int repeats, TickMethod tickMethod)
{ if (profileState.status == Profile_Active) {
Panic("-P or -C can only be passed once\n");
}
profileState.OutputRepeats = repeats;
fopenMaybeCompressed(filename, &profileState); if(!profileState.Stream) {
Panic("Failed to open '%s' for profiling output.\n", filename);
}
// This function is for when GAP is started with -c, and // enables profiling at startup. If anything goes wrong, // we quit straight away. int enableCodeCoverageAtStartup(constchar * argv[], void * dummy)
{
enableAtStartup(argv[0], 0, Tick_Mem); return1;
}
// This function is for when GAP is started with -P, and // enables profiling at startup. If anything goes wrong, // we quit straight away. int enableProfilingAtStartup(constchar * argv[], void * dummy)
{
TickMethod tickMethod = Tick_WallTime; #ifdef HAVE_GETTIMEOFDAY
tickMethod = Tick_WallTime; #else #ifdef HAVE_GETRUSAGE
tickMethod = Tick_CPUTime; #endif #endif
enableAtStartup(argv[0], 1, tickMethod); return1;
}
static Obj FuncACTIVATE_PROFILING(Obj self,
Obj filename, // filename to write to
Obj coverage,
Obj wallTime,
Obj recordMem,
Obj resolution)
{ if (profileState.status != Profile_Disabled) { return Fail;
}
if(profileState.profiledPreviously &&
coverage == True) {
ErrorMayQuit("Code coverage can only be started once per" " GAP session. Please exit GAP and restart. Sorry.",0,0);
}
if(coverage != True && coverage != False) {
ErrorMayQuit("<coverage> must be a boolean",0,0);
}
if(wallTime != True && wallTime != False) {
ErrorMayQuit("<wallTime> must be a boolean",0,0);
}
#ifndef HAVE_GETTIMEOFDAY if(wallTime == True) {
ErrorMayQuit("This OS does not support wall-clock based timing",0,0);
} #endif #ifndef HAVE_GETRUSAGE if(wallTime == False) {
ErrorMayQuit("This OS does not support CPU based timing",0,0);
} #endif
staticInt PostRestore ( StructInitInfo * module )
{ /* When we restore a workspace, we start a new profile. *'OutputtedFilenameList'istheonlypartoftheprofilewhichis *storedintheGAPmemoryspace,soweneedtoclearitincase *itstillhasavaluefromapreviousprofile.
*/
OutputtedFilenameList = NEW_PLIST(T_PLIST, 0); return0;
}
/**************************************************************************** ** *FInitInfoStats().................tableofinitfunctions
*/ static StructInitInfo module = { // init struct using C99 designated initializers; for a full list of // fields, please refer to the definition of StructInitInfo
.type = MODULE_BUILTIN,
.name = "profile",
.initKernel = InitKernel,
.initLibrary = InitLibrary,
.postRestore = PostRestore
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.