CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / tools / trace-malloc / tmfrags.c
blob0cd7ab3e9cb89eeecfa6c98917f791c7cee370b9
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is tmfrags.c code, released
17 * Oct 26, 2002.
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.
24 * Contributor(s):
25 * Garrett Arch Blythe, 26-October-2002
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <math.h>
49 #include "nspr.h"
50 #include "tmreader.h"
53 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
54 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
57 #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
58 #define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
59 #define TICK_RESOLUTION 1000
60 #define TICK_PRINTABLE(timeval) ((PRFloat64)(timeval) / (PRFloat64)ST_TIMEVAL_RESOLUTION)
63 typedef struct __struct_Options
65 ** Options to control how we perform.
67 ** mProgramName Used in help text.
68 ** mInputName Name of the file.
69 ** mOutput Output file, append.
70 ** Default is stdout.
71 ** mOutputName Name of the file.
72 ** mHelp Whether or not help should be shown.
73 ** mOverhead How much overhead an allocation will have.
74 ** mAlignment What boundry will the end of an allocation line up on.
75 ** mPageSize Controls the page size. A page containing only fragments
76 ** is not fragmented. A page containing any life memory
77 ** costs mPageSize in bytes.
80 const char* mProgramName;
81 char* mInputName;
82 FILE* mOutput;
83 char* mOutputName;
84 int mHelp;
85 unsigned mOverhead;
86 unsigned mAlignment;
87 unsigned mPageSize;
89 Options;
92 typedef struct __struct_Switch
94 ** Command line options.
97 const char* mLongName;
98 const char* mShortName;
99 int mHasValue;
100 const char* mValue;
101 const char* mDescription;
103 Switch;
105 #define DESC_NEWLINE "\n\t\t"
107 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
108 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
109 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
110 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."};
111 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."};
112 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."};
114 static Switch* gSwitches[] = {
115 &gInputSwitch,
116 &gOutputSwitch,
117 &gAlignmentSwitch,
118 &gOverheadSwitch,
119 &gPageSizeSwitch,
120 &gHelpSwitch
124 typedef struct __struct_AnyArray
126 ** Variable sized item array.
128 ** mItems The void pointer items.
129 ** mItemSize Size of each different item.
130 ** mCount The number of items in the array.
131 ** mCapacity How many more items we can hold before reallocing.
132 ** mGrowBy How many items we allocate when we grow.
135 void* mItems;
136 unsigned mItemSize;
137 unsigned mCount;
138 unsigned mCapacity;
139 unsigned mGrowBy;
141 AnyArray;
144 typedef int (*arrayMatchFunc)(void* inContext, AnyArray* inArray, void* inItem, unsigned inItemIndex)
146 ** Callback function for the arrayIndexFn function.
147 ** Used to determine an item match by customizable criteria.
149 ** inContext The criteria and state of the search.
150 ** User specified/created.
151 ** inArray The array the item is in.
152 ** inItem The item to evaluate for match.
153 ** inItemIndex The index of this particular item in the array.
155 ** return int 0 to specify a match.
156 ** !0 to continue the search performed by arrayIndexFn.
161 typedef enum __enum_HeapEventType
163 ** Simple heap events are really one of two things.
166 FREE,
167 ALLOC
169 HeapEventType;
172 typedef enum __enum_HeapObjectType
174 ** The various types of heap objects we track.
177 ALLOCATION,
178 FRAGMENT
180 HeapObjectType;
183 typedef struct __struct_HeapObject HeapObject;
184 typedef struct __struct_HeapHistory
186 ** A marker as to what has happened.
188 ** mTimestamp When history occurred.
189 ** mTMRSerial The historical state as known to the tmreader.
190 ** mObjectIndex Index to the object that was before or after this event.
191 ** The index as in the index according to all heap objects
192 ** kept in the TMState structure.
193 ** We use an index instead of a pointer as the array of
194 ** objects can change location in the heap.
197 unsigned mTimestamp;
198 unsigned mTMRSerial;
199 unsigned mObjectIndex;
201 HeapHistory;
204 struct __struct_HeapObject
206 ** An object in the heap.
208 ** A special case should be noted here. If either the birth or death
209 ** history leads to an object of the same type, then this object
210 ** is the same as that object, but was modified somehow.
211 ** Also note that multiple objects may have the same birth object,
212 ** as well as the same death object.
214 ** mUniqueID Each object is unique.
215 ** mType Either allocation or fragment.
216 ** mHeapOffset Where in the heap the object is.
217 ** mSize How much of the heap the object takes.
218 ** mBirth History about the birth event.
219 ** mDeath History about the death event.
222 unsigned mUniqueID;
224 HeapObjectType mType;
225 unsigned mHeapOffset;
226 unsigned mSize;
228 HeapHistory mBirth;
229 HeapHistory mDeath;
233 typedef struct __struct_TMState
235 ** State of our current operation.
236 ** Stats we are trying to calculate.
238 ** mOptions Obilgatory options pointer.
239 ** mTMR The tmreader, used in tmreader API calls.
240 ** mLoopExitTMR Set to non zero in order to quickly exit from tmreader
241 ** input loop. This will also result in an error.
242 ** uMinTicks Start of run, milliseconds.
243 ** uMaxTicks End of run, milliseconds.
246 Options* mOptions;
247 tmreader* mTMR;
249 int mLoopExitTMR;
251 unsigned uMinTicks;
252 unsigned uMaxTicks;
254 TMState;
257 int initOptions(Options* outOptions, int inArgc, char** inArgv)
259 ** returns int 0 if successful.
262 int retval = 0;
263 int loop = 0;
264 int switchLoop = 0;
265 int match = 0;
266 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
267 Switch* current = NULL;
270 ** Set any defaults.
272 memset(outOptions, 0, sizeof(Options));
273 outOptions->mProgramName = inArgv[0];
274 outOptions->mInputName = strdup("-");
275 outOptions->mOutput = stdout;
276 outOptions->mOutputName = strdup("stdout");
277 outOptions->mAlignment = 16;
278 outOptions->mOverhead = 8;
280 if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
282 retval = __LINE__;
283 ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
287 ** Go through and attempt to do the right thing.
289 for(loop = 1; loop < inArgc && 0 == retval; loop++)
291 match = 0;
292 current = NULL;
294 for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
296 if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
298 match = __LINE__;
300 else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
302 match = __LINE__;
305 if(match)
307 if(gSwitches[switchLoop]->mHasValue)
310 ** Attempt to absorb next option to fullfill value.
312 if(loop + 1 < inArgc)
314 loop++;
316 current = gSwitches[switchLoop];
317 current->mValue = inArgv[loop];
320 else
322 current = gSwitches[switchLoop];
325 break;
329 if(0 == match)
331 outOptions->mHelp = __LINE__;
332 retval = __LINE__;
333 ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
335 else if(NULL == current)
337 outOptions->mHelp = __LINE__;
338 retval = __LINE__;
339 ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
341 else
344 ** Do something based on address/swtich.
346 if(current == &gInputSwitch)
348 CLEANUP(outOptions->mInputName);
349 outOptions->mInputName = strdup(current->mValue);
350 if(NULL == outOptions->mInputName)
352 retval = __LINE__;
353 ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
356 else if(current == &gOutputSwitch)
358 CLEANUP(outOptions->mOutputName);
359 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
361 fclose(outOptions->mOutput);
362 outOptions->mOutput = NULL;
365 outOptions->mOutput = fopen(current->mValue, "a");
366 if(NULL == outOptions->mOutput)
368 retval = __LINE__;
369 ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
371 else
373 outOptions->mOutputName = strdup(current->mValue);
374 if(NULL == outOptions->mOutputName)
376 retval = __LINE__;
377 ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
381 else if(current == &gHelpSwitch)
383 outOptions->mHelp = __LINE__;
385 else if(current == &gAlignmentSwitch)
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)
394 outOptions->mAlignment = arg;
396 else
398 retval = __LINE__;
399 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
402 else if(current == &gOverheadSwitch)
404 unsigned arg = 0;
405 char* endScan = NULL;
407 errno = 0;
408 arg = strtoul(current->mValue, &endScan, 0);
409 if(0 == errno && endScan != current->mValue)
411 outOptions->mOverhead = arg;
413 else
415 retval = __LINE__;
416 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
419 else if(current == &gPageSizeSwitch)
421 unsigned arg = 0;
422 char* endScan = NULL;
424 errno = 0;
425 arg = strtoul(current->mValue, &endScan, 0);
426 if(0 == errno && endScan != current->mValue)
428 outOptions->mPageSize = arg;
430 else
432 retval = __LINE__;
433 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
436 else
438 retval = __LINE__;
439 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
444 return retval;
448 PRUint32 ticks2xsec(tmreader* aReader, PRUint32 aTicks, PRUint32 aResolution)
450 ** Convert platform specific ticks to second units
453 PRUint32 retval = 0;
454 PRUint64 bigone;
455 PRUint64 tmp64;
457 LL_UI2L(bigone, aResolution);
458 LL_UI2L(tmp64, aTicks);
459 LL_MUL(bigone, bigone, tmp64);
460 LL_UI2L(tmp64, aReader->ticksPerSec);
461 LL_DIV(bigone, bigone, tmp64);
462 LL_L2UI(retval, bigone);
463 return retval;
467 void cleanOptions(Options* inOptions)
469 ** Clean up any open handles.
472 unsigned loop = 0;
474 CLEANUP(inOptions->mInputName);
475 CLEANUP(inOptions->mOutputName);
476 if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
478 fclose(inOptions->mOutput);
481 memset(inOptions, 0, sizeof(Options));
485 void showHelp(Options* inOptions)
487 ** Show some simple help text on usage.
490 int loop = 0;
491 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
492 const char* valueText = NULL;
494 printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
495 printf("\n");
496 printf("arguments:\n");
498 for(loop = 0; loop < switchCount; loop++)
500 if(gSwitches[loop]->mHasValue)
502 valueText = " <value>";
504 else
506 valueText = "";
509 printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
510 printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
511 printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
514 printf("This tool reports heap fragmentation stats from a trace-malloc log.\n");
518 AnyArray* arrayCreate(unsigned inItemSize, unsigned inGrowBy)
520 ** Create an array container object.
523 AnyArray* retval = NULL;
525 if(0 != inGrowBy && 0 != inItemSize)
527 retval = (AnyArray*)calloc(1, sizeof(AnyArray));
528 retval->mItemSize = inItemSize;
529 retval->mGrowBy = inGrowBy;
532 return retval;
536 void arrayDestroy(AnyArray* inArray)
538 ** Release the memory the array contains.
539 ** This will release the items as well.
542 if(NULL != inArray)
544 if(NULL != inArray->mItems)
546 free(inArray->mItems);
548 free(inArray);
553 unsigned arrayAlloc(AnyArray* inArray, unsigned inItems)
555 ** Resize the item array capcity to a specific number of items.
556 ** This could possibly truncate the array, so handle that as well.
558 ** returns unsigned <= inArray->mCapacity on success.
561 unsigned retval = (unsigned)-1;
563 if(NULL != inArray)
565 void* moved = NULL;
567 moved = realloc(inArray->mItems, inItems * inArray->mItemSize);
568 if(NULL != moved)
570 inArray->mItems = moved;
571 inArray->mCapacity = inItems;
572 if(inArray->mCount > inItems)
574 inArray->mCount = inItems;
577 retval = inItems;
581 return retval;
585 void* arrayItem(AnyArray* inArray, unsigned inIndex)
587 ** Return the array item at said index.
588 ** Zero based index.
590 ** returns void* NULL on failure.
593 void* retval = NULL;
595 if(NULL != inArray && inIndex < inArray->mCount)
597 retval = (void*)((char*)inArray->mItems + (inArray->mItemSize * inIndex));
600 return retval;
604 unsigned arrayIndex(AnyArray* inArray, void* inItem, unsigned inStartIndex)
606 ** Go through the array from the index specified looking for an item
607 ** match based on byte for byte comparison.
608 ** We allow specifying the start index in order to handle arrays with
609 ** duplicate items.
611 ** returns unsigned >= inArray->mCount on failure.
614 unsigned retval = (unsigned)-1;
616 if(NULL != inArray && NULL != inItem && inStartIndex < inArray->mCount)
618 void* curItem = NULL;
620 for(retval = inStartIndex; retval < inArray->mCount; retval++)
622 curItem = arrayItem(inArray, retval);
623 if(0 == memcmp(inItem, curItem, inArray->mItemSize))
625 break;
631 return retval;
635 unsigned arrayIndexFn(AnyArray* inArray, arrayMatchFunc inFunc, void* inFuncContext, unsigned inStartIndex)
637 ** Go through the array from the index specified looking for an item
638 ** match based upon the return value of inFunc (0, Zero, is a match).
639 ** We allow specifying the start index in order to facilitate looping over
640 ** the array which could have multiple matches.
642 ** returns unsigned >= inArray->mCount on failure.
645 unsigned retval = (unsigned)-1;
647 if(NULL != inArray && NULL != inFunc && inStartIndex < inArray->mCount)
649 void* curItem = NULL;
651 for(retval = inStartIndex; retval < inArray->mCount; retval++)
653 curItem = arrayItem(inArray, retval);
654 if(0 == inFunc(inFuncContext, inArray, curItem, retval))
656 break;
661 return retval;
665 unsigned arrayAddItem(AnyArray* inArray, void* inItem)
667 ** Add a new item to the array.
668 ** This is done by copying the item.
670 ** returns unsigned < inArray->mCount on success.
673 unsigned retval = (unsigned)-1;
675 if(NULL != inArray && NULL != inItem)
677 int noCopy = 0;
680 ** See if the array should grow.
682 if(inArray->mCount == inArray->mCapacity)
684 unsigned allocRes = 0;
686 allocRes = arrayAlloc(inArray, inArray->mCapacity + inArray->mGrowBy);
687 if(allocRes > inArray->mCapacity)
689 noCopy = __LINE__;
693 if(0 == noCopy)
695 retval = inArray->mCount;
697 inArray->mCount++;
698 memcpy(arrayItem(inArray, retval), inItem, inArray->mItemSize);
702 return retval;
706 HeapObject* initHeapObject(HeapObject* inObject)
708 ** Function to init the heap object just right.
709 ** Sets the unique ID to something unique.
712 HeapObject* retval = inObject;
714 if(NULL != inObject)
716 static unsigned uniqueGenerator = 0;
718 memset(inObject, -1, sizeof(HeapObject));
720 inObject->mUniqueID = uniqueGenerator;
721 uniqueGenerator++;
724 return retval;
728 int simpleHeapEvent(TMState* inStats, HeapEventType inType, unsigned mTimestamp, unsigned inSerial, unsigned inHeapID, unsigned inSize)
730 ** A new heap event will cause the creation of a new heap object.
731 ** The new heap object will displace, or replace, a heap object of a different type.
734 int retval = 0;
735 HeapObject newObject;
738 ** Set the most basic object details.
740 initHeapObject(&newObject);
741 newObject.mHeapOffset = inHeapID;
742 newObject.mSize = inSize;
743 if(FREE == inType)
745 newObject.mType = FRAGMENT;
747 else if(ALLOC == inType)
749 newObject.mType = ALLOCATION;
753 ** Add it to the heap object array.
757 ** TODO GAB
759 ** First thing to do is to add the new object to the heap in order to
760 ** obtain a valid index.
762 ** Next, find all matches to this range of heap memory that this event
763 ** refers to, that are alive during this timestamp (no death yet).
764 ** Fill in the death event of those objects.
765 ** If the objects contain some portions outside of the range, then
766 ** new objects for those ranges need to be created that carry on
767 ** the same object type, have the index of the old object for birth,
768 ** and the serial of the old object, new timestamp of course.
769 ** The old object's death points to the new object, which tells why the
770 ** fragmentation took place.
771 ** The new object birth points to the old object only if a fragment.
772 ** An allocation only has a birth object when it is a realloc (complex)
773 ** heap event.
775 ** I believe this give us enough information to look up particular
776 ** details of the heap at any given time.
779 return retval;
783 int complexHeapEvent(TMState* inStats, unsigned mTimestamp, unsigned inOldSerial, unsigned inOldHeapID, unsigned inOSize, unsigned inNewSerial, unsigned inNewHeapID, unsigned inNewSize)
785 ** Generally, this event intends to chain one old heap object to a newer heap object.
786 ** Otherwise, the functionality should recognizable ala simpleHeapEvent.
789 int retval = 0;
792 ** TODO GAB
795 return retval;
799 unsigned actualByteSize(Options* inOptions, unsigned retval)
801 ** Apply alignment and overhead to size to figure out actual byte size.
802 ** This by default mimics spacetrace with default options (msvc crt heap).
805 if(0 != retval)
807 unsigned eval = 0;
808 unsigned over = 0;
810 eval = retval - 1;
811 if(0 != inOptions->mAlignment)
813 over = eval % inOptions->mAlignment;
815 retval = eval + inOptions->mOverhead + inOptions->mAlignment - over;
818 return retval;
822 void tmEventHandler(tmreader* inReader, tmevent* inEvent)
824 ** Callback from the tmreader_eventloop.
825 ** Build up our fragmentation information herein.
828 char type = inEvent->type;
829 TMState* stats = (TMState*)inReader->data;
832 ** Only intersted in handling events of a particular type.
834 switch(type)
836 default:
837 return;
839 case TM_EVENT_MALLOC:
840 case TM_EVENT_CALLOC:
841 case TM_EVENT_REALLOC:
842 case TM_EVENT_FREE:
843 break;
847 ** Should we even try to look?
848 ** Set mLoopExitTMR to non-zero to abort the read loop faster.
850 if(0 == stats->mLoopExitTMR)
852 Options* options = (Options*)stats->mOptions;
853 unsigned timestamp = ticks2msec(stats->mTMR, inEvent->u.alloc.interval);
854 unsigned actualSize = actualByteSize(options, inEvent->u.alloc.size);
855 unsigned heapID = inEvent->u.alloc.ptr;
856 unsigned serial = inEvent->serial;
859 ** Check the timestamp range of our overall state.
861 if(stats->uMinTicks > timestamp)
863 stats->uMinTicks = timestamp;
865 if(stats->uMaxTicks < timestamp)
867 stats->uMaxTicks = timestamp;
871 ** Realloc in general deserves some special attention if dealing
872 ** with an old allocation (not new memory).
874 if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial)
876 unsigned oldActualSize = actualByteSize(options, inEvent->u.alloc.oldsize);
877 unsigned oldHeapID = inEvent->u.alloc.oldptr;
878 unsigned oldSerial = inEvent->u.alloc.oldserial;
880 if(0 == actualSize)
883 ** Reallocs of size zero are to become free events.
885 stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, oldHeapID, oldActualSize);
887 else if(heapID != oldHeapID || actualSize != oldActualSize)
890 ** Reallocs which moved generate two events.
891 ** Reallocs which changed size generate two events.
893 ** One event to free the old memory area.
894 ** Another event to allocate the new memory area.
895 ** They are to be linked to one another, so the history
896 ** and true origin can be tracked.
898 stats->mLoopExitTMR = complexHeapEvent(stats, timestamp, oldSerial, oldHeapID, oldActualSize, serial, heapID, actualSize);
900 else
903 ** The realloc is not considered an operation and is skipped.
904 ** It is not an operation, because it did not move or change
905 ** size; this can happen if a realloc falls within the
906 ** alignment of an allocation.
907 ** Say if you realloc a 1 byte allocation to 2 bytes, it will
908 ** not really change heap impact unless you have 1 set as
909 ** the alignment of your allocations.
913 else if(TM_EVENT_FREE == type)
916 ** Generate a free event to create a fragment.
918 stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, heapID, actualSize);
920 else
923 ** Generate an allocation event to clear fragments.
925 stats->mLoopExitTMR = simpleHeapEvent(stats, ALLOC, timestamp, serial, heapID, actualSize);
931 int tmfrags(Options* inOptions)
933 ** Load the input file and report stats.
936 int retval = 0;
937 TMState stats;
939 memset(&stats, 0, sizeof(stats));
940 stats.mOptions = inOptions;
941 stats.uMinTicks = 0xFFFFFFFFU;
944 ** Need a tmreader.
946 stats.mTMR = tmreader_new(inOptions->mProgramName, &stats);
947 if(NULL != stats.mTMR)
949 int tmResult = 0;
951 tmResult = tmreader_eventloop(stats.mTMR, inOptions->mInputName, tmEventHandler);
952 if(0 == tmResult)
954 retval = __LINE__;
955 ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data.");
957 if(0 != stats.mLoopExitTMR)
959 retval = stats.mLoopExitTMR;
960 ERROR_REPORT(retval, inOptions->mInputName, "Aborted trace-malloc input loop.");
963 tmreader_destroy(stats.mTMR);
964 stats.mTMR = NULL;
966 else
968 retval = __LINE__;
969 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader.");
972 return retval;
976 int main(int inArgc, char** inArgv)
978 int retval = 0;
979 Options options;
981 retval = initOptions(&options, inArgc, inArgv);
982 if(options.mHelp)
984 showHelp(&options);
986 else if(0 == retval)
988 retval = tmfrags(&options);
991 cleanOptions(&options);
992 return retval;