diff --git a/tools/Runtime Condenser/Main.cpp b/tools/Runtime Condenser/Main.cpp index b1ba3546372..f5410bd7981 100644 --- a/tools/Runtime Condenser/Main.cpp +++ b/tools/Runtime Condenser/Main.cpp @@ -1,4 +1,5 @@ /* Runtime Condenser by Nodrak + * Cleaned up and refactored by MrStonedOne * This will sum up identical runtimes into one, giving a total of how many times it occured. The first occurance * of the runtime will log the proc, source, usr and src, the rest will just add to the total. Infinite loops will * also be caught and displayed (if any) above the list of runtimes. @@ -7,238 +8,230 @@ * 1) Copy and paste your list of runtimes from Dream Daemon into input.exe * 2) Run RuntimeCondenser.exe * 3) Open output.txt for a condensed report of the runtimes + * + * How to compile: + * Requires visual c++ compiler 2012 or any linux compiler with c++11 support. + * Windows: + * Normal: cl.exe /EHsc /Ox /Qpar Main.cpp + * Debug: cl.exe /EHsc /Zi Main.cpp + * Linux: + * Normal: g++ -O3 -std=c++11 Main.cpp -o rc + * Debug: g++ -g -Og -std=c++11 Main.cpp -o rc + * Any Compile errors most likely indicate lack of c++11 support. Google how to upgrade or nag coderbus for help.. */ #include #include #include #include +#include +#include +#include using namespace std; -//Make all of these global. It's bad yes, but it's a small program so it really doesn't affect anything. - //Because hardcoded numbers are bad :( - const unsigned short maxStorage = 99; //100 - 1 +struct runtime { + string text; + string proc; + string source; + string usr; + string src; + string loc; + unsigned int count; +}; +struct harddel { + string type; + unsigned int count; +}; +//What we use to read input +string lastLine = ""; +string currentLine = ""; +string nextLine = ""; - //What we use to read input - string currentLine = "Blank"; - string nextLine = "Blank"; +//Stores lines we want to keep to print out +unordered_map storedRuntime; +unordered_map storedInfiniteLoop; +unordered_map storedHardDel; - //Stores lines we want to keep to print out - string storedRuntime[maxStorage+1]; - string storedProc[maxStorage+1]; - string storedSource[maxStorage+1]; - string storedUsr[maxStorage+1]; - string storedSrc[maxStorage+1]; - - string storedHardDel[maxStorage+1]; +//Stat tracking stuff for output +unsigned int totalRuntimes = 0; +unsigned int totalUniqueRuntimes = 0; +unsigned int totalInfiniteLoops = 0; +unsigned int totalUniqueInfiniteLoops = 0; +unsigned int totalHardDels = 0; +unsigned int totalUniqueHardDels = 0; - //Stat tracking stuff for output - unsigned int totalRuntimes = 0; - unsigned int totalUniqueRuntimes = 0; - unsigned int totalInfiniteLoops = 0; - unsigned int totalUniqueInfiniteLoops = 0; - unsigned int totalHardDels = 0; - unsigned int totalUniqueHardDels = 0; +//Misc +runtime* currentRuntime = NULL; //for storing the current runtime when we want to read the extra info on the next lines. - //Misc - unsigned int numRuntime[maxStorage+1]; //Number of times a specific runtime has occured - unsigned int numHardDel[maxStorage+1]; //Number of times a specific hard del has occured - bool checkNextLines = false; //Used in case byond has condensed a large number of similar runtimes - int storedIterator = 0; //Used to remember where we stored the runtime - -bool readFromFile() -{ +//like substr, but returns an empty string if the string is smaller then start, rather then an exception. +inline string safe_substr(string S, size_t start = 0, size_t end = string::npos) { + if (start > S.length()) + start = S.length(); + return S.substr(start, end); +} +inline void forward_progress(ifstream &inputFile) { + lastLine = currentLine; + currentLine = nextLine; + getline(inputFile, nextLine); + //strip out any timestamps. + if (nextLine.length() >= 10) + if (nextLine[0] == '[' && nextLine[3] == ':' && nextLine[6] == ':' && nextLine[9] == ']') + nextLine = nextLine.substr(10); +} +bool readFromFile() { //Open file to read - ifstream inputFile("input.txt"); - - if(inputFile.is_open()) - { - while(!inputFile.eof()) //Until end of file - { - //If we've run out of storage - if(storedRuntime[maxStorage] != "Blank") break; + ifstream inputFile("Input.txt"); + if (inputFile.is_open()) { + while (!inputFile.eof()) { //Until end of file //Update our lines - currentLine = nextLine; - getline(inputFile, nextLine); + forward_progress(inputFile); //After finding a new runtime, check to see if there are extra values to store - if(checkNextLines) - { + if (currentRuntime) { //Skip ahead - currentLine = nextLine; - getline(inputFile, nextLine); + forward_progress(inputFile); //If we find this, we have new stuff to store - if(nextLine.find("usr:") != std::string::npos) - { + if (safe_substr(nextLine, 2, 4) == "usr:") { + //Skip ahead + forward_progress(inputFile); + //Store more info - storedSource[storedIterator] = currentLine; - storedUsr[storedIterator] = nextLine; - + currentRuntime->source = lastLine; + currentRuntime->usr = currentLine; + currentRuntime->src = nextLine; + //Skip ahead again - currentLine = nextLine; - getline(inputFile, nextLine); + forward_progress(inputFile); - //Store the last of the info - storedSrc[storedIterator] = nextLine; + if (safe_substr(nextLine, 2, 8) == "src.loc:") + currentRuntime->loc = nextLine; + } - checkNextLines = false; + currentRuntime = NULL; } //Found an infinite loop! - if(currentLine.find("Infinite loop suspected") != std::string::npos || currentLine.find("Maximum recursion level reached") != std::string::npos) - { + if (safe_substr(currentLine, 0, 23) == "Infinite loop suspected" || safe_substr(currentLine, 0, 31) == "Maximum recursion level reached") { totalInfiniteLoops++; - - for(int i=0; i <= maxStorage; i++) - { - //We've already encountered this - if(currentLine == storedRuntime[i]) - { - numRuntime[i]++; - break; - } - - //We've never encountered this - if(storedRuntime[i] == "Blank") - { - storedRuntime[i] = currentLine; - currentLine = nextLine; - getline(inputFile, nextLine); //Skip the "if this is not an infinite loop" line - storedProc[i] = nextLine; - numRuntime[i] = 1; - checkNextLines = true; - storedIterator = i; - totalUniqueInfiniteLoops++; - break; - } + runtime* R = storedInfiniteLoop[currentLine]; + if (!R || R->text != currentLine) { + R = new runtime; + storedInfiniteLoop[currentLine] = R; + R->text = currentLine; + forward_progress(inputFile); + R->proc = nextLine; + R->count = 1; + currentRuntime = R; + totalUniqueInfiniteLoops++; + } else { //existed already + R->count++; } + } //Found a runtime! - else if(currentLine.find("runtime error:") != std::string::npos) - { + else if (safe_substr(currentLine, 0, 14) == "runtime error:") { + if (currentLine.length() <= 17) { //empty runtime, check next line. + if (nextLine.length() < 2) //runtime is on the line before this one. (byond bug) + nextLine = lastLine; + forward_progress(inputFile); + currentLine = "runtime error: " + currentLine; + } totalRuntimes++; - for(int i=0; i <= maxStorage; i++) - { - //We've already encountered this - if(currentLine == storedRuntime[i]) - { - numRuntime[i]++; - break; - } - - //We've never encountered this - if(storedRuntime[i] == "Blank") - { - storedRuntime[i] = currentLine; - storedProc[i] = nextLine; - numRuntime[i] = 1; - checkNextLines = true; - storedIterator = i; - totalUniqueRuntimes++; - break; - } + runtime* R = storedRuntime[currentLine]; + if (!R || R->text != currentLine) { + R = new runtime; + storedRuntime[currentLine] = R; + R->text = currentLine; + R->proc = nextLine; + R->count = 1; + currentRuntime = R; + totalUniqueRuntimes++; + } else { //existed already + R->count++; } } //Found a hard del! - else if(currentLine.find("Path :") != std::string::npos) - { - //this is pretty ugly but the alternative was implementing regex which I haven't the slightest idea how to do - //it takes advantage of the formatting of the line to extract the amount of failures - std::string tmp; - char c; - int failures; - std::stringstream ss(nextLine); - ss >> tmp >> c >> failures; + else if (safe_substr(currentLine, 0, 7) == "Path : ") { + string deltype = safe_substr(currentLine, 7); + if (deltype.substr(deltype.size()-1,1) == " ") //some times they have a single trailing space. + deltype = deltype.substr(0, deltype.size()-1); + + unsigned int failures = strtoul(safe_substr(nextLine, 11).c_str(), NULL, 10); + if (failures <= 0) + continue; totalHardDels += failures; - - for(int i=0; i <= maxStorage; i++) - { - - //We've already encountered this - if(currentLine == storedHardDel[i]) - { - numHardDel[i] += failures; - break; - } - - //We've never encountered this - if(storedHardDel[i] == "Blank") - { - storedHardDel[i] = currentLine; - numHardDel[i] = failures; - checkNextLines = true; - totalUniqueHardDels++; - break; - } + harddel * D = storedHardDel[deltype]; + if (!D || D->type != deltype) { + D = new harddel; + storedHardDel[deltype] = D; + D->type = deltype; + D->count = failures; + totalUniqueHardDels++; + } else { + D->count += failures; } } } - } - else - { + } else { return false; } return true; } +bool runtimeComp(const runtime* a, const runtime* b) { + return a->count > b->count; +} -bool writeToFile() -{ +bool hardDelComp(const harddel* a, const harddel* b) { + return a->count > b->count; +} +bool writeToFile() { //Open and clear the file ofstream outputFile("Output.txt", ios::trunc); - if(outputFile.is_open()) - { + if(outputFile.is_open()) { outputFile << "Note: The proc name, source file, src and usr are all from the FIRST of the identical runtimes. Everything else is cropped.\n\n"; if(totalUniqueInfiniteLoops > 0) - { outputFile << "Total unique infinite loops: " << totalUniqueInfiniteLoops << endl; - } - if(totalInfiniteLoops > 0) - { + + if(totalInfiniteLoops > 0) outputFile << "Total infinite loops: " << totalInfiniteLoops << endl << endl; - } + outputFile << "Total unique runtimes: " << totalUniqueRuntimes << endl; outputFile << "Total runtimes: " << totalRuntimes << endl << endl; if(totalUniqueHardDels > 0) - { outputFile << "Total unique hard deletions: " << totalUniqueHardDels << endl; - } - if(totalHardDels > 0) - { - outputFile << "Total hard deletions: " << totalHardDels << endl << endl; - } - //Display a warning if we've hit the maximum space we've allocated for storage - if(totalUniqueRuntimes + totalUniqueInfiniteLoops >= maxStorage) - { - outputFile << "Warning: The maximum number of unique runtimes has been hit. If there were more, they have been cropped out.\n\n"; - } - if(totalUniqueHardDels >= maxStorage) - { - outputFile << "Warning: The maximum number of unique hard deletions has been hit. If there were more, they have been croped out.\n\n"; - } + if(totalHardDels > 0) + outputFile << "Total hard deletions: " << totalHardDels << endl << endl; //If we have infinite loops, display them first. - if(totalInfiniteLoops > 0) - { + if(totalInfiniteLoops > 0) { + vector infiniteLoops; + infiniteLoops.reserve(storedInfiniteLoop.size()); + for (unordered_map::iterator it=storedInfiniteLoop.begin(); it != storedInfiniteLoop.end(); it++) + infiniteLoops.push_back(it->second); + sort(infiniteLoops.begin(), infiniteLoops.end(), runtimeComp); outputFile << "** Infinite loops **"; - for(int i=0; i <= maxStorage; i++) - { - if(storedRuntime[i].find("Infinite loop suspected") != std::string::npos || storedRuntime[i].find("Maximum recursion level reached") != std::string::npos) - { - if(numRuntime[i] != 0) outputFile << endl << endl << "The following infinite loop has occured " << numRuntime[i] << " time(s).\n"; - if(storedRuntime[i] != "Blank") outputFile << storedRuntime[i] << endl; - if(storedProc[i] != "Blank") outputFile << storedProc[i] << endl; - if(storedSource[i] != "Blank") outputFile << storedSource[i] << endl; - if(storedUsr[i] != "Blank") outputFile << storedUsr[i] << endl; - if(storedSrc[i] != "Blank") outputFile << storedSrc[i] << endl; - } + for (int i=0; i < infiniteLoops.size(); i++) { + runtime* R = infiniteLoops[i]; + outputFile << endl << endl << "The following infinite loop has occurred " << R->count << " time(s).\n"; + outputFile << R->text << endl; + if(R->proc.length()) + outputFile << R->proc << endl; + if(R->source.length()) + outputFile << R->source << endl; + if(R->usr.length()) + outputFile << R->usr << endl; + if(R->src.length()) + outputFile << R->src << endl; + if(R->loc.length()) + outputFile << R->loc << endl; } outputFile << endl << endl; //For spacing } @@ -246,170 +239,68 @@ bool writeToFile() //Do runtimes next outputFile << "** Runtimes **"; - for(int i=0; i <= maxStorage; i++) - { - if(storedRuntime[i].find("Infinite loop suspected") != std::string::npos || storedRuntime[i].find("Maximum recursion level reached") != std::string::npos) continue; - - if(numRuntime[i] != 0) outputFile << endl << endl << "The following runtime has occured " << numRuntime[i] << " time(s).\n"; - if(storedRuntime[i] != "Blank") outputFile << storedRuntime[i] << endl; - if(storedProc[i] != "Blank") outputFile << storedProc[i] << endl; - if(storedSource[i] != "Blank") outputFile << storedSource[i] << endl; - if(storedUsr[i] != "Blank") outputFile << storedUsr[i] << endl; - if(storedSrc[i] != "Blank") outputFile << storedSrc[i] << endl; + vector runtimes; + runtimes.reserve(storedRuntime.size()); + for (unordered_map::iterator it=storedRuntime.begin(); it != storedRuntime.end(); it++) + runtimes.push_back(it->second); + sort(runtimes.begin(), runtimes.end(), runtimeComp); + for (int i=0; i < runtimes.size(); i++) { + runtime* R = runtimes[i]; + outputFile << endl << endl << "The following runtime has occurred " << R->count << " time(s).\n"; + outputFile << R->text << endl; + if(R->proc.length()) + outputFile << R->proc << endl; + if(R->source.length()) + outputFile << R->source << endl; + if(R->usr.length()) + outputFile << R->usr << endl; + if(R->src.length()) + outputFile << R->src << endl; + if(R->loc.length()) + outputFile << R->loc << endl; } + outputFile << endl << endl; //For spacing //and finally, hard deletes - if(totalHardDels > 0) - { + if(totalHardDels > 0) { outputFile << endl << "** Hard deletions **"; - for(int i=0; i <= maxStorage; i++) - { - if(numHardDel[i] != 0 && storedHardDel[i] != "Blank") outputFile << endl << storedHardDel[i] << " - " << numHardDel[i] << " time(s).\n"; + vector hardDels; + hardDels.reserve(storedHardDel.size()); + for (unordered_map::iterator it=storedHardDel.begin(); it != storedHardDel.end(); it++) + hardDels.push_back(it->second); + sort(hardDels.begin(), hardDels.end(), hardDelComp); + for(int i=0; i < hardDels.size(); i++) { + harddel* D = hardDels[i]; + outputFile << endl << D->type << " - " << D->count << " time(s).\n"; } } outputFile.close(); - } - else - { + } else { return false; } return true; } -void sortRuntimes() -{ - string tempRuntime[maxStorage+1]; - string tempProc[maxStorage+1]; - string tempSource[maxStorage+1]; - string tempUsr[maxStorage+1]; - string tempSrc[maxStorage+1]; - unsigned int tempNumRuntime[maxStorage+1]; - unsigned int highestCount = 0; //Used for descending order -// int keepLooping = 0; - - //Move all of our data into temporary arrays. Also clear the stored data (not necessary but.. just incase) - for(int i=0; i <= maxStorage; i++) - { - //Get the largest occurance of a single runtime - if(highestCount < numRuntime[i]) - { - highestCount = numRuntime[i]; - } - - tempRuntime[i] = storedRuntime[i]; storedRuntime[i] = "Blank"; - tempProc[i] = storedProc[i]; storedProc[i] = "Blank"; - tempSource[i] = storedSource[i]; storedSource[i] = "Blank"; - tempUsr[i] = storedUsr[i]; storedUsr[i] = "Blank"; - tempSrc[i] = storedSrc[i]; storedSrc[i] = "Blank"; - tempNumRuntime[i] = numRuntime[i]; numRuntime[i] = 0; - } - - while(highestCount > 0) - { - for(int i=0; i <= maxStorage; i++) //For every runtime - { - if(tempNumRuntime[i] == highestCount) //If the number of occurances of that runtime is equal to our current highest - { - for(int j=0; j <= maxStorage; j++) //Find the next available slot and store the info - { - if(storedRuntime[j] == "Blank") //Found an empty spot - { - storedRuntime[j] = tempRuntime[i]; - storedProc[j] = tempProc[i]; - storedSource[j] = tempSource[i]; - storedUsr[j] = tempUsr[i]; - storedSrc[j] = tempSrc[i]; - numRuntime[j] = tempNumRuntime[i]; - break; - } - } - } - } - highestCount--; //Lower our 'highest' by one and continue - } -} - -void sortHardDels() //copypasting and I don't care~ -{ - string tempHardDel[maxStorage + 1]; - unsigned int tempNumHardDel[maxStorage + 1]; - unsigned int highestCount = 0; - - for (int i = 0; i <= maxStorage; i++) - { - //Get the largest occurance of a single hard deletion - if (highestCount < numHardDel[i]) - { - highestCount = numHardDel[i]; - } - - tempHardDel[i] = storedHardDel[i]; storedHardDel[i] = "Blank"; - tempNumHardDel[i] = numHardDel[i]; numHardDel[i] = 0; - } - - while (highestCount > 0) - { - for (int i = 0; i <= maxStorage; i++) //For every hard deletion - { - if (tempNumHardDel[i] == highestCount) //If the number of occurances of that hard deletion is equal to our current highest - { - for (int j = 0; j <= maxStorage; j++) //Find the next available slot and store the info - { - if (storedHardDel[j] == "Blank") //Found an empty spot - { - storedHardDel[j] = tempHardDel[i]; - numHardDel[j] = tempNumHardDel[i]; - break; - } - } - } - } - highestCount--; //Lower our 'highest' by one and continue - } -} - - int main() { - char exit; //Used to stop the program from immediatly exiting + char exit; //Used to stop the program from immediately exiting - //Start everything fresh. "Blank" should never occur in the runtime logs on its own. - for(int i=0; i <= maxStorage; i++) - { - storedRuntime[i] = "Blank"; - storedProc[i] = "Blank"; - storedSource[i] = "Blank"; - storedUsr[i] = "Blank"; - storedSrc[i] = "Blank"; - numRuntime[i] = 0; - storedHardDel[i] = "Blank"; - numHardDel[i] = 0; - - } - - if(readFromFile()) - { + if(readFromFile()) { cout << "Input read successfully!\n"; - } - else - { + } else { cout << "Input failed to open, shutting down.\n"; cout << "\nEnter any letter to quit.\n"; cin >> exit; return 1; } - sortRuntimes(); - sortHardDels(); - if(writeToFile()) - { + + if(writeToFile()) { cout << "Output was successful!\n"; cout << "\nEnter any letter to quit.\n"; cin >> exit; return 0; - } - else - { + } else { cout << "The output file could not be opened, shutting down.\n"; cout << "\nEnter any letter to quit.\n"; cin >> exit; diff --git a/tools/Runtime Condenser/RuntimeCondenser.exe b/tools/Runtime Condenser/RuntimeCondenser.exe index 9a5e4f26248..f32fe9d6ece 100644 Binary files a/tools/Runtime Condenser/RuntimeCondenser.exe and b/tools/Runtime Condenser/RuntimeCondenser.exe differ diff --git a/tools/Runtime Condenser/condenser.py b/tools/Runtime Condenser/condenser.py deleted file mode 100644 index c21328a365b..00000000000 --- a/tools/Runtime Condenser/condenser.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 -# Runtime Condenser, rewritten in python by tkdrg -# Only the first set of proc, file, usr and src will be kept. The rest is truncated. -# This script is valid on both python2 and python3 -runtimes = dict() -infloops = dict() -harddels = dict() - - -def readFromFile(target='input.txt'): - global runtimes, infloops, harddels - f = open(target) - - for line in iter(f.readline, ''): - if 'Infinite loop suspected' in line or 'Maximum recursion level reached' in line: - f.readline() # Throw away the 'If it is not an infinite loop' line - line = f.readline() - try: - i = infloops[line] - i['num'] += 1 - except KeyError: - infloops[line] = {'file': f.readline(), 'usr': f.readline(), 'src': f.readline(), 'num': 1} - - elif 'runtime error:' in line: - try: - i = runtimes[line] - i['num'] += 1 - except KeyError: - runtimes[line] = {'proc': f.readline(), 'file': f.readline(), 'usr': f.readline(), 'src': f.readline(), 'num': 1} - - elif 'Path :' in line: - try: - i = harddels[line] - i['num'] += int(f.readline().split()[2]) - except KeyError: - harddels[line] = {'num': int(f.readline().split()[2])} - - -def writeToFile(target='output.txt'): - global runtimes, infloops, harddels - f = open(target, 'w') - - f.write('Note: The proc name, file, and usr are all from the FIRST of the identical runtimes. Everything else is cropped.\n\n') - if len(infloops): - f.write('Total unique infinite loops: {}\n'.format(len(infloops))) - f.write('Total infinite loops: {}\n\n'.format(sum((i[1]['num'] for i in infloops)))) - - if len(runtimes): - f.write('Total unique runtimes: {}\n'.format(len(runtimes))) - f.write('Total runtimes: {}\n\n'.format(sum((i[1]['num'] for i in runtimes)))) - - if len(harddels): - f.write('Total unique hard deletions: {}\n'.format(len(harddels))) - f.write('Total hard deletions: {}\n\n'.format(sum((i[1]['num'] for i in harddels)))) - - if len(infloops): - f.write('** Infinite loops **\n') - for t in infloops: - i = t[1] - f.write('The following infinite loop has occured {} time(s).\n'.format(i['num'])) - f.write(''.join([t[0], i['file'], i['usr'], i['src']])) - f.write('\n') - - if len(runtimes): - f.write('** Runtimes **\n') - for t in runtimes: - i = t[1] - f.write('The following runtime has occured {} time(s).\n'.format(i['num'])) - f.write(''.join([t[0], i['proc'], i['file'], i['usr'], i['src']])) - f.write('\n') - - if len(harddels): - f.write('** Hard deletions **\n') - for t in harddels: - i = t[1] - f.write('The following path has failed to GC {} time(s).\n'.format(i['num'])) - f.write('{}\n'.format(t[0])) - - -if __name__ == '__main__': - readFromFile() - - runtimes = sorted(runtimes.items(), key=lambda x: x[1]['num'], reverse=True) - infloops = sorted(infloops.items(), key=lambda x: x[1]['num'], reverse=True) - harddels = sorted(harddels.items(), key=lambda x: x[1]['num'], reverse=True) - - writeToFile()