Index: third_party/codesighs/maptsvdifftool.c |
=================================================================== |
--- third_party/codesighs/maptsvdifftool.c (revision 0) |
+++ third_party/codesighs/maptsvdifftool.c (revision 0) |
@@ -0,0 +1,1311 @@ |
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
+ * |
+ * ***** BEGIN LICENSE BLOCK ***** |
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License Version |
+ * 1.1 (the "License"); you may not use this file except in compliance with |
+ * the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * Software distributed under the License is distributed on an "AS IS" basis, |
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
+ * for the specific language governing rights and limitations under the |
+ * License. |
+ * |
+ * The Original Code is maptsvdifftool.c code, released |
+ * Oct 3, 2002. |
+ * |
+ * The Initial Developer of the Original Code is |
+ * Netscape Communications Corporation. |
+ * Portions created by the Initial Developer are Copyright (C) 2002 |
+ * the Initial Developer. All Rights Reserved. |
+ * |
+ * Contributor(s): |
+ * Garrett Arch Blythe, 03-October-2002 |
+ * |
+ * Alternatively, the contents of this file may be used under the terms of |
+ * either the GNU General Public License Version 2 or later (the "GPL"), or |
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
+ * in which case the provisions of the GPL or the LGPL are applicable instead |
+ * of those above. If you wish to allow use of your version of this file only |
+ * under the terms of either the GPL or the LGPL, and not to allow others to |
+ * use your version of this file under the terms of the MPL, indicate your |
+ * decision by deleting the provisions above and replace them with the notice |
+ * and other provisions required by the GPL or the LGPL. If you do not delete |
+ * the provisions above, a recipient may use your version of this file under |
+ * the terms of any one of the MPL, the GPL or the LGPL. |
+ * |
+ * ***** END LICENSE BLOCK ***** */ |
+ |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <time.h> |
+#include <ctype.h> |
+ |
+#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); |
+#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) |
+ |
+ |
+typedef struct __struct_Options |
+/* |
+** Options to control how we perform. |
+** |
+** mProgramName Used in help text. |
+** mInput File to read for input. |
+** Default is stdin. |
+** mInputName Name of the file. |
+** mOutput Output file, append. |
+** Default is stdout. |
+** mOutputName Name of the file. |
+** mHelp Whether or not help should be shown. |
+** mSummaryOnly Only output a signle line. |
+** mZeroDrift Output zero drift data. |
+** mNegation Perform negation heuristics on the symbol drifts. |
+*/ |
+{ |
+ const char* mProgramName; |
+ FILE* mInput; |
+ char* mInputName; |
+ FILE* mOutput; |
+ char* mOutputName; |
+ int mHelp; |
+ int mSummaryOnly; |
+ int mZeroDrift; |
+ int mNegation; |
+} |
+Options; |
+ |
+ |
+typedef struct __struct_Switch |
+/* |
+** Command line options. |
+*/ |
+{ |
+ const char* mLongName; |
+ const char* mShortName; |
+ int mHasValue; |
+ const char* mValue; |
+ const char* mDescription; |
+} |
+Switch; |
+ |
+#define DESC_NEWLINE "\n\t\t" |
+ |
+static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; |
+static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; |
+static Switch gSummarySwitch = {"--summary", "-s", 0, NULL, "Only output a single line." DESC_NEWLINE "The cumulative size changes." DESC_NEWLINE "Overrides all other output options."}; |
+static Switch gZeroDriftSwitch = {"--zerodrift", "-z", 0, NULL, "Output zero drift data." DESC_NEWLINE "Reports symbol changes even when there is no net drift."}; |
+static Switch gNegationSwitch = {"--negation", "-n", 0, NULL, "Use negation heuristics." DESC_NEWLINE "When symbol sizes are inferred by offset, order changes cause noise." DESC_NEWLINE "This helps see through the noise by eliminating equal and opposite drifts."}; |
+static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; |
+ |
+static Switch* gSwitches[] = { |
+ &gInputSwitch, |
+ &gOutputSwitch, |
+ &gSummarySwitch, |
+ &gZeroDriftSwitch, |
+ &gNegationSwitch, |
+ &gHelpSwitch |
+}; |
+ |
+ |
+typedef struct __struct_SizeComposition |
+/* |
+** Used to keep which parts positive and negative resulted in the total. |
+*/ |
+{ |
+ int mPositive; |
+ int mNegative; |
+} |
+SizeComposition; |
+ |
+ |
+typedef struct __struct_SizeStats |
+/* |
+** Keep track of sizes. |
+** Use signed integers so that negatives are valid, in which case we shrunk. |
+*/ |
+{ |
+ int mCode; |
+ SizeComposition mCodeComposition; |
+ |
+ int mData; |
+ SizeComposition mDataComposition; |
+} |
+SizeStats; |
+ |
+ |
+typedef enum __enum_SegmentClass |
+/* |
+** What type of data a segment holds. |
+*/ |
+{ |
+ CODE, |
+ DATA |
+} |
+SegmentClass; |
+ |
+ |
+typedef struct __struct_SymbolStats |
+/* |
+** Symbol level stats. |
+*/ |
+{ |
+ char* mSymbol; |
+ int mSize; |
+} |
+SymbolStats; |
+ |
+ |
+typedef struct __struct_ObjectStats |
+/* |
+** Object level stats. |
+*/ |
+{ |
+ char* mObject; |
+ int mSize; |
+ SizeComposition mComposition; |
+ SymbolStats* mSymbols; |
+ unsigned mSymbolCount; |
+} |
+ObjectStats; |
+ |
+ |
+typedef struct __struct_SegmentStats |
+/* |
+** Segment level stats. |
+*/ |
+{ |
+ char* mSegment; |
+ SegmentClass mClass; |
+ int mSize; |
+ SizeComposition mComposition; |
+ ObjectStats* mObjects; |
+ unsigned mObjectCount; |
+} |
+SegmentStats; |
+ |
+ |
+typedef struct __struct_ModuleStats |
+/* |
+** Module level stats. |
+*/ |
+{ |
+ char* mModule; |
+ SizeStats mSize; |
+ SegmentStats* mSegments; |
+ unsigned mSegmentCount; |
+} |
+ModuleStats; |
+ |
+ |
+static int moduleCompare(const void* in1, const void* in2) |
+/* |
+** qsort helper. |
+*/ |
+{ |
+ int retval = 0; |
+ |
+ ModuleStats* one = (ModuleStats*)in1; |
+ ModuleStats* two = (ModuleStats*)in2; |
+ |
+ int oneSize = (one->mSize.mCode + one->mSize.mData); |
+ int twoSize = (two->mSize.mCode + two->mSize.mData); |
+ |
+ if(oneSize < twoSize) |
+ { |
+ retval = 1; |
+ } |
+ else if(oneSize > twoSize) |
+ { |
+ retval = -1; |
+ } |
+ else |
+ { |
+ retval = strcmp(one->mModule, two->mModule); |
+ if(0 > oneSize && 0 > twoSize) |
+ { |
+ retval *= -1; |
+ } |
+ } |
+ |
+ return retval; |
+} |
+ |
+ |
+static int segmentCompare(const void* in1, const void* in2) |
+/* |
+** qsort helper. |
+*/ |
+{ |
+ int retval = 0; |
+ |
+ SegmentStats* one = (SegmentStats*)in1; |
+ SegmentStats* two = (SegmentStats*)in2; |
+ |
+ if(one->mSize < two->mSize) |
+ { |
+ retval = 1; |
+ } |
+ else if(one->mSize > two->mSize) |
+ { |
+ retval = -1; |
+ } |
+ else |
+ { |
+ retval = strcmp(one->mSegment, two->mSegment); |
+ if(0 > one->mSize && 0 > two->mSize) |
+ { |
+ retval *= -1; |
+ } |
+ } |
+ |
+ return retval; |
+} |
+ |
+ |
+static int objectCompare(const void* in1, const void* in2) |
+/* |
+** qsort helper. |
+*/ |
+{ |
+ int retval = 0; |
+ |
+ ObjectStats* one = (ObjectStats*)in1; |
+ ObjectStats* two = (ObjectStats*)in2; |
+ |
+ if(one->mSize < two->mSize) |
+ { |
+ retval = 1; |
+ } |
+ else if(one->mSize > two->mSize) |
+ { |
+ retval = -1; |
+ } |
+ else |
+ { |
+ retval = strcmp(one->mObject, two->mObject); |
+ if(0 > one->mSize && 0 > two->mSize) |
+ { |
+ retval *= -1; |
+ } |
+ } |
+ |
+ return retval; |
+} |
+ |
+ |
+static int symbolCompare(const void* in1, const void* in2) |
+/* |
+** qsort helper. |
+*/ |
+{ |
+ int retval = 0; |
+ |
+ SymbolStats* one = (SymbolStats*)in1; |
+ SymbolStats* two = (SymbolStats*)in2; |
+ |
+ if(one->mSize < two->mSize) |
+ { |
+ retval = 1; |
+ } |
+ else if(one->mSize > two->mSize) |
+ { |
+ retval = -1; |
+ } |
+ else |
+ { |
+ retval = strcmp(one->mSymbol, two->mSymbol); |
+ if(0 > one->mSize && 0 > two->mSize) |
+ { |
+ retval *= -1; |
+ } |
+ } |
+ |
+ return retval; |
+} |
+ |
+ |
+void trimWhite(char* inString) |
+/* |
+** Remove any whitespace from the end of the string. |
+*/ |
+{ |
+ int len = strlen(inString); |
+ |
+ while(len) |
+ { |
+ len--; |
+ |
+ if(isspace(*(inString + len))) |
+ { |
+ *(inString + len) = '\0'; |
+ } |
+ else |
+ { |
+ break; |
+ } |
+ } |
+} |
+ |
+ |
+int difftool(Options* inOptions) |
+/* |
+** Read a diff file and spit out relevant information. |
+*/ |
+{ |
+ int retval = 0; |
+ char lineBuffer[0x500]; |
+ SizeStats overall; |
+ ModuleStats* modules = NULL; |
+ unsigned moduleCount = 0; |
+ unsigned moduleLoop = 0; |
+ ModuleStats* theModule = NULL; |
+ unsigned segmentLoop = 0; |
+ SegmentStats* theSegment = NULL; |
+ unsigned objectLoop = 0; |
+ ObjectStats* theObject = NULL; |
+ unsigned symbolLoop = 0; |
+ SymbolStats* theSymbol = NULL; |
+ unsigned allSymbolCount = 0; |
+ |
+ memset(&overall, 0, sizeof(overall)); |
+ |
+ /* |
+ ** Read the entire diff file. |
+ ** We're only interested in lines beginning with < or > |
+ */ |
+ while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) |
+ { |
+ trimWhite(lineBuffer); |
+ |
+ if(('<' == lineBuffer[0] || '>' == lineBuffer[0]) && ' ' == lineBuffer[1]) |
+ { |
+ int additive = 0; |
+ char* theLine = &lineBuffer[2]; |
+ int scanRes = 0; |
+ int size; |
+ char segClass[0x10]; |
+ char scope[0x10]; |
+ char module[0x100]; |
+ char segment[0x40]; |
+ char object[0x100]; |
+ char* symbol = NULL; |
+ |
+ /* |
+ ** Figure out if the line adds or subtracts from something. |
+ */ |
+ if('>' == lineBuffer[0]) |
+ { |
+ additive = __LINE__; |
+ } |
+ |
+ |
+ /* |
+ ** Scan the line for information. |
+ */ |
+ scanRes = sscanf(theLine, |
+ "%x\t%s\t%s\t%s\t%s\t%s\t", |
+ (unsigned*)&size, |
+ segClass, |
+ scope, |
+ module, |
+ segment, |
+ object); |
+ |
+ if(6 == scanRes) |
+ { |
+ SegmentClass segmentClass = DATA; |
+ |
+ symbol = strrchr(theLine, '\t') + 1; |
+ |
+ if(0 == strcmp(segClass, "CODE")) |
+ { |
+ segmentClass = CODE; |
+ } |
+ else if(0 == strcmp(segClass, "DATA")) |
+ { |
+ segmentClass = DATA; |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, segClass, "Unable to determine segment class."); |
+ } |
+ |
+ if(0 == retval) |
+ { |
+ unsigned moduleIndex = 0; |
+ |
+ /* |
+ ** Find, in succession, the following things: |
+ ** the module |
+ ** the segment |
+ ** the object |
+ ** the symbol |
+ ** Failure to find any one of these means to create it. |
+ */ |
+ |
+ for(moduleIndex = 0; moduleIndex < moduleCount; moduleIndex++) |
+ { |
+ if(0 == strcmp(modules[moduleIndex].mModule, module)) |
+ { |
+ break; |
+ } |
+ } |
+ |
+ if(moduleIndex == moduleCount) |
+ { |
+ void* moved = NULL; |
+ |
+ moved = realloc(modules, sizeof(ModuleStats) * (1 + moduleCount)); |
+ if(NULL != moved) |
+ { |
+ modules = (ModuleStats*)moved; |
+ moduleCount++; |
+ memset(modules + moduleIndex, 0, sizeof(ModuleStats)); |
+ |
+ modules[moduleIndex].mModule = strdup(module); |
+ if(NULL == modules[moduleIndex].mModule) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, module, "Unable to duplicate string."); |
+ } |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase module array."); |
+ } |
+ } |
+ |
+ if(0 == retval) |
+ { |
+ unsigned segmentIndex = 0; |
+ theModule = (modules + moduleIndex); |
+ |
+ for(segmentIndex = 0; segmentIndex < theModule->mSegmentCount; segmentIndex++) |
+ { |
+ if(0 == strcmp(segment, theModule->mSegments[segmentIndex].mSegment)) |
+ { |
+ break; |
+ } |
+ } |
+ |
+ if(segmentIndex == theModule->mSegmentCount) |
+ { |
+ void* moved = NULL; |
+ |
+ moved = realloc(theModule->mSegments, sizeof(SegmentStats) * (theModule->mSegmentCount + 1)); |
+ if(NULL != moved) |
+ { |
+ theModule->mSegments = (SegmentStats*)moved; |
+ theModule->mSegmentCount++; |
+ memset(theModule->mSegments + segmentIndex, 0, sizeof(SegmentStats)); |
+ |
+ theModule->mSegments[segmentIndex].mClass = segmentClass; |
+ theModule->mSegments[segmentIndex].mSegment = strdup(segment); |
+ if(NULL == theModule->mSegments[segmentIndex].mSegment) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, segment, "Unable to duplicate string."); |
+ } |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase segment array."); |
+ } |
+ } |
+ |
+ if(0 == retval) |
+ { |
+ unsigned objectIndex = 0; |
+ theSegment = (theModule->mSegments + segmentIndex); |
+ |
+ for(objectIndex = 0; objectIndex < theSegment->mObjectCount; objectIndex++) |
+ { |
+ if(0 == strcmp(object, theSegment->mObjects[objectIndex].mObject)) |
+ { |
+ break; |
+ } |
+ } |
+ |
+ if(objectIndex == theSegment->mObjectCount) |
+ { |
+ void* moved = NULL; |
+ |
+ moved = realloc(theSegment->mObjects, sizeof(ObjectStats) * (1 + theSegment->mObjectCount)); |
+ if(NULL != moved) |
+ { |
+ theSegment->mObjects = (ObjectStats*)moved; |
+ theSegment->mObjectCount++; |
+ memset(theSegment->mObjects + objectIndex, 0, sizeof(ObjectStats)); |
+ |
+ theSegment->mObjects[objectIndex].mObject = strdup(object); |
+ if(NULL == theSegment->mObjects[objectIndex].mObject) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, object, "Unable to duplicate string."); |
+ } |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase object array."); |
+ } |
+ } |
+ |
+ if(0 == retval) |
+ { |
+ unsigned symbolIndex = 0; |
+ theObject = (theSegment->mObjects + objectIndex); |
+ |
+ for(symbolIndex = 0; symbolIndex < theObject->mSymbolCount; symbolIndex++) |
+ { |
+ if(0 == strcmp(symbol, theObject->mSymbols[symbolIndex].mSymbol)) |
+ { |
+ break; |
+ } |
+ } |
+ |
+ if(symbolIndex == theObject->mSymbolCount) |
+ { |
+ void* moved = NULL; |
+ |
+ moved = realloc(theObject->mSymbols, sizeof(SymbolStats) * (1 + theObject->mSymbolCount)); |
+ if(NULL != moved) |
+ { |
+ theObject->mSymbols = (SymbolStats*)moved; |
+ theObject->mSymbolCount++; |
+ allSymbolCount++; |
+ memset(theObject->mSymbols + symbolIndex, 0, sizeof(SymbolStats)); |
+ |
+ theObject->mSymbols[symbolIndex].mSymbol = strdup(symbol); |
+ if(NULL == theObject->mSymbols[symbolIndex].mSymbol) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, symbol, "Unable to duplicate string."); |
+ } |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase symbol array."); |
+ } |
+ } |
+ |
+ if(0 == retval) |
+ { |
+ theSymbol = (theObject->mSymbols + symbolIndex); |
+ |
+ /* |
+ ** Update our various totals. |
+ */ |
+ if(additive) |
+ { |
+ if(CODE == segmentClass) |
+ { |
+ overall.mCode += size; |
+ theModule->mSize.mCode += size; |
+ } |
+ else if(DATA == segmentClass) |
+ { |
+ overall.mData += size; |
+ theModule->mSize.mData += size; |
+ } |
+ |
+ theSegment->mSize += size; |
+ theObject->mSize += size; |
+ theSymbol->mSize += size; |
+ } |
+ else |
+ { |
+ if(CODE == segmentClass) |
+ { |
+ overall.mCode -= size; |
+ theModule->mSize.mCode -= size; |
+ } |
+ else if(DATA == segmentClass) |
+ { |
+ overall.mData -= size; |
+ theModule->mSize.mData -= size; |
+ } |
+ |
+ theSegment->mSize -= size; |
+ theObject->mSize -= size; |
+ theSymbol->mSize -= size; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mInputName, "Unable to scan line data."); |
+ } |
+ } |
+ } |
+ |
+ if(0 == retval && 0 != ferror(inOptions->mInput)) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); |
+ } |
+ |
+ /* |
+ ** Next, it is time to perform revisionist history of sorts. |
+ ** If the negation switch is in play, we perfrom the following |
+ ** aggressive steps: |
+ ** |
+ ** For each section, find size changes which have an equal and |
+ ** opposite change, and set them both to zero. |
+ ** However, you can only do this if the number of negating changes |
+ ** is even, as if it is odd, then any one of the many could be |
+ ** at fault for the actual change. |
+ ** |
+ ** This orginally exists to make the win32 codesighs reports more |
+ ** readable/meaningful. |
+ */ |
+ if(0 == retval && 0 != inOptions->mNegation) |
+ { |
+ ObjectStats** objArray = NULL; |
+ SymbolStats** symArray = NULL; |
+ |
+ /* |
+ ** Create arrays big enough to hold all symbols. |
+ ** As well as an array to keep the owning object at the same index. |
+ ** We will keep the object around as we may need to modify the size. |
+ */ |
+ objArray = (ObjectStats**)malloc(allSymbolCount * sizeof(ObjectStats*)); |
+ symArray = (SymbolStats**)malloc(allSymbolCount * sizeof(SymbolStats*)); |
+ if(NULL == objArray || NULL == symArray) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inOptions->mProgramName, "Unable to allocate negation array memory."); |
+ } |
+ else |
+ { |
+ unsigned arrayCount = 0; |
+ unsigned arrayLoop = 0; |
+ |
+ /* |
+ ** Go through and perform the steps on each section/segment. |
+ */ |
+ for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) |
+ { |
+ theModule = modules + moduleLoop; |
+ |
+ for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) |
+ { |
+ theSegment = theModule->mSegments + segmentLoop; |
+ |
+ /* |
+ ** Collect all symbols under this section. |
+ ** The symbols are spread out between all the objects, |
+ ** so keep track of both independently at the |
+ ** same index. |
+ */ |
+ arrayCount = 0; |
+ |
+ for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) |
+ { |
+ theObject = theSegment->mObjects + objectLoop; |
+ |
+ for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) |
+ { |
+ theSymbol = theObject->mSymbols + symbolLoop; |
+ |
+ objArray[arrayCount] = theObject; |
+ symArray[arrayCount] = theSymbol; |
+ arrayCount++; |
+ } |
+ } |
+ |
+ /* |
+ ** Now that we have a list of symbols, go through each |
+ ** and see if there is a chance of negation. |
+ */ |
+ for(arrayLoop = 0; arrayLoop < arrayCount; arrayLoop++) |
+ { |
+ /* |
+ ** If the item is NULL, it was already negated. |
+ ** Don't do this for items with a zero size. |
+ */ |
+ if(NULL != symArray[arrayLoop] && 0 != symArray[arrayLoop]->mSize) |
+ { |
+ unsigned identicalValues = 0; |
+ unsigned oppositeValues = 0; |
+ unsigned lookLoop = 0; |
+ const int lookingFor = symArray[arrayLoop]->mSize; |
+ |
+ /* |
+ ** Count the number of items with this value. |
+ ** Count the number of items with the opposite equal value. |
+ ** If they are equal, go through and negate all sizes. |
+ */ |
+ for(lookLoop = arrayLoop; lookLoop < arrayCount; lookLoop++) |
+ { |
+ /* |
+ ** Skip negated items. |
+ ** Skip zero length items. |
+ */ |
+ if(NULL == symArray[lookLoop] || 0 == symArray[lookLoop]->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ if(lookingFor == symArray[lookLoop]->mSize) |
+ { |
+ identicalValues++; |
+ } |
+ else if((-1 * lookingFor) == symArray[lookLoop]->mSize) |
+ { |
+ oppositeValues++; |
+ } |
+ } |
+ |
+ if(0 != identicalValues && identicalValues == oppositeValues) |
+ { |
+ unsigned negationLoop = 0; |
+ |
+ for(negationLoop = arrayLoop; 0 != identicalValues || 0 != oppositeValues; negationLoop++) |
+ { |
+ /* |
+ ** Skip negated items. |
+ ** Skip zero length items. |
+ */ |
+ if(NULL == symArray[negationLoop] || 0 == symArray[negationLoop]->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ /* |
+ ** Negate any size matches. |
+ ** Reflect the change in the object as well. |
+ ** Clear the symbol. |
+ */ |
+ if(lookingFor == symArray[negationLoop]->mSize) |
+ { |
+ objArray[negationLoop]->mSize -= lookingFor; |
+ symArray[negationLoop]->mSize = 0; |
+ symArray[negationLoop] = NULL; |
+ |
+ identicalValues--; |
+ } |
+ else if((-1 * lookingFor) == symArray[negationLoop]->mSize) |
+ { |
+ objArray[negationLoop]->mSize += lookingFor; |
+ symArray[negationLoop]->mSize = 0; |
+ symArray[negationLoop] = NULL; |
+ |
+ oppositeValues--; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ CLEANUP(objArray); |
+ CLEANUP(symArray); |
+ } |
+ |
+ |
+ /* |
+ ** If all went well, time to report. |
+ */ |
+ if(0 == retval) |
+ { |
+ /* |
+ ** Loop through our data once more, so that the symbols can |
+ ** propigate their changes upwards in a positive/negative |
+ ** fashion. |
+ ** This will help give the composite change more meaning. |
+ */ |
+ for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) |
+ { |
+ theModule = modules + moduleLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) |
+ { |
+ continue; |
+ } |
+ |
+ for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) |
+ { |
+ theSegment = theModule->mSegments + segmentLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) |
+ { |
+ theObject = theSegment->mObjects + objectLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) |
+ { |
+ theSymbol = theObject->mSymbols + symbolLoop; |
+ |
+ /* |
+ ** Propagate the composition all the way to the top. |
+ ** Sizes of zero change are skipped. |
+ */ |
+ if(0 < theSymbol->mSize) |
+ { |
+ theObject->mComposition.mPositive += theSymbol->mSize; |
+ theSegment->mComposition.mPositive += theSymbol->mSize; |
+ if(CODE == theSegment->mClass) |
+ { |
+ overall.mCodeComposition.mPositive += theSymbol->mSize; |
+ theModule->mSize.mCodeComposition.mPositive += theSymbol->mSize; |
+ } |
+ else if(DATA == theSegment->mClass) |
+ { |
+ overall.mDataComposition.mPositive += theSymbol->mSize; |
+ theModule->mSize.mDataComposition.mPositive += theSymbol->mSize; |
+ } |
+ } |
+ else if(0 > theSymbol->mSize) |
+ { |
+ theObject->mComposition.mNegative += theSymbol->mSize; |
+ theSegment->mComposition.mNegative += theSymbol->mSize; |
+ if(CODE == theSegment->mClass) |
+ { |
+ overall.mCodeComposition.mNegative += theSymbol->mSize; |
+ theModule->mSize.mCodeComposition.mNegative += theSymbol->mSize; |
+ } |
+ else if(DATA == theSegment->mClass) |
+ { |
+ overall.mDataComposition.mNegative += theSymbol->mSize; |
+ theModule->mSize.mDataComposition.mNegative += theSymbol->mSize; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ |
+ if(inOptions->mSummaryOnly) |
+ { |
+ fprintf(inOptions->mOutput, "%+d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative); |
+ } |
+ else |
+ { |
+ fprintf(inOptions->mOutput, "Overall Change in Size\n"); |
+ fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative); |
+ fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", overall.mCode, overall.mCodeComposition.mPositive, overall.mCodeComposition.mNegative); |
+ fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", overall.mData, overall.mDataComposition.mPositive, overall.mDataComposition.mNegative); |
+ } |
+ |
+ /* |
+ ** Check what else we should output. |
+ */ |
+ if(0 == inOptions->mSummaryOnly && NULL != modules && moduleCount) |
+ { |
+ const char* segmentType = NULL; |
+ |
+ /* |
+ ** We're going to sort everything. |
+ */ |
+ qsort(modules, moduleCount, sizeof(ModuleStats), moduleCompare); |
+ for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) |
+ { |
+ theModule = modules + moduleLoop; |
+ |
+ qsort(theModule->mSegments, theModule->mSegmentCount, sizeof(SegmentStats), segmentCompare); |
+ |
+ for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) |
+ { |
+ theSegment = theModule->mSegments + segmentLoop; |
+ |
+ qsort(theSegment->mObjects, theSegment->mObjectCount, sizeof(ObjectStats), objectCompare); |
+ |
+ for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) |
+ { |
+ theObject = theSegment->mObjects + objectLoop; |
+ |
+ qsort(theObject->mSymbols, theObject->mSymbolCount, sizeof(SymbolStats), symbolCompare); |
+ } |
+ } |
+ } |
+ |
+ /* |
+ ** Loop through for output. |
+ */ |
+ for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) |
+ { |
+ theModule = modules + moduleLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) |
+ { |
+ continue; |
+ } |
+ |
+ fprintf(inOptions->mOutput, "\n"); |
+ fprintf(inOptions->mOutput, "%s\n", theModule->mModule); |
+ fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode + theModule->mSize.mData, theModule->mSize.mCodeComposition.mPositive + theModule->mSize.mDataComposition.mPositive, theModule->mSize.mCodeComposition.mNegative + theModule->mSize.mDataComposition.mNegative); |
+ fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode, theModule->mSize.mCodeComposition.mPositive, theModule->mSize.mCodeComposition.mNegative); |
+ fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", theModule->mSize.mData, theModule->mSize.mDataComposition.mPositive, theModule->mSize.mDataComposition.mNegative); |
+ |
+ for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) |
+ { |
+ theSegment = theModule->mSegments + segmentLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ if(CODE == theSegment->mClass) |
+ { |
+ segmentType = "CODE"; |
+ } |
+ else if(DATA == theSegment->mClass) |
+ { |
+ segmentType = "DATA"; |
+ } |
+ |
+ fprintf(inOptions->mOutput, "\t%+11d (%+d/%+d)\t%s (%s)\n", theSegment->mSize, theSegment->mComposition.mPositive, theSegment->mComposition.mNegative, theSegment->mSegment, segmentType); |
+ |
+ for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) |
+ { |
+ theObject = theSegment->mObjects + objectLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ fprintf(inOptions->mOutput, "\t\t%+11d (%+d/%+d)\t%s\n", theObject->mSize, theObject->mComposition.mPositive, theObject->mComposition.mNegative, theObject->mObject); |
+ |
+ for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) |
+ { |
+ theSymbol = theObject->mSymbols + symbolLoop; |
+ |
+ /* |
+ ** Skip if there is zero drift, or no net change. |
+ */ |
+ if(0 == inOptions->mZeroDrift && 0 == theSymbol->mSize) |
+ { |
+ continue; |
+ } |
+ |
+ fprintf(inOptions->mOutput, "\t\t\t%+11d\t%s\n", theSymbol->mSize, theSymbol->mSymbol); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ /* |
+ ** Cleanup time. |
+ */ |
+ for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) |
+ { |
+ theModule = modules + moduleLoop; |
+ |
+ for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) |
+ { |
+ theSegment = theModule->mSegments + segmentLoop; |
+ |
+ for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) |
+ { |
+ theObject = theSegment->mObjects + objectLoop; |
+ |
+ for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) |
+ { |
+ theSymbol = theObject->mSymbols + symbolLoop; |
+ |
+ CLEANUP(theSymbol->mSymbol); |
+ } |
+ |
+ CLEANUP(theObject->mSymbols); |
+ CLEANUP(theObject->mObject); |
+ } |
+ |
+ CLEANUP(theSegment->mObjects); |
+ CLEANUP(theSegment->mSegment); |
+ } |
+ |
+ CLEANUP(theModule->mSegments); |
+ CLEANUP(theModule->mModule); |
+ } |
+ CLEANUP(modules); |
+ |
+ return retval; |
+} |
+ |
+ |
+int initOptions(Options* outOptions, int inArgc, char** inArgv) |
+/* |
+** returns int 0 if successful. |
+*/ |
+{ |
+ int retval = 0; |
+ int loop = 0; |
+ int switchLoop = 0; |
+ int match = 0; |
+ const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); |
+ Switch* current = NULL; |
+ |
+ /* |
+ ** Set any defaults. |
+ */ |
+ memset(outOptions, 0, sizeof(Options)); |
+ outOptions->mProgramName = inArgv[0]; |
+ outOptions->mInput = stdin; |
+ outOptions->mInputName = strdup("stdin"); |
+ outOptions->mOutput = stdout; |
+ outOptions->mOutputName = strdup("stdout"); |
+ |
+ if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); |
+ } |
+ |
+ /* |
+ ** Go through and attempt to do the right thing. |
+ */ |
+ for(loop = 1; loop < inArgc && 0 == retval; loop++) |
+ { |
+ match = 0; |
+ current = NULL; |
+ |
+ for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) |
+ { |
+ if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) |
+ { |
+ match = __LINE__; |
+ } |
+ else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) |
+ { |
+ match = __LINE__; |
+ } |
+ |
+ if(match) |
+ { |
+ if(gSwitches[switchLoop]->mHasValue) |
+ { |
+ /* |
+ ** Attempt to absorb next option to fullfill value. |
+ */ |
+ if(loop + 1 < inArgc) |
+ { |
+ loop++; |
+ |
+ current = gSwitches[switchLoop]; |
+ current->mValue = inArgv[loop]; |
+ } |
+ } |
+ else |
+ { |
+ current = gSwitches[switchLoop]; |
+ } |
+ |
+ break; |
+ } |
+ } |
+ |
+ if(0 == match) |
+ { |
+ outOptions->mHelp = __LINE__; |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); |
+ } |
+ else if(NULL == current) |
+ { |
+ outOptions->mHelp = __LINE__; |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); |
+ } |
+ else |
+ { |
+ /* |
+ ** Do something based on address/swtich. |
+ */ |
+ if(current == &gInputSwitch) |
+ { |
+ CLEANUP(outOptions->mInputName); |
+ if(NULL != outOptions->mInput && stdin != outOptions->mInput) |
+ { |
+ fclose(outOptions->mInput); |
+ outOptions->mInput = NULL; |
+ } |
+ |
+ outOptions->mInput = fopen(current->mValue, "r"); |
+ if(NULL == outOptions->mInput) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, current->mValue, "Unable to open input file."); |
+ } |
+ else |
+ { |
+ outOptions->mInputName = strdup(current->mValue); |
+ if(NULL == outOptions->mInputName) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, current->mValue, "Unable to strdup."); |
+ } |
+ } |
+ } |
+ else if(current == &gOutputSwitch) |
+ { |
+ CLEANUP(outOptions->mOutputName); |
+ if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) |
+ { |
+ fclose(outOptions->mOutput); |
+ outOptions->mOutput = NULL; |
+ } |
+ |
+ outOptions->mOutput = fopen(current->mValue, "a"); |
+ if(NULL == outOptions->mOutput) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, current->mValue, "Unable to open output file."); |
+ } |
+ else |
+ { |
+ outOptions->mOutputName = strdup(current->mValue); |
+ if(NULL == outOptions->mOutputName) |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, current->mValue, "Unable to strdup."); |
+ } |
+ } |
+ } |
+ else if(current == &gHelpSwitch) |
+ { |
+ outOptions->mHelp = __LINE__; |
+ } |
+ else if(current == &gSummarySwitch) |
+ { |
+ outOptions->mSummaryOnly = __LINE__; |
+ } |
+ else if(current == &gZeroDriftSwitch) |
+ { |
+ outOptions->mZeroDrift = __LINE__; |
+ } |
+ else if(current == &gNegationSwitch) |
+ { |
+ outOptions->mNegation = __LINE__; |
+ } |
+ else |
+ { |
+ retval = __LINE__; |
+ ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); |
+ } |
+ } |
+ } |
+ |
+ return retval; |
+} |
+ |
+ |
+void cleanOptions(Options* inOptions) |
+/* |
+** Clean up any open handles. |
+*/ |
+{ |
+ CLEANUP(inOptions->mInputName); |
+ if(NULL != inOptions->mInput && stdin != inOptions->mInput) |
+ { |
+ fclose(inOptions->mInput); |
+ } |
+ CLEANUP(inOptions->mOutputName); |
+ if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) |
+ { |
+ fclose(inOptions->mOutput); |
+ } |
+ |
+ memset(inOptions, 0, sizeof(Options)); |
+} |
+ |
+ |
+void showHelp(Options* inOptions) |
+/* |
+** Show some simple help text on usage. |
+*/ |
+{ |
+ int loop = 0; |
+ const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); |
+ const char* valueText = NULL; |
+ |
+ printf("usage:\t%s [arguments]\n", inOptions->mProgramName); |
+ printf("\n"); |
+ printf("arguments:\n"); |
+ |
+ for(loop = 0; loop < switchCount; loop++) |
+ { |
+ if(gSwitches[loop]->mHasValue) |
+ { |
+ valueText = " <value>"; |
+ } |
+ else |
+ { |
+ valueText = ""; |
+ } |
+ |
+ printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); |
+ printf("\t %s%s", gSwitches[loop]->mShortName, valueText); |
+ printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); |
+ } |
+ |
+ printf("This tool takes the diff of two sorted tsv files to form a summary report\n"); |
+ printf("of code and data size changes which is hoped to be human readable.\n"); |
+} |
+ |
+ |
+int main(int inArgc, char** inArgv) |
+{ |
+ int retval = 0; |
+ Options options; |
+ |
+ retval = initOptions(&options, inArgc, inArgv); |
+ if(options.mHelp) |
+ { |
+ showHelp(&options); |
+ } |
+ else if(0 == retval) |
+ { |
+ retval = difftool(&options); |
+ } |
+ |
+ cleanOptions(&options); |
+ return retval; |
+} |
+ |
Property changes on: third_party/codesighs/maptsvdifftool.c |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |