1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * ***** BEGIN LICENSE BLOCK ***** 4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 * 6 * The contents of this file are subject to the Mozilla Public License Version 7 * 1.1 (the "License"); you may not use this file except in compliance with 8 * the License. You may obtain a copy of the License at 9 * http://www.mozilla.org/MPL/ 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 * 16 * The Original Code is maptsvdifftool.c code, released 17 * Oct 3, 2002. 18 * 19 * The Initial Developer of the Original Code is 20 * Netscape Communications Corporation. 21 * Portions created by the Initial Developer are Copyright (C) 2002 22 * the Initial Developer. All Rights Reserved. 23 * 24 * Contributor(s): 25 * Garrett Arch Blythe, 03-October-2002 26 * 27 * Alternatively, the contents of this file may be used under the terms of 28 * either the GNU General Public License Version 2 or later (the "GPL"), or 29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 30 * in which case the provisions of the GPL or the LGPL are applicable instead 31 * of those above. If you wish to allow use of your version of this file only 32 * under the terms of either the GPL or the LGPL, and not to allow others to 33 * use your version of this file under the terms of the MPL, indicate your 34 * decision by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL or the LGPL. If you do not delete 36 * the provisions above, a recipient may use your version of this file under 37 * the terms of any one of the MPL, the GPL or the LGPL. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <ctype.h> 46 47 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); 48 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) 49 50 51 typedef struct __struct_Options 52 /* 53 ** Options to control how we perform. 54 ** 55 ** mProgramName Used in help text. 56 ** mInput File to read for input. 57 ** Default is stdin. 58 ** mInputName Name of the file. 59 ** mOutput Output file, append. 60 ** Default is stdout. 61 ** mOutputName Name of the file. 62 ** mHelp Whether or not help should be shown. 63 ** mSummaryOnly Only output a signle line. 64 ** mZeroDrift Output zero drift data. 65 ** mNegation Perform negation heuristics on the symbol drifts. 66 */ 67 { 68 const char* mProgramName; 69 FILE* mInput; 70 char* mInputName; 71 FILE* mOutput; 72 char* mOutputName; 73 int mHelp; 74 int mSummaryOnly; 75 int mZeroDrift; 76 int mNegation; 77 } 78 Options; 79 80 81 typedef struct __struct_Switch 82 /* 83 ** Command line options. 84 */ 85 { 86 const char* mLongName; 87 const char* mShortName; 88 int mHasValue; 89 const char* mValue; 90 const char* mDescription; 91 } 92 Switch; 93 94 #define DESC_NEWLINE "\n\t\t" 95 96 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; 97 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; 98 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."}; 99 static Switch gZeroDriftSwitch = {"--zerodrift", "-z", 0, NULL, "Output zero drift data." DESC_NEWLINE "Reports symbol changes even when there is no net drift."}; 100 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."}; 101 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; 102 103 static Switch* gSwitches[] = { 104 &gInputSwitch, 105 &gOutputSwitch, 106 &gSummarySwitch, 107 &gZeroDriftSwitch, 108 &gNegationSwitch, 109 &gHelpSwitch 110 }; 111 112 113 typedef struct __struct_SizeComposition 114 /* 115 ** Used to keep which parts positive and negative resulted in the total. 116 */ 117 { 118 int mPositive; 119 int mNegative; 120 } 121 SizeComposition; 122 123 124 typedef struct __struct_SizeStats 125 /* 126 ** Keep track of sizes. 127 ** Use signed integers so that negatives are valid, in which case we shrunk. 128 */ 129 { 130 int mCode; 131 SizeComposition mCodeComposition; 132 133 int mData; 134 SizeComposition mDataComposition; 135 } 136 SizeStats; 137 138 139 typedef enum __enum_SegmentClass 140 /* 141 ** What type of data a segment holds. 142 */ 143 { 144 CODE, 145 DATA 146 } 147 SegmentClass; 148 149 150 typedef struct __struct_SymbolStats 151 /* 152 ** Symbol level stats. 153 */ 154 { 155 char* mSymbol; 156 int mSize; 157 } 158 SymbolStats; 159 160 161 typedef struct __struct_ObjectStats 162 /* 163 ** Object level stats. 164 */ 165 { 166 char* mObject; 167 int mSize; 168 SizeComposition mComposition; 169 SymbolStats* mSymbols; 170 unsigned mSymbolCount; 171 } 172 ObjectStats; 173 174 175 typedef struct __struct_SegmentStats 176 /* 177 ** Segment level stats. 178 */ 179 { 180 char* mSegment; 181 SegmentClass mClass; 182 int mSize; 183 SizeComposition mComposition; 184 ObjectStats* mObjects; 185 unsigned mObjectCount; 186 } 187 SegmentStats; 188 189 190 typedef struct __struct_ModuleStats 191 /* 192 ** Module level stats. 193 */ 194 { 195 char* mModule; 196 SizeStats mSize; 197 SegmentStats* mSegments; 198 unsigned mSegmentCount; 199 } 200 ModuleStats; 201 202 203 static int moduleCompare(const void* in1, const void* in2) 204 /* 205 ** qsort helper. 206 */ 207 { 208 int retval = 0; 209 210 ModuleStats* one = (ModuleStats*)in1; 211 ModuleStats* two = (ModuleStats*)in2; 212 213 int oneSize = (one->mSize.mCode + one->mSize.mData); 214 int twoSize = (two->mSize.mCode + two->mSize.mData); 215 216 if(oneSize < twoSize) 217 { 218 retval = 1; 219 } 220 else if(oneSize > twoSize) 221 { 222 retval = -1; 223 } 224 else 225 { 226 retval = strcmp(one->mModule, two->mModule); 227 if(0 > oneSize && 0 > twoSize) 228 { 229 retval *= -1; 230 } 231 } 232 233 return retval; 234 } 235 236 237 static int segmentCompare(const void* in1, const void* in2) 238 /* 239 ** qsort helper. 240 */ 241 { 242 int retval = 0; 243 244 SegmentStats* one = (SegmentStats*)in1; 245 SegmentStats* two = (SegmentStats*)in2; 246 247 if(one->mSize < two->mSize) 248 { 249 retval = 1; 250 } 251 else if(one->mSize > two->mSize) 252 { 253 retval = -1; 254 } 255 else 256 { 257 retval = strcmp(one->mSegment, two->mSegment); 258 if(0 > one->mSize && 0 > two->mSize) 259 { 260 retval *= -1; 261 } 262 } 263 264 return retval; 265 } 266 267 268 static int objectCompare(const void* in1, const void* in2) 269 /* 270 ** qsort helper. 271 */ 272 { 273 int retval = 0; 274 275 ObjectStats* one = (ObjectStats*)in1; 276 ObjectStats* two = (ObjectStats*)in2; 277 278 if(one->mSize < two->mSize) 279 { 280 retval = 1; 281 } 282 else if(one->mSize > two->mSize) 283 { 284 retval = -1; 285 } 286 else 287 { 288 retval = strcmp(one->mObject, two->mObject); 289 if(0 > one->mSize && 0 > two->mSize) 290 { 291 retval *= -1; 292 } 293 } 294 295 return retval; 296 } 297 298 299 static int symbolCompare(const void* in1, const void* in2) 300 /* 301 ** qsort helper. 302 */ 303 { 304 int retval = 0; 305 306 SymbolStats* one = (SymbolStats*)in1; 307 SymbolStats* two = (SymbolStats*)in2; 308 309 if(one->mSize < two->mSize) 310 { 311 retval = 1; 312 } 313 else if(one->mSize > two->mSize) 314 { 315 retval = -1; 316 } 317 else 318 { 319 retval = strcmp(one->mSymbol, two->mSymbol); 320 if(0 > one->mSize && 0 > two->mSize) 321 { 322 retval *= -1; 323 } 324 } 325 326 return retval; 327 } 328 329 330 void trimWhite(char* inString) 331 /* 332 ** Remove any whitespace from the end of the string. 333 */ 334 { 335 int len = strlen(inString); 336 337 while(len) 338 { 339 len--; 340 341 if(isspace(*(inString + len))) 342 { 343 *(inString + len) = '\0'; 344 } 345 else 346 { 347 break; 348 } 349 } 350 } 351 352 353 int difftool(Options* inOptions) 354 /* 355 ** Read a diff file and spit out relevant information. 356 */ 357 { 358 int retval = 0; 359 char lineBuffer[0x500]; 360 SizeStats overall; 361 ModuleStats* modules = NULL; 362 unsigned moduleCount = 0; 363 unsigned moduleLoop = 0; 364 ModuleStats* theModule = NULL; 365 unsigned segmentLoop = 0; 366 SegmentStats* theSegment = NULL; 367 unsigned objectLoop = 0; 368 ObjectStats* theObject = NULL; 369 unsigned symbolLoop = 0; 370 SymbolStats* theSymbol = NULL; 371 unsigned allSymbolCount = 0; 372 373 memset(&overall, 0, sizeof(overall)); 374 375 /* 376 ** Read the entire diff file. 377 ** We're only interested in lines beginning with < or > 378 */ 379 while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) 380 { 381 trimWhite(lineBuffer); 382 383 if(('<' == lineBuffer[0] || '>' == lineBuffer[0]) && ' ' == lineBuffer[1]) 384 { 385 int additive = 0; 386 char* theLine = &lineBuffer[2]; 387 int scanRes = 0; 388 int size; 389 char segClass[0x10]; 390 char scope[0x10]; 391 char module[0x100]; 392 char segment[0x40]; 393 char object[0x100]; 394 char* symbol = NULL; 395 396 /* 397 ** Figure out if the line adds or subtracts from something. 398 */ 399 if('>' == lineBuffer[0]) 400 { 401 additive = __LINE__; 402 } 403 404 405 /* 406 ** Scan the line for information. 407 */ 408 scanRes = sscanf(theLine, 409 "%x\t%s\t%s\t%s\t%s\t%s\t", 410 (unsigned*)&size, 411 segClass, 412 scope, 413 module, 414 segment, 415 object); 416 417 if(6 == scanRes) 418 { 419 SegmentClass segmentClass = DATA; 420 421 symbol = strrchr(theLine, '\t') + 1; 422 423 if(0 == strcmp(segClass, "CODE")) 424 { 425 segmentClass = CODE; 426 } 427 else if(0 == strcmp(segClass, "DATA")) 428 { 429 segmentClass = DATA; 430 } 431 else 432 { 433 retval = __LINE__; 434 ERROR_REPORT(retval, segClass, "Unable to determine segment class."); 435 } 436 437 if(0 == retval) 438 { 439 unsigned moduleIndex = 0; 440 441 /* 442 ** Find, in succession, the following things: 443 ** the module 444 ** the segment 445 ** the object 446 ** the symbol 447 ** Failure to find any one of these means to create it. 448 */ 449 450 for(moduleIndex = 0; moduleIndex < moduleCount; moduleIndex++) 451 { 452 if(0 == strcmp(modules[moduleIndex].mModule, module)) 453 { 454 break; 455 } 456 } 457 458 if(moduleIndex == moduleCount) 459 { 460 void* moved = NULL; 461 462 moved = realloc(modules, sizeof(ModuleStats) * (1 + moduleCount)); 463 if(NULL != moved) 464 { 465 modules = (ModuleStats*)moved; 466 moduleCount++; 467 memset(modules + moduleIndex, 0, sizeof(ModuleStats)); 468 469 modules[moduleIndex].mModule = strdup(module); 470 if(NULL == modules[moduleIndex].mModule) 471 { 472 retval = __LINE__; 473 ERROR_REPORT(retval, module, "Unable to duplicate string."); 474 } 475 } 476 else 477 { 478 retval = __LINE__; 479 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase module array."); 480 } 481 } 482 483 if(0 == retval) 484 { 485 unsigned segmentIndex = 0; 486 theModule = (modules + moduleIndex); 487 488 for(segmentIndex = 0; segmentIndex < theModule->mSegmentCount; segmentIndex++) 489 { 490 if(0 == strcmp(segment, theModule->mSegments[segmentIndex].mSegment)) 491 { 492 break; 493 } 494 } 495 496 if(segmentIndex == theModule->mSegmentCount) 497 { 498 void* moved = NULL; 499 500 moved = realloc(theModule->mSegments, sizeof(SegmentStats) * (theModule->mSegmentCount + 1)); 501 if(NULL != moved) 502 { 503 theModule->mSegments = (SegmentStats*)moved; 504 theModule->mSegmentCount++; 505 memset(theModule->mSegments + segmentIndex, 0, sizeof(SegmentStats)); 506 507 theModule->mSegments[segmentIndex].mClass = segmentClass; 508 theModule->mSegments[segmentIndex].mSegment = strdup(segment); 509 if(NULL == theModule->mSegments[segmentIndex].mSegment) 510 { 511 retval = __LINE__; 512 ERROR_REPORT(retval, segment, "Unable to duplicate string."); 513 } 514 } 515 else 516 { 517 retval = __LINE__; 518 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase segment array."); 519 } 520 } 521 522 if(0 == retval) 523 { 524 unsigned objectIndex = 0; 525 theSegment = (theModule->mSegments + segmentIndex); 526 527 for(objectIndex = 0; objectIndex < theSegment->mObjectCount; objectIndex++) 528 { 529 if(0 == strcmp(object, theSegment->mObjects[objectIndex].mObject)) 530 { 531 break; 532 } 533 } 534 535 if(objectIndex == theSegment->mObjectCount) 536 { 537 void* moved = NULL; 538 539 moved = realloc(theSegment->mObjects, sizeof(ObjectStats) * (1 + theSegment->mObjectCount)); 540 if(NULL != moved) 541 { 542 theSegment->mObjects = (ObjectStats*)moved; 543 theSegment->mObjectCount++; 544 memset(theSegment->mObjects + objectIndex, 0, sizeof(ObjectStats)); 545 546 theSegment->mObjects[objectIndex].mObject = strdup(object); 547 if(NULL == theSegment->mObjects[objectIndex].mObject) 548 { 549 retval = __LINE__; 550 ERROR_REPORT(retval, object, "Unable to duplicate string."); 551 } 552 } 553 else 554 { 555 retval = __LINE__; 556 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase object array."); 557 } 558 } 559 560 if(0 == retval) 561 { 562 unsigned symbolIndex = 0; 563 theObject = (theSegment->mObjects + objectIndex); 564 565 for(symbolIndex = 0; symbolIndex < theObject->mSymbolCount; symbolIndex++) 566 { 567 if(0 == strcmp(symbol, theObject->mSymbols[symbolIndex].mSymbol)) 568 { 569 break; 570 } 571 } 572 573 if(symbolIndex == theObject->mSymbolCount) 574 { 575 void* moved = NULL; 576 577 moved = realloc(theObject->mSymbols, sizeof(SymbolStats) * (1 + theObject->mSymbolCount)); 578 if(NULL != moved) 579 { 580 theObject->mSymbols = (SymbolStats*)moved; 581 theObject->mSymbolCount++; 582 allSymbolCount++; 583 memset(theObject->mSymbols + symbolIndex, 0, sizeof(SymbolStats)); 584 585 theObject->mSymbols[symbolIndex].mSymbol = strdup(symbol); 586 if(NULL == theObject->mSymbols[symbolIndex].mSymbol) 587 { 588 retval = __LINE__; 589 ERROR_REPORT(retval, symbol, "Unable to duplicate string."); 590 } 591 } 592 else 593 { 594 retval = __LINE__; 595 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase symbol array."); 596 } 597 } 598 599 if(0 == retval) 600 { 601 theSymbol = (theObject->mSymbols + symbolIndex); 602 603 /* 604 ** Update our various totals. 605 */ 606 if(additive) 607 { 608 if(CODE == segmentClass) 609 { 610 overall.mCode += size; 611 theModule->mSize.mCode += size; 612 } 613 else if(DATA == segmentClass) 614 { 615 overall.mData += size; 616 theModule->mSize.mData += size; 617 } 618 619 theSegment->mSize += size; 620 theObject->mSize += size; 621 theSymbol->mSize += size; 622 } 623 else 624 { 625 if(CODE == segmentClass) 626 { 627 overall.mCode -= size; 628 theModule->mSize.mCode -= size; 629 } 630 else if(DATA == segmentClass) 631 { 632 overall.mData -= size; 633 theModule->mSize.mData -= size; 634 } 635 636 theSegment->mSize -= size; 637 theObject->mSize -= size; 638 theSymbol->mSize -= size; 639 } 640 } 641 } 642 } 643 } 644 } 645 } 646 else 647 { 648 retval = __LINE__; 649 ERROR_REPORT(retval, inOptions->mInputName, "Unable to scan line data."); 650 } 651 } 652 } 653 654 if(0 == retval && 0 != ferror(inOptions->mInput)) 655 { 656 retval = __LINE__; 657 ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); 658 } 659 660 /* 661 ** Next, it is time to perform revisionist history of sorts. 662 ** If the negation switch is in play, we perfrom the following 663 ** aggressive steps: 664 ** 665 ** For each section, find size changes which have an equal and 666 ** opposite change, and set them both to zero. 667 ** However, you can only do this if the number of negating changes 668 ** is even, as if it is odd, then any one of the many could be 669 ** at fault for the actual change. 670 ** 671 ** This orginally exists to make the win32 codesighs reports more 672 ** readable/meaningful. 673 */ 674 if(0 == retval && 0 != inOptions->mNegation) 675 { 676 ObjectStats** objArray = NULL; 677 SymbolStats** symArray = NULL; 678 679 /* 680 ** Create arrays big enough to hold all symbols. 681 ** As well as an array to keep the owning object at the same index. 682 ** We will keep the object around as we may need to modify the size. 683 */ 684 objArray = (ObjectStats**)malloc(allSymbolCount * sizeof(ObjectStats*)); 685 symArray = (SymbolStats**)malloc(allSymbolCount * sizeof(SymbolStats*)); 686 if(NULL == objArray || NULL == symArray) 687 { 688 retval = __LINE__; 689 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to allocate negation array memory."); 690 } 691 else 692 { 693 unsigned arrayCount = 0; 694 unsigned arrayLoop = 0; 695 696 /* 697 ** Go through and perform the steps on each section/segment. 698 */ 699 for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) 700 { 701 theModule = modules + moduleLoop; 702 703 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) 704 { 705 theSegment = theModule->mSegments + segmentLoop; 706 707 /* 708 ** Collect all symbols under this section. 709 ** The symbols are spread out between all the objects, 710 ** so keep track of both independently at the 711 ** same index. 712 */ 713 arrayCount = 0; 714 715 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) 716 { 717 theObject = theSegment->mObjects + objectLoop; 718 719 for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) 720 { 721 theSymbol = theObject->mSymbols + symbolLoop; 722 723 objArray[arrayCount] = theObject; 724 symArray[arrayCount] = theSymbol; 725 arrayCount++; 726 } 727 } 728 729 /* 730 ** Now that we have a list of symbols, go through each 731 ** and see if there is a chance of negation. 732 */ 733 for(arrayLoop = 0; arrayLoop < arrayCount; arrayLoop++) 734 { 735 /* 736 ** If the item is NULL, it was already negated. 737 ** Don't do this for items with a zero size. 738 */ 739 if(NULL != symArray[arrayLoop] && 0 != symArray[arrayLoop]->mSize) 740 { 741 unsigned identicalValues = 0; 742 unsigned oppositeValues = 0; 743 unsigned lookLoop = 0; 744 const int lookingFor = symArray[arrayLoop]->mSize; 745 746 /* 747 ** Count the number of items with this value. 748 ** Count the number of items with the opposite equal value. 749 ** If they are equal, go through and negate all sizes. 750 */ 751 for(lookLoop = arrayLoop; lookLoop < arrayCount; lookLoop++) 752 { 753 /* 754 ** Skip negated items. 755 ** Skip zero length items. 756 */ 757 if(NULL == symArray[lookLoop] || 0 == symArray[lookLoop]->mSize) 758 { 759 continue; 760 } 761 762 if(lookingFor == symArray[lookLoop]->mSize) 763 { 764 identicalValues++; 765 } 766 else if((-1 * lookingFor) == symArray[lookLoop]->mSize) 767 { 768 oppositeValues++; 769 } 770 } 771 772 if(0 != identicalValues && identicalValues == oppositeValues) 773 { 774 unsigned negationLoop = 0; 775 776 for(negationLoop = arrayLoop; 0 != identicalValues || 0 != oppositeValues; negationLoop++) 777 { 778 /* 779 ** Skip negated items. 780 ** Skip zero length items. 781 */ 782 if(NULL == symArray[negationLoop] || 0 == symArray[negationLoop]->mSize) 783 { 784 continue; 785 } 786 787 /* 788 ** Negate any size matches. 789 ** Reflect the change in the object as well. 790 ** Clear the symbol. 791 */ 792 if(lookingFor == symArray[negationLoop]->mSize) 793 { 794 objArray[negationLoop]->mSize -= lookingFor; 795 symArray[negationLoop]->mSize = 0; 796 symArray[negationLoop] = NULL; 797 798 identicalValues--; 799 } 800 else if((-1 * lookingFor) == symArray[negationLoop]->mSize) 801 { 802 objArray[negationLoop]->mSize += lookingFor; 803 symArray[negationLoop]->mSize = 0; 804 symArray[negationLoop] = NULL; 805 806 oppositeValues--; 807 } 808 } 809 } 810 } 811 } 812 } 813 } 814 } 815 816 CLEANUP(objArray); 817 CLEANUP(symArray); 818 } 819 820 821 /* 822 ** If all went well, time to report. 823 */ 824 if(0 == retval) 825 { 826 /* 827 ** Loop through our data once more, so that the symbols can 828 ** propigate their changes upwards in a positive/negative 829 ** fashion. 830 ** This will help give the composite change more meaning. 831 */ 832 for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) 833 { 834 theModule = modules + moduleLoop; 835 836 /* 837 ** Skip if there is zero drift, or no net change. 838 */ 839 if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) 840 { 841 continue; 842 } 843 844 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) 845 { 846 theSegment = theModule->mSegments + segmentLoop; 847 848 /* 849 ** Skip if there is zero drift, or no net change. 850 */ 851 if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) 852 { 853 continue; 854 } 855 856 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) 857 { 858 theObject = theSegment->mObjects + objectLoop; 859 860 /* 861 ** Skip if there is zero drift, or no net change. 862 */ 863 if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) 864 { 865 continue; 866 } 867 868 for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) 869 { 870 theSymbol = theObject->mSymbols + symbolLoop; 871 872 /* 873 ** Propagate the composition all the way to the top. 874 ** Sizes of zero change are skipped. 875 */ 876 if(0 < theSymbol->mSize) 877 { 878 theObject->mComposition.mPositive += theSymbol->mSize; 879 theSegment->mComposition.mPositive += theSymbol->mSize; 880 if(CODE == theSegment->mClass) 881 { 882 overall.mCodeComposition.mPositive += theSymbol->mSize; 883 theModule->mSize.mCodeComposition.mPositive += theSymbol->mSize; 884 } 885 else if(DATA == theSegment->mClass) 886 { 887 overall.mDataComposition.mPositive += theSymbol->mSize; 888 theModule->mSize.mDataComposition.mPositive += theSymbol->mSize; 889 } 890 } 891 else if(0 > theSymbol->mSize) 892 { 893 theObject->mComposition.mNegative += theSymbol->mSize; 894 theSegment->mComposition.mNegative += theSymbol->mSize; 895 if(CODE == theSegment->mClass) 896 { 897 overall.mCodeComposition.mNegative += theSymbol->mSize; 898 theModule->mSize.mCodeComposition.mNegative += theSymbol->mSize; 899 } 900 else if(DATA == theSegment->mClass) 901 { 902 overall.mDataComposition.mNegative += theSymbol->mSize; 903 theModule->mSize.mDataComposition.mNegative += theSymbol->mSize; 904 } 905 } 906 } 907 } 908 } 909 } 910 911 912 if(inOptions->mSummaryOnly) 913 { 914 fprintf(inOptions->mOutput, "%+d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative); 915 } 916 else 917 { 918 fprintf(inOptions->mOutput, "Overall Change in Size\n"); 919 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); 920 fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", overall.mCode, overall.mCodeComposition.mPositive, overall.mCodeComposition.mNegative); 921 fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", overall.mData, overall.mDataComposition.mPositive, overall.mDataComposition.mNegative); 922 } 923 924 /* 925 ** Check what else we should output. 926 */ 927 if(0 == inOptions->mSummaryOnly && NULL != modules && moduleCount) 928 { 929 const char* segmentType = NULL; 930 931 /* 932 ** We're going to sort everything. 933 */ 934 qsort(modules, moduleCount, sizeof(ModuleStats), moduleCompare); 935 for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) 936 { 937 theModule = modules + moduleLoop; 938 939 qsort(theModule->mSegments, theModule->mSegmentCount, sizeof(SegmentStats), segmentCompare); 940 941 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) 942 { 943 theSegment = theModule->mSegments + segmentLoop; 944 945 qsort(theSegment->mObjects, theSegment->mObjectCount, sizeof(ObjectStats), objectCompare); 946 947 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) 948 { 949 theObject = theSegment->mObjects + objectLoop; 950 951 qsort(theObject->mSymbols, theObject->mSymbolCount, sizeof(SymbolStats), symbolCompare); 952 } 953 } 954 } 955 956 /* 957 ** Loop through for output. 958 */ 959 for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) 960 { 961 theModule = modules + moduleLoop; 962 963 /* 964 ** Skip if there is zero drift, or no net change. 965 */ 966 if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData)) 967 { 968 continue; 969 } 970 971 fprintf(inOptions->mOutput, "\n"); 972 fprintf(inOptions->mOutput, "%s\n", theModule->mModule); 973 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); 974 fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode, theModule->mSize.mCodeComposition.mPositive, theModule->mSize.mCodeComposition.mNegative); 975 fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", theModule->mSize.mData, theModule->mSize.mDataComposition.mPositive, theModule->mSize.mDataComposition.mNegative); 976 977 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) 978 { 979 theSegment = theModule->mSegments + segmentLoop; 980 981 /* 982 ** Skip if there is zero drift, or no net change. 983 */ 984 if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize) 985 { 986 continue; 987 } 988 989 if(CODE == theSegment->mClass) 990 { 991 segmentType = "CODE"; 992 } 993 else if(DATA == theSegment->mClass) 994 { 995 segmentType = "DATA"; 996 } 997 998 fprintf(inOptions->mOutput, "\t%+11d (%+d/%+d)\t%s (%s)\n", theSegment->mSize, theSegment->mComposition.mPositive, theSegment->mComposition.mNegative, theSegment->mSegment, segmentType); 999 1000 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) 1001 { 1002 theObject = theSegment->mObjects + objectLoop; 1003 1004 /* 1005 ** Skip if there is zero drift, or no net change. 1006 */ 1007 if(0 == inOptions->mZeroDrift && 0 == theObject->mSize) 1008 { 1009 continue; 1010 } 1011 1012 fprintf(inOptions->mOutput, "\t\t%+11d (%+d/%+d)\t%s\n", theObject->mSize, theObject->mComposition.mPositive, theObject->mComposition.mNegative, theObject->mObject); 1013 1014 for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) 1015 { 1016 theSymbol = theObject->mSymbols + symbolLoop; 1017 1018 /* 1019 ** Skip if there is zero drift, or no net change. 1020 */ 1021 if(0 == inOptions->mZeroDrift && 0 == theSymbol->mSize) 1022 { 1023 continue; 1024 } 1025 1026 fprintf(inOptions->mOutput, "\t\t\t%+11d\t%s\n", theSymbol->mSize, theSymbol->mSymbol); 1027 } 1028 } 1029 } 1030 } 1031 } 1032 } 1033 1034 /* 1035 ** Cleanup time. 1036 */ 1037 for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++) 1038 { 1039 theModule = modules + moduleLoop; 1040 1041 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++) 1042 { 1043 theSegment = theModule->mSegments + segmentLoop; 1044 1045 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++) 1046 { 1047 theObject = theSegment->mObjects + objectLoop; 1048 1049 for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++) 1050 { 1051 theSymbol = theObject->mSymbols + symbolLoop; 1052 1053 CLEANUP(theSymbol->mSymbol); 1054 } 1055 1056 CLEANUP(theObject->mSymbols); 1057 CLEANUP(theObject->mObject); 1058 } 1059 1060 CLEANUP(theSegment->mObjects); 1061 CLEANUP(theSegment->mSegment); 1062 } 1063 1064 CLEANUP(theModule->mSegments); 1065 CLEANUP(theModule->mModule); 1066 } 1067 CLEANUP(modules); 1068 1069 return retval; 1070 } 1071 1072 1073 int initOptions(Options* outOptions, int inArgc, char** inArgv) 1074 /* 1075 ** returns int 0 if successful. 1076 */ 1077 { 1078 int retval = 0; 1079 int loop = 0; 1080 int switchLoop = 0; 1081 int match = 0; 1082 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); 1083 Switch* current = NULL; 1084 1085 /* 1086 ** Set any defaults. 1087 */ 1088 memset(outOptions, 0, sizeof(Options)); 1089 outOptions->mProgramName = inArgv[0]; 1090 outOptions->mInput = stdin; 1091 outOptions->mInputName = strdup("stdin"); 1092 outOptions->mOutput = stdout; 1093 outOptions->mOutputName = strdup("stdout"); 1094 1095 if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) 1096 { 1097 retval = __LINE__; 1098 ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); 1099 } 1100 1101 /* 1102 ** Go through and attempt to do the right thing. 1103 */ 1104 for(loop = 1; loop < inArgc && 0 == retval; loop++) 1105 { 1106 match = 0; 1107 current = NULL; 1108 1109 for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) 1110 { 1111 if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) 1112 { 1113 match = __LINE__; 1114 } 1115 else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) 1116 { 1117 match = __LINE__; 1118 } 1119 1120 if(match) 1121 { 1122 if(gSwitches[switchLoop]->mHasValue) 1123 { 1124 /* 1125 ** Attempt to absorb next option to fullfill value. 1126 */ 1127 if(loop + 1 < inArgc) 1128 { 1129 loop++; 1130 1131 current = gSwitches[switchLoop]; 1132 current->mValue = inArgv[loop]; 1133 } 1134 } 1135 else 1136 { 1137 current = gSwitches[switchLoop]; 1138 } 1139 1140 break; 1141 } 1142 } 1143 1144 if(0 == match) 1145 { 1146 outOptions->mHelp = __LINE__; 1147 retval = __LINE__; 1148 ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); 1149 } 1150 else if(NULL == current) 1151 { 1152 outOptions->mHelp = __LINE__; 1153 retval = __LINE__; 1154 ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); 1155 } 1156 else 1157 { 1158 /* 1159 ** Do something based on address/swtich. 1160 */ 1161 if(current == &gInputSwitch) 1162 { 1163 CLEANUP(outOptions->mInputName); 1164 if(NULL != outOptions->mInput && stdin != outOptions->mInput) 1165 { 1166 fclose(outOptions->mInput); 1167 outOptions->mInput = NULL; 1168 } 1169 1170 outOptions->mInput = fopen(current->mValue, "r"); 1171 if(NULL == outOptions->mInput) 1172 { 1173 retval = __LINE__; 1174 ERROR_REPORT(retval, current->mValue, "Unable to open input file."); 1175 } 1176 else 1177 { 1178 outOptions->mInputName = strdup(current->mValue); 1179 if(NULL == outOptions->mInputName) 1180 { 1181 retval = __LINE__; 1182 ERROR_REPORT(retval, current->mValue, "Unable to strdup."); 1183 } 1184 } 1185 } 1186 else if(current == &gOutputSwitch) 1187 { 1188 CLEANUP(outOptions->mOutputName); 1189 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) 1190 { 1191 fclose(outOptions->mOutput); 1192 outOptions->mOutput = NULL; 1193 } 1194 1195 outOptions->mOutput = fopen(current->mValue, "a"); 1196 if(NULL == outOptions->mOutput) 1197 { 1198 retval = __LINE__; 1199 ERROR_REPORT(retval, current->mValue, "Unable to open output file."); 1200 } 1201 else 1202 { 1203 outOptions->mOutputName = strdup(current->mValue); 1204 if(NULL == outOptions->mOutputName) 1205 { 1206 retval = __LINE__; 1207 ERROR_REPORT(retval, current->mValue, "Unable to strdup."); 1208 } 1209 } 1210 } 1211 else if(current == &gHelpSwitch) 1212 { 1213 outOptions->mHelp = __LINE__; 1214 } 1215 else if(current == &gSummarySwitch) 1216 { 1217 outOptions->mSummaryOnly = __LINE__; 1218 } 1219 else if(current == &gZeroDriftSwitch) 1220 { 1221 outOptions->mZeroDrift = __LINE__; 1222 } 1223 else if(current == &gNegationSwitch) 1224 { 1225 outOptions->mNegation = __LINE__; 1226 } 1227 else 1228 { 1229 retval = __LINE__; 1230 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); 1231 } 1232 } 1233 } 1234 1235 return retval; 1236 } 1237 1238 1239 void cleanOptions(Options* inOptions) 1240 /* 1241 ** Clean up any open handles. 1242 */ 1243 { 1244 CLEANUP(inOptions->mInputName); 1245 if(NULL != inOptions->mInput && stdin != inOptions->mInput) 1246 { 1247 fclose(inOptions->mInput); 1248 } 1249 CLEANUP(inOptions->mOutputName); 1250 if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) 1251 { 1252 fclose(inOptions->mOutput); 1253 } 1254 1255 memset(inOptions, 0, sizeof(Options)); 1256 } 1257 1258 1259 void showHelp(Options* inOptions) 1260 /* 1261 ** Show some simple help text on usage. 1262 */ 1263 { 1264 int loop = 0; 1265 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); 1266 const char* valueText = NULL; 1267 1268 printf("usage:\t%s [arguments]\n", inOptions->mProgramName); 1269 printf("\n"); 1270 printf("arguments:\n"); 1271 1272 for(loop = 0; loop < switchCount; loop++) 1273 { 1274 if(gSwitches[loop]->mHasValue) 1275 { 1276 valueText = " <value>"; 1277 } 1278 else 1279 { 1280 valueText = ""; 1281 } 1282 1283 printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); 1284 printf("\t %s%s", gSwitches[loop]->mShortName, valueText); 1285 printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); 1286 } 1287 1288 printf("This tool takes the diff of two sorted tsv files to form a summary report\n"); 1289 printf("of code and data size changes which is hoped to be human readable.\n"); 1290 } 1291 1292 1293 int main(int inArgc, char** inArgv) 1294 { 1295 int retval = 0; 1296 Options options; 1297 1298 retval = initOptions(&options, inArgc, inArgv); 1299 if(options.mHelp) 1300 { 1301 showHelp(&options); 1302 } 1303 else if(0 == retval) 1304 { 1305 retval = difftool(&options); 1306 } 1307 1308 cleanOptions(&options); 1309 return retval; 1310 } 1311 1312