tools/trace-malloc/tmfrags.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <stdio.h>
     8 #include <stdlib.h>
     9 #include <string.h>
    10 #include <time.h>
    11 #include <ctype.h>
    12 #include <errno.h>
    13 #include <math.h>
    15 #include "nspr.h"
    16 #include "tmreader.h"
    19 #define ERROR_REPORT(num, val, msg)   fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
    20 #define CLEANUP(ptr)    do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
    23 #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
    24 #define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
    25 #define TICK_RESOLUTION 1000
    26 #define TICK_PRINTABLE(timeval) ((double)(timeval) / (double)ST_TIMEVAL_RESOLUTION)
    29 typedef struct __struct_Options
    30 /*
    31 **  Options to control how we perform.
    32 **
    33 **  mProgramName    Used in help text.
    34 **  mInputName      Name of the file.
    35 **  mOutput         Output file, append.
    36 **                  Default is stdout.
    37 **  mOutputName     Name of the file.
    38 **  mHelp           Whether or not help should be shown.
    39 **  mOverhead       How much overhead an allocation will have.
    40 **  mAlignment      What boundry will the end of an allocation line up on.
    41 **  mPageSize       Controls the page size.  A page containing only fragments
    42 **                      is not fragmented.  A page containing any life memory
    43 **                      costs mPageSize in bytes.
    44 */
    45 {
    46     const char* mProgramName;
    47     char* mInputName;
    48     FILE* mOutput;
    49     char* mOutputName;
    50     int mHelp;
    51     unsigned mOverhead;
    52     unsigned mAlignment;
    53     unsigned mPageSize;
    54 }
    55 Options;
    58 typedef struct __struct_Switch
    59 /*
    60 **  Command line options.
    61 */
    62 {
    63     const char* mLongName;
    64     const char* mShortName;
    65     int mHasValue;
    66     const char* mValue;
    67     const char* mDescription;
    68 }
    69 Switch;
    71 #define DESC_NEWLINE "\n\t\t"
    73 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
    74 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
    75 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
    76 static Switch gAlignmentSwitch = {"--alignment", "-al", 1, NULL, "All allocation sizes are made to be a multiple of this number." DESC_NEWLINE "Closer to actual heap conditions; set to 1 for true sizes." DESC_NEWLINE "Default value is 16."};
    77 static Switch gOverheadSwitch = {"--overhead", "-ov", 1, NULL, "After alignment, all allocations are made to increase by this number." DESC_NEWLINE "Closer to actual heap conditions; set to 0 for true sizes." DESC_NEWLINE "Default value is 8."};
    78 static Switch gPageSizeSwitch = {"--page-size", "-ps", 1, NULL, "Sets the page size which aids the identification of fragmentation." DESC_NEWLINE "Closer to actual heap conditions; set to 4294967295 for true sizes."  DESC_NEWLINE "Default value is 4096."};
    80 static Switch* gSwitches[] = {
    81         &gInputSwitch,
    82         &gOutputSwitch,
    83         &gAlignmentSwitch,
    84         &gOverheadSwitch,
    85         &gPageSizeSwitch,
    86         &gHelpSwitch
    87 };
    90 typedef struct __struct_AnyArray
    91 /*
    92 **  Variable sized item array.
    93 **
    94 **  mItems      The void pointer items.
    95 **  mItemSize   Size of each different item.
    96 **  mCount      The number of items in the array.
    97 **  mCapacity   How many more items we can hold before reallocing.
    98 **  mGrowBy     How many items we allocate when we grow.
    99 */
   100 {
   101     void* mItems;
   102     unsigned mItemSize;
   103     unsigned mCount;
   104     unsigned mCapacity;
   105     unsigned mGrowBy;
   106 }
   107 AnyArray;
   110 typedef int (*arrayMatchFunc)(void* inContext, AnyArray* inArray, void* inItem, unsigned inItemIndex)
   111 /*
   112 **  Callback function for the arrayIndexFn function.
   113 **  Used to determine an item match by customizable criteria.
   114 **
   115 **  inContext       The criteria and state of the search.
   116 **                  User specified/created.
   117 **  inArray         The array the item is in.
   118 **  inItem          The item to evaluate for match.
   119 **  inItemIndex     The index of this particular item in the array.
   120 **
   121 **  return int      0 to specify a match.
   122 **                  !0 to continue the search performed by arrayIndexFn.
   123 */
   124 ;
   127 typedef enum __enum_HeapEventType
   128 /*
   129 **  Simple heap events are really one of two things.
   130 */
   131 {
   132     FREE,
   133     ALLOC
   134 }
   135 HeapEventType;
   138 typedef enum __enum_HeapObjectType
   139 /*
   140 **  The various types of heap objects we track.
   141 */
   142 {
   143     ALLOCATION,
   144     FRAGMENT
   145 }
   146 HeapObjectType;
   149 typedef struct __struct_HeapObject HeapObject;
   150 typedef struct __struct_HeapHistory
   151 /*
   152 **  A marker as to what has happened.
   153 **
   154 **  mTimestamp      When history occurred.
   155 **  mTMRSerial      The historical state as known to the tmreader.
   156 **  mObjectIndex    Index to the object that was before or after this event.
   157 **                  The index as in the index according to all heap objects
   158 **                      kept in the TMState structure.
   159 **                  We use an index instead of a pointer as the array of
   160 **                      objects can change location in the heap.
   161 */
   162 {
   163     unsigned mTimestamp;
   164     unsigned mTMRSerial;
   165     unsigned mObjectIndex;
   166 }
   167 HeapHistory;
   170 struct __struct_HeapObject
   171 /*
   172 **  An object in the heap.
   173 **
   174 **  A special case should be noted here.  If either the birth or death
   175 **      history leads to an object of the same type, then this object
   176 **      is the same as that object, but was modified somehow.
   177 **  Also note that multiple objects may have the same birth object,
   178 **      as well as the same death object.
   179 **
   180 **  mUniqueID           Each object is unique.
   181 **  mType               Either allocation or fragment.
   182 **  mHeapOffset         Where in the heap the object is.
   183 **  mSize               How much of the heap the object takes.
   184 **  mBirth              History about the birth event.
   185 **  mDeath              History about the death event.
   186 */
   187 {
   188     unsigned mUniqueID;
   190     HeapObjectType mType;
   191     unsigned mHeapOffset;
   192     unsigned mSize;
   194     HeapHistory mBirth;
   195     HeapHistory mDeath;
   196 };
   199 typedef struct __struct_TMState
   200 /*
   201 **  State of our current operation.
   202 **  Stats we are trying to calculate.
   203 **
   204 **  mOptions        Obilgatory options pointer.
   205 **  mTMR            The tmreader, used in tmreader API calls.
   206 **  mLoopExitTMR    Set to non zero in order to quickly exit from tmreader
   207 **                      input loop.  This will also result in an error.
   208 **  uMinTicks       Start of run, milliseconds.
   209 **  uMaxTicks       End of run, milliseconds.
   210 */
   211 {
   212     Options* mOptions;
   213     tmreader* mTMR;
   215     int mLoopExitTMR;
   217     unsigned uMinTicks;
   218     unsigned uMaxTicks;
   219 }
   220 TMState;
   223 int initOptions(Options* outOptions, int inArgc, char** inArgv)
   224 /*
   225 **  returns int     0 if successful.
   226 */
   227 {
   228     int retval = 0;
   229     int loop = 0;
   230     int switchLoop = 0;
   231     int match = 0;
   232     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
   233     Switch* current = NULL;
   235     /*
   236     **  Set any defaults.
   237     */
   238     memset(outOptions, 0, sizeof(Options));
   239     outOptions->mProgramName = inArgv[0];
   240     outOptions->mInputName = strdup("-");
   241     outOptions->mOutput = stdout;
   242     outOptions->mOutputName = strdup("stdout");
   243     outOptions->mAlignment = 16;
   244     outOptions->mOverhead = 8;
   246     if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
   247     {
   248         retval = __LINE__;
   249         ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
   250     }
   252     /*
   253     **  Go through and attempt to do the right thing.
   254     */
   255     for(loop = 1; loop < inArgc && 0 == retval; loop++)
   256     {
   257         match = 0;
   258         current = NULL;
   260         for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
   261         {
   262             if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
   263             {
   264                 match = __LINE__;
   265             }
   266             else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
   267             {
   268                 match = __LINE__;
   269             }
   271             if(match)
   272             {
   273                 if(gSwitches[switchLoop]->mHasValue)
   274                 {
   275                     /*
   276                     **  Attempt to absorb next option to fullfill value.
   277                     */
   278                     if(loop + 1 < inArgc)
   279                     {
   280                         loop++;
   282                         current = gSwitches[switchLoop];
   283                         current->mValue = inArgv[loop];
   284                     }
   285                 }
   286                 else
   287                 {
   288                     current = gSwitches[switchLoop];
   289                 }
   291                 break;
   292             }
   293         }
   295         if(0 == match)
   296         {
   297             outOptions->mHelp = __LINE__;
   298             retval = __LINE__;
   299             ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
   300         }
   301         else if(NULL == current)
   302         {
   303             outOptions->mHelp = __LINE__;
   304             retval = __LINE__;
   305             ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
   306         }
   307         else
   308         {
   309             /*
   310             ** Do something based on address/swtich.
   311             */
   312             if(current == &gInputSwitch)
   313             {
   314                 CLEANUP(outOptions->mInputName);
   315                 outOptions->mInputName = strdup(current->mValue);
   316                 if(NULL == outOptions->mInputName)
   317                 {
   318                     retval = __LINE__;
   319                     ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
   320                 }
   321             }
   322             else if(current == &gOutputSwitch)
   323             {
   324                 CLEANUP(outOptions->mOutputName);
   325                 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
   326                 {
   327                     fclose(outOptions->mOutput);
   328                     outOptions->mOutput = NULL;
   329                 }
   331                 outOptions->mOutput = fopen(current->mValue, "a");
   332                 if(NULL == outOptions->mOutput)
   333                 {
   334                     retval = __LINE__;
   335                     ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
   336                 }
   337                 else
   338                 {
   339                     outOptions->mOutputName = strdup(current->mValue);
   340                     if(NULL == outOptions->mOutputName)
   341                     {
   342                         retval = __LINE__;
   343                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
   344                     }
   345                 }
   346             }
   347             else if(current == &gHelpSwitch)
   348             {
   349                 outOptions->mHelp = __LINE__;
   350             }
   351             else if(current == &gAlignmentSwitch)
   352             {
   353                 unsigned arg = 0;
   354                 char* endScan = NULL;
   356                 errno = 0;
   357                 arg = strtoul(current->mValue, &endScan, 0);
   358                 if(0 == errno && endScan != current->mValue)
   359                 {
   360                     outOptions->mAlignment = arg;
   361                 }
   362                 else
   363                 {
   364                     retval = __LINE__;
   365                     ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
   366                 }
   367             }
   368             else if(current == &gOverheadSwitch)
   369             {
   370                 unsigned arg = 0;
   371                 char* endScan = NULL;
   373                 errno = 0;
   374                 arg = strtoul(current->mValue, &endScan, 0);
   375                 if(0 == errno && endScan != current->mValue)
   376                 {
   377                     outOptions->mOverhead = arg;
   378                 }
   379                 else
   380                 {
   381                     retval = __LINE__;
   382                     ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
   383                 }
   384             }
   385             else if(current == &gPageSizeSwitch)
   386             {
   387                 unsigned arg = 0;
   388                 char* endScan = NULL;
   390                 errno = 0;
   391                 arg = strtoul(current->mValue, &endScan, 0);
   392                 if(0 == errno && endScan != current->mValue)
   393                 {
   394                     outOptions->mPageSize = arg;
   395                 }
   396                 else
   397                 {
   398                     retval = __LINE__;
   399                     ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
   400                 }
   401             }
   402             else
   403             {
   404                 retval = __LINE__;
   405                 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
   406             }
   407         }
   408     }
   410     return retval;
   411 }
   414 uint32_t ticks2xsec(tmreader* aReader, uint32_t aTicks, uint32_t aResolution)
   415 /*
   416 ** Convert platform specific ticks to second units
   417 */
   418 {
   419     return (uint32)((aResolution * aTicks) / aReader->ticksPerSec);
   420 }
   423 void cleanOptions(Options* inOptions)
   424 /*
   425 **  Clean up any open handles.
   426 */
   427 {
   428     unsigned loop = 0;
   430     CLEANUP(inOptions->mInputName);
   431     CLEANUP(inOptions->mOutputName);
   432     if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
   433     {
   434         fclose(inOptions->mOutput);
   435     }
   437     memset(inOptions, 0, sizeof(Options));
   438 }
   441 void showHelp(Options* inOptions)
   442 /*
   443 **  Show some simple help text on usage.
   444 */
   445 {
   446     int loop = 0;
   447     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
   448     const char* valueText = NULL;
   450     printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
   451     printf("\n");
   452     printf("arguments:\n");
   454     for(loop = 0; loop < switchCount; loop++)
   455     {
   456         if(gSwitches[loop]->mHasValue)
   457         {
   458             valueText = " <value>";
   459         }
   460         else
   461         {
   462             valueText = "";
   463         }
   465         printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
   466         printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
   467         printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
   468     }
   470     printf("This tool reports heap fragmentation stats from a trace-malloc log.\n");
   471 }
   474 AnyArray* arrayCreate(unsigned inItemSize, unsigned inGrowBy)
   475 /*
   476 **  Create an array container object.
   477 */
   478 {
   479     AnyArray* retval = NULL;
   481     if(0 != inGrowBy && 0 != inItemSize)
   482     {
   483         retval = (AnyArray*)calloc(1, sizeof(AnyArray));
   484         retval->mItemSize = inItemSize;
   485         retval->mGrowBy = inGrowBy;
   486     }
   488     return retval;
   489 }
   492 void arrayDestroy(AnyArray* inArray)
   493 /*
   494 **  Release the memory the array contains.
   495 **  This will release the items as well.
   496 */
   497 {
   498     if(NULL != inArray)
   499     {
   500         if(NULL != inArray->mItems)
   501         {
   502             free(inArray->mItems);
   503         }
   504         free(inArray);
   505     }
   506 }
   509 unsigned arrayAlloc(AnyArray* inArray, unsigned inItems)
   510 /*
   511 **  Resize the item array capcity to a specific number of items.
   512 **  This could possibly truncate the array, so handle that as well.
   513 **
   514 **  returns unsigned        <= inArray->mCapacity on success.
   515 */
   516 {
   517     unsigned retval = (unsigned)-1;
   519     if(NULL != inArray)
   520     {
   521         void* moved = NULL;
   523         moved = realloc(inArray->mItems, inItems * inArray->mItemSize);
   524         if(NULL != moved)
   525         {
   526             inArray->mItems = moved;
   527             inArray->mCapacity = inItems;
   528             if(inArray->mCount > inItems)
   529             {
   530                 inArray->mCount = inItems;
   531             }
   533             retval = inItems;
   534         }
   535     }
   537     return retval;
   538 }
   541 void* arrayItem(AnyArray* inArray, unsigned inIndex)
   542 /*
   543 **  Return the array item at said index.
   544 **  Zero based index.
   545 **
   546 **  returns void*       NULL on failure.
   547 */
   548 {
   549     void* retval = NULL;
   551     if(NULL != inArray && inIndex < inArray->mCount)
   552     {
   553         retval = (void*)((char*)inArray->mItems + (inArray->mItemSize * inIndex));
   554     }
   556     return retval;
   557 }
   560 unsigned arrayIndex(AnyArray* inArray, void* inItem, unsigned inStartIndex)
   561 /*
   562 **  Go through the array from the index specified looking for an item
   563 **      match based on byte for byte comparison.
   564 **  We allow specifying the start index in order to handle arrays with
   565 **      duplicate items.
   566 **
   567 **  returns unsigned        >= inArray->mCount on failure.
   568 */
   569 {
   570     unsigned retval = (unsigned)-1;
   572     if(NULL != inArray && NULL != inItem && inStartIndex < inArray->mCount)
   573     {
   574         void* curItem = NULL;
   576         for(retval = inStartIndex; retval < inArray->mCount; retval++)
   577         {
   578             curItem = arrayItem(inArray, retval);
   579             if(0 == memcmp(inItem, curItem, inArray->mItemSize))
   580             {
   581                 break;
   582             }
   583         }
   584     }
   587     return retval;
   588 }
   591 unsigned arrayIndexFn(AnyArray* inArray, arrayMatchFunc inFunc, void* inFuncContext, unsigned inStartIndex)
   592 /*
   593 **  Go through the array from the index specified looking for an item
   594 **      match based upon the return value of inFunc (0, Zero, is a match).
   595 **  We allow specifying the start index in order to facilitate looping over
   596 **      the array which could have multiple matches.
   597 **
   598 **  returns unsigned        >= inArray->mCount on failure.
   599 */
   600 {
   601     unsigned retval = (unsigned)-1;
   603     if(NULL != inArray && NULL != inFunc && inStartIndex < inArray->mCount)
   604     {
   605         void* curItem = NULL;
   607         for(retval = inStartIndex; retval < inArray->mCount; retval++)
   608         {
   609             curItem = arrayItem(inArray, retval);
   610             if(0 == inFunc(inFuncContext, inArray, curItem, retval))
   611             {
   612                 break;
   613             }
   614         }
   615     }
   617     return retval;
   618 }
   621 unsigned arrayAddItem(AnyArray* inArray, void* inItem)
   622 /*
   623 **  Add a new item to the array.
   624 **  This is done by copying the item.
   625 **
   626 **  returns unsigned        < inArray->mCount on success.
   627 */
   628 {
   629     unsigned retval = (unsigned)-1;
   631     if(NULL != inArray && NULL != inItem)
   632     {
   633         int noCopy = 0;
   635         /*
   636         **  See if the array should grow.
   637         */
   638         if(inArray->mCount == inArray->mCapacity)
   639         {
   640             unsigned allocRes = 0;
   642             allocRes = arrayAlloc(inArray, inArray->mCapacity + inArray->mGrowBy);
   643             if(allocRes > inArray->mCapacity)
   644             {
   645                 noCopy = __LINE__;
   646             }
   647         }
   649         if(0 == noCopy)
   650         {
   651             retval = inArray->mCount;
   653             inArray->mCount++;
   654             memcpy(arrayItem(inArray, retval), inItem, inArray->mItemSize);
   655         }
   656     }
   658     return retval;
   659 }
   662 HeapObject* initHeapObject(HeapObject* inObject)
   663 /*
   664 **  Function to init the heap object just right.
   665 **  Sets the unique ID to something unique.
   666 */
   667 {
   668     HeapObject* retval = inObject;
   670     if(NULL != inObject)
   671     {
   672         static unsigned uniqueGenerator = 0;
   674         memset(inObject, -1, sizeof(HeapObject));
   676         inObject->mUniqueID = uniqueGenerator;
   677         uniqueGenerator++;
   678     }
   680     return retval;
   681 }
   684 int simpleHeapEvent(TMState* inStats, HeapEventType inType, unsigned mTimestamp, unsigned inSerial, unsigned inHeapID, unsigned inSize)
   685 /*
   686 **  A new heap event will cause the creation of a new heap object.
   687 **  The new heap object will displace, or replace, a heap object of a different type.
   688 */
   689 {
   690     int retval = 0;
   691     HeapObject newObject;
   693     /*
   694     **  Set the most basic object details.
   695     */
   696     initHeapObject(&newObject);
   697     newObject.mHeapOffset = inHeapID;
   698     newObject.mSize = inSize;
   699     if(FREE == inType)
   700     {
   701         newObject.mType = FRAGMENT;
   702     }
   703     else if(ALLOC == inType)
   704     {
   705         newObject.mType = ALLOCATION;
   706     }
   708     /*
   709     **  Add it to the heap object array.
   710     */
   712     /*
   713     **  TODO GAB
   714     **
   715     **  First thing to do is to add the new object to the heap in order to
   716     **      obtain a valid index.
   717     **
   718     **  Next, find all matches to this range of heap memory that this event
   719     **      refers to, that are alive during this timestamp (no death yet).
   720     **  Fill in the death event of those objects.
   721     **  If the objects contain some portions outside of the range, then
   722     **      new objects for those ranges need to be created that carry on
   723     **      the same object type, have the index of the old object for birth,
   724     **      and the serial of the old object, new timestamp of course.
   725     **  The old object's death points to the new object, which tells why the
   726     **      fragmentation took place.
   727     **  The new object birth points to the old object only if a fragment.
   728     **  An allocation only has a birth object when it is a realloc (complex)
   729     **      heap event.
   730     **
   731     **  I believe this give us enough information to look up particular
   732     **      details of the heap at any given time.
   733     */
   735     return retval;
   736 }
   739 int complexHeapEvent(TMState* inStats, unsigned mTimestamp, unsigned inOldSerial, unsigned inOldHeapID, unsigned inOSize, unsigned inNewSerial, unsigned inNewHeapID, unsigned inNewSize)
   740 /*
   741 **  Generally, this event intends to chain one old heap object to a newer heap object.
   742 **  Otherwise, the functionality should recognizable ala simpleHeapEvent.
   743 */
   744 {
   745     int retval = 0;
   747     /*
   748     **  TODO GAB
   749     */
   751     return retval;
   752 }
   755 unsigned actualByteSize(Options* inOptions, unsigned retval)
   756 /*
   757 **  Apply alignment and overhead to size to figure out actual byte size.
   758 **  This by default mimics spacetrace with default options (msvc crt heap).
   759 */
   760 {
   761     if(0 != retval)
   762     {
   763         unsigned eval = 0;
   764         unsigned over = 0;
   766         eval = retval - 1;
   767         if(0 != inOptions->mAlignment)
   768         {
   769             over = eval % inOptions->mAlignment;
   770         }
   771         retval = eval + inOptions->mOverhead + inOptions->mAlignment - over;
   772     }
   774     return retval;
   775 }
   778 void tmEventHandler(tmreader* inReader, tmevent* inEvent)
   779 /*
   780 **  Callback from the tmreader_eventloop.
   781 **  Build up our fragmentation information herein.
   782 */
   783 {
   784     char type = inEvent->type;
   785     TMState* stats = (TMState*)inReader->data;
   787     /*
   788     **  Only intersted in handling events of a particular type.
   789     */
   790     switch(type)
   791     {
   792     default:
   793         return;
   795     case TM_EVENT_MALLOC:
   796     case TM_EVENT_CALLOC:
   797     case TM_EVENT_REALLOC:
   798     case TM_EVENT_FREE:
   799         break;
   800     }
   802     /*
   803     **  Should we even try to look?
   804     **  Set mLoopExitTMR to non-zero to abort the read loop faster.
   805     */
   806     if(0 == stats->mLoopExitTMR)
   807     {
   808         Options* options = (Options*)stats->mOptions;
   809         unsigned timestamp = ticks2msec(stats->mTMR, inEvent->u.alloc.interval);
   810         unsigned actualSize = actualByteSize(options, inEvent->u.alloc.size);
   811         unsigned heapID = inEvent->u.alloc.ptr;
   812         unsigned serial = inEvent->serial;
   814         /*
   815         **  Check the timestamp range of our overall state.
   816         */
   817         if(stats->uMinTicks > timestamp)
   818         {
   819             stats->uMinTicks = timestamp;
   820         }
   821         if(stats->uMaxTicks < timestamp)
   822         {
   823             stats->uMaxTicks = timestamp;
   824         }
   826         /*
   827         **  Realloc in general deserves some special attention if dealing
   828         **      with an old allocation (not new memory).
   829         */
   830         if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial)
   831         {
   832             unsigned oldActualSize = actualByteSize(options, inEvent->u.alloc.oldsize);
   833             unsigned oldHeapID = inEvent->u.alloc.oldptr;
   834             unsigned oldSerial = inEvent->u.alloc.oldserial;
   836             if(0 == actualSize)
   837             {
   838                 /*
   839                 **  Reallocs of size zero are to become free events.
   840                 */
   841                 stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, oldHeapID, oldActualSize);
   842             }
   843             else if(heapID != oldHeapID || actualSize != oldActualSize)
   844             {
   845                 /*
   846                 **  Reallocs which moved generate two events.
   847                 **  Reallocs which changed size generate two events.
   848                 **
   849                 **  One event to free the old memory area.
   850                 **  Another event to allocate the new memory area.
   851                 **  They are to be linked to one another, so the history
   852                 **      and true origin can be tracked.
   853                 */
   854                 stats->mLoopExitTMR = complexHeapEvent(stats, timestamp, oldSerial, oldHeapID, oldActualSize, serial, heapID, actualSize);
   855             }
   856             else
   857             {
   858                 /*
   859                 **  The realloc is not considered an operation and is skipped.
   860                 **  It is not an operation, because it did not move or change
   861                 **      size; this can happen if a realloc falls within the
   862                 **      alignment of an allocation.
   863                 **  Say if you realloc a 1 byte allocation to 2 bytes, it will
   864                 **      not really change heap impact unless you have 1 set as
   865                 **      the alignment of your allocations.
   866                 */
   867             }
   868         }
   869         else if(TM_EVENT_FREE == type)
   870         {
   871             /*
   872             **  Generate a free event to create a fragment.
   873             */
   874             stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, heapID, actualSize);
   875         }
   876         else
   877         {
   878             /*
   879             **  Generate an allocation event to clear fragments.
   880             */
   881             stats->mLoopExitTMR = simpleHeapEvent(stats, ALLOC, timestamp, serial, heapID, actualSize);
   882         }
   883     }
   884 }
   887 int tmfrags(Options* inOptions)
   888 /*
   889 **  Load the input file and report stats.
   890 */
   891 {
   892     int retval = 0;
   893     TMState stats;
   895     memset(&stats, 0, sizeof(stats));
   896     stats.mOptions = inOptions;
   897     stats.uMinTicks = 0xFFFFFFFFU;
   899     /*
   900     **  Need a tmreader.
   901     */
   902     stats.mTMR = tmreader_new(inOptions->mProgramName, &stats);
   903     if(NULL != stats.mTMR)
   904     {
   905         int tmResult = 0;
   907         tmResult = tmreader_eventloop(stats.mTMR, inOptions->mInputName, tmEventHandler);
   908         if(0 == tmResult)
   909         {
   910             retval = __LINE__;
   911             ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data.");
   912         }
   913         if(0 != stats.mLoopExitTMR)
   914         {
   915             retval = stats.mLoopExitTMR;
   916             ERROR_REPORT(retval, inOptions->mInputName, "Aborted trace-malloc input loop.");
   917         }
   919         tmreader_destroy(stats.mTMR);
   920         stats.mTMR = NULL;
   921     }
   922     else
   923     {
   924         retval = __LINE__;
   925         ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader.");
   926     }
   928     return retval;
   929 }
   932 int main(int inArgc, char** inArgv)
   933 {
   934     int retval = 0;
   935     Options options;
   937     retval = initOptions(&options, inArgc, inArgv);
   938     if(options.mHelp)
   939     {
   940         showHelp(&options);
   941     }
   942     else if(0 == retval)
   943     {
   944         retval = tmfrags(&options);
   945     }
   947     cleanOptions(&options);
   948     return retval;
   949 }

mercurial