2 Copyright © 2002-2013, The AROS Development Team. All rights reserved.
5 Desc: show deltas of resources usage
9 /******************************************************************************
26 It's like doing avail flush before and after executing a program to detect
27 memleaks, excepted that this program tries to detect much more leaks.
29 Press CRTL-f whenever you want to check for leak.
45 It has to not leak itself, and not even allocate memory between checks.
46 That's why lists and nodes are statically allocated (in a system that could
47 be improved to allow reuse of nodes. (like using an Allocate-based own
48 memory allocator using a huge static array as heap)
52 ******************************************************************************/
54 #include <exec/memory.h>
55 #include <exec/tasks.h>
56 #ifndef EXEC_SEMAPHORES
57 # include <exec/semaphores.h>
59 #include <exec/execbase.h>
60 #include <graphics/gfxbase.h>
61 #include <graphics/text.h>
62 #include <intuition/classes.h>
63 #include <proto/exec.h>
64 #include <proto/graphics.h>
65 #include <dos/dosextens.h>
66 #include <proto/dos.h>
67 #include <clib/alib_protos.h>
72 #include <aros/debug.h>
74 const TEXT version
[] = "$VER: LeakWatch 0.2 (10.03.2013)\n";
76 static struct Library
*GadToolsBase
;
78 struct GadToolsBase_intern
85 Class
* scrollerclass
;
88 Class
* listviewclass
;
89 Class
* checkboxclass
;
94 /* Semaphore to protect the bevel object. */
95 struct SignalSemaphore bevelsema
;
96 /* Actually an Object *. The image used for bevel boxes. */
99 /* RenderHook for GTListView class */
100 struct Hook lv_RenderHook
;
102 /* Seglist pointer */
105 /* Required libraies */
106 APTR gt_IntuitionBase
;
114 /* struct arrays : ORN = OpenedResourceNode, TR = TrackedResources, RD = ResourceDiff
115 MR = ModifiedResource STR=stringbuf
117 #define MAX_ORN 30000
120 #define MAX_STR 300000
123 /* All resources that have an opencount, eg. libs, devs, fonts */
124 struct OpenedResourceNode
{
132 /* All resources that LeakWatch handles. */
133 struct TrackedResources
{
134 struct List opened
; /* List of OpenedResourceNode */
135 ULONG freeMem
; /* total free memory */
138 struct ModifiedResource
{
147 /* store eg. new libraries, or libraries with modified opencount
150 struct ResourceDiff
{
152 struct List modifiedOpened
; /* contains ModifiedResource */
155 /* static storage to avoid interfering with memleaks debugging */
156 static struct OpenedResourceNode _ornbuf
[MAX_ORN
];
157 static struct TrackedResources _trbuf
[MAX_TR
];
158 static struct ResourceDiff _rdbuf
[MAX_RD
];
159 static UBYTE _strbuf
[MAX_STR
];
160 static struct ModifiedResource _mrbuf
[MAX_MR
];
162 static int next_orn
= 0;
163 static int next_tr
= 0;
164 static int next_rd
= 0;
165 static int next_str
= 0;
166 static int next_mr
= 0;
168 static struct OpenedResourceNode
*get_orn()
170 if (next_orn
== MAX_ORN
)
172 return &_ornbuf
[next_orn
++];
175 static void release_orn (struct OpenedResourceNode
*orn
)
179 static struct TrackedResources
*get_tr()
181 if (next_tr
== MAX_TR
)
183 return &_trbuf
[next_tr
++];
186 static void release_tr (struct TrackedResources
*tr
)
190 static struct ResourceDiff
*get_rd()
192 if (next_rd
== MAX_RD
)
194 return &_rdbuf
[next_rd
++];
197 static void release_rd (struct ResourceDiff
*rd
)
201 static struct ModifiedResource
*get_mr()
203 if (next_mr
== MAX_MR
)
205 return &_mrbuf
[next_mr
++];
208 static void release_mr (struct ModifiedResource
*mr
)
212 CONST_STRPTR
StaticStrDup (CONST_STRPTR str
)
214 UBYTE
*start
= &_strbuf
[next_str
];
222 if (len
+ next_str
+ 1 > MAX_STR
)
225 while ((*t
++ = *str
++))
227 next_str
+= t
- start
;
228 return (CONST_STRPTR
)start
;
231 void StrFree (CONST_STRPTR str
)
235 /***********************************************************************/
237 static struct TrackedResources
*NewResourcesState(void);
238 static void DeleteResourcesState(struct TrackedResources
*rs
);
239 static struct ResourceDiff
*NewStateDiff(const struct TrackedResources
*old
,
240 const struct TrackedResources
*new);
241 static void DisplayStateDiff(const struct ResourceDiff
*rd
, int pagelines
);
242 static void DeleteStateDiff(struct ResourceDiff
*rd
);
243 static struct TrackedResources
* CopyResourcesState(const struct TrackedResources
*src
);
245 static BOOL
AddLibs(struct List
*opened
)
250 for(lib
=(struct Library
*)SysBase
->LibList
.lh_Head
;
251 lib
->lib_Node
.ln_Succ
!=NULL
;
252 lib
=(struct Library
*)lib
->lib_Node
.ln_Succ
)
254 struct OpenedResourceNode
*orn
= get_orn();
260 orn
->type
= "Library";
261 orn
->name
= StaticStrDup(lib
->lib_Node
.ln_Name
);
263 orn
->count
= lib
->lib_OpenCnt
;
264 Enqueue(opened
, (struct Node
*)orn
);
270 static BOOL
AddDevs(struct List
*opened
)
275 for(dev
=(struct Device
*)SysBase
->DeviceList
.lh_Head
;
276 dev
->dd_Library
.lib_Node
.ln_Succ
!=NULL
;
277 dev
=(struct Device
*)dev
->dd_Library
.lib_Node
.ln_Succ
)
279 struct OpenedResourceNode
*orn
= get_orn();
285 orn
->type
= "Device";
286 orn
->name
= StaticStrDup(dev
->dd_Library
.lib_Node
.ln_Name
);
288 orn
->count
= dev
->dd_Library
.lib_OpenCnt
;
289 Enqueue(opened
, (struct Node
*)orn
);
295 static BOOL
AddFonts(struct List
*opened
)
300 for(tf
=(struct TextFont
*)GfxBase
->TextFonts
.lh_Head
;
301 tf
->tf_Message
.mn_Node
.ln_Succ
!=NULL
;
302 tf
=(struct TextFont
*)tf
->tf_Message
.mn_Node
.ln_Succ
)
304 struct OpenedResourceNode
*orn
= get_orn();
311 orn
->name
= StaticStrDup(tf
->tf_Message
.mn_Node
.ln_Name
);
313 orn
->count
= tf
->tf_Accessors
;
314 Enqueue(opened
, (struct Node
*)orn
);
320 static BOOL
AddNodeNames(struct List
*opened
, struct List
*list
, CONST_STRPTR type
)
325 for(lib
=(struct Node
*)list
->lh_Head
;
327 lib
=(struct Node
*)lib
->ln_Succ
)
329 struct OpenedResourceNode
*orn
= get_orn();
336 orn
->name
= StaticStrDup(lib
->ln_Name
);
339 Enqueue(opened
, (struct Node
*)orn
);
345 static BOOL
AddASemaphore(struct List
*opened
,
346 struct SignalSemaphore
*ss
,
349 struct OpenedResourceNode
*orn
= get_orn();
357 orn
->type
= "Semaphore";
358 orn
->name
= StaticStrDup(name
);
360 orn
->count
= ss
->ss_NestCount
;
363 Enqueue(opened
, (struct Node
*)orn
);
368 static BOOL
AddAClass(struct List
*opened
,
369 Class
*cl
, CONST_STRPTR name
)
371 struct OpenedResourceNode
*orn
;
386 orn
->name
= StaticStrDup(name
);
388 orn
->count
= cl
->cl_ObjectCount
;
391 Enqueue(opened
, (struct Node
*)orn
);
396 /* Add opencount-based resources to the tracking list. */
397 static BOOL
AddOpenedResources(struct List
*opened
)
399 struct DosInfo
*di
= BADDR(DOSBase
->dl_Root
->rn_Info
);
401 if (!AddLibs(opened
))
403 if (!AddDevs(opened
))
405 if (!AddFonts(opened
))
407 if (!AddNodeNames(opened
, &SysBase
->ResourceList
, "Resource"))
409 if (!AddNodeNames(opened
, &SysBase
->IntrList
, "Interrupt"))
411 if (!AddNodeNames(opened
, &SysBase
->PortList
, "Port"))
413 if (!AddNodeNames(opened
, &SysBase
->SemaphoreList
, "Semaphore"))
415 if (!AddASemaphore(opened
, &di
->di_DevLock
, "di_DevLock"))
417 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->buttonclass
, "button"))
419 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->textclass
, "text"))
421 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->sliderclass
, "slider"))
423 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->scrollerclass
, "scroller"))
425 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->arrowclass
, "arrow"))
427 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->stringclass
, "string"))
429 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->listviewclass
, "listview"))
431 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->checkboxclass
, "checkbox"))
433 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->cycleclass
, "cycle"))
435 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->mxclass
, "mx"))
437 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->paletteclass
, "palette"))
442 /* Get a snapshot of current resources */
443 static struct TrackedResources
*NewResourcesState(void)
445 struct TrackedResources
*tr
;
452 FreeVec(AllocVec((ULONG
)(~0ul/2), MEMF_ANY
));
454 /* opencount-based stuff */
455 NEWLIST(&tr
->opened
);
456 if (!AddOpenedResources(&tr
->opened
))
460 tr
->freeMem
= AvailMem(MEMF_ANY
);
465 static void DeleteResourceNode(struct OpenedResourceNode
*orn
)
469 StrFree((APTR
)orn
->name
);
474 /* Free snapshot of current resources */
475 static void DeleteResourcesState(struct TrackedResources
*rs
)
477 struct OpenedResourceNode
*orn
;
478 struct OpenedResourceNode
*tmp
;
483 for(orn
=(struct OpenedResourceNode
*)rs
->opened
.lh_Head
;
484 orn
->node
.ln_Succ
!=NULL
;
487 tmp
= (struct OpenedResourceNode
*)orn
->node
.ln_Succ
;
488 Remove((struct Node
*)orn
);
489 DeleteResourceNode(orn
);
494 void DisplayResourcesState(const struct TrackedResources
*rs
, int pagelines
)
497 struct OpenedResourceNode
*orn
;
505 FPuts(Output(), "LeakWatch snapshot:\n");
507 mem
[0] = rs
->freeMem
;
508 VFPrintf(Output(), " Free memory : %ld bytes\n", mem
);
510 FPuts(Output(), " Opened resources:\n");
512 for(orn
=(struct OpenedResourceNode
*)rs
->opened
.lh_Head
;
513 orn
->node
.ln_Succ
!=NULL
;
514 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
516 tmp
[0] = (IPTR
)orn
->type
;
517 tmp
[1] = (IPTR
)orn
->name
;
518 tmp
[2] = (IPTR
)orn
->addr
;
519 tmp
[3] = (IPTR
)orn
->count
;
521 if (currentlines
>= (pagelines
- 2))
525 FPuts(Output(), "--- Press a key to continue ---\n");
527 Read(Input(), &buf
, 1);
530 VFPrintf(Output(), " %s: %s (0x%lx) : %lu\n", tmp
);
533 FPuts(Output(), "-- end of state\n");
536 /* Compute the delta between 2 resources snapshots.
537 * the ResourceDiff can have dangling pointers in old and nu, so dont clear
538 * them before being done with rd in the processing loop
540 static struct ResourceDiff
*NewStateDiff(const struct TrackedResources
*old
,
541 const struct TrackedResources
*nu
)
544 struct OpenedResourceNode
*orn
;
545 struct ResourceDiff
*rd
;
551 NEWLIST(&rd
->modifiedOpened
);
553 for(orn
=(struct OpenedResourceNode
*)nu
->opened
.lh_Head
;
554 orn
->node
.ln_Succ
!=NULL
;
555 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
557 struct OpenedResourceNode
*other
;
560 for(other
=(struct OpenedResourceNode
*)old
->opened
.lh_Head
;
561 other
->node
.ln_Succ
!=NULL
;
562 other
=(struct OpenedResourceNode
*)other
->node
.ln_Succ
)
564 if (orn
->addr
== other
->addr
)
566 if (!strcmp(orn
->name
, other
->name
))
569 if (orn
->count
!= other
->count
)
571 struct ModifiedResource
*mr
= get_mr();
574 mr
->type
= other
->type
;
575 mr
->name
= other
->name
;
576 mr
->addr
= other
->addr
;
577 mr
->before_count
= other
->count
;
578 mr
->after_count
= orn
->count
;
579 Enqueue(&rd
->modifiedOpened
, (struct Node
*)mr
);
586 struct ModifiedResource
*mr
= get_mr();
590 mr
->type
= orn
->type
;
591 mr
->name
= orn
->name
;
592 mr
->addr
= orn
->addr
;
593 mr
->before_count
= 0;
594 mr
->after_count
= orn
->count
;
596 Enqueue(&rd
->modifiedOpened
, (struct Node
*)mr
);
601 rd
->memLost
= old
->freeMem
- nu
->freeMem
;
606 static void DisplayStateDiff(const struct ResourceDiff
*rd
, int pagelines
)
611 struct ModifiedResource
*mr
;
614 FPuts(Output(), "LeakWatch report:\n");
615 mem
[0] = rd
->memLost
;
616 mem
[1] = (IPTR
)((rd
->memLost
> 1) ? "s" : "");
617 VFPrintf(Output(), " Memory lost : %ld byte%s\n", mem
);
619 FPuts(Output(), " Open count:\n");
621 for(mr
=(struct ModifiedResource
*)rd
->modifiedOpened
.lh_Head
;
622 mr
->node
.ln_Succ
!=NULL
;
623 mr
=(struct ModifiedResource
*)mr
->node
.ln_Succ
)
625 modified
[0] = (IPTR
)mr
->type
;
626 modified
[1] = (IPTR
)mr
->name
;
627 modified
[2] = (IPTR
)mr
->addr
;
628 modified
[3] = mr
->before_count
;
629 modified
[4] = mr
->after_count
;
631 if (currentlines
>= (pagelines
- 2))
635 FPuts(Output(), "--- Press a key to continue ---\n");
637 Read(Input(), &buf
, 1);
640 VFPrintf(Output(), " %s: %s (0x%lx) : %lu -> %lu\n", modified
);
644 FPuts(Output(), "-- end of diff\n");
648 static void DeleteStateDiff(struct ResourceDiff
*rd
)
651 struct ModifiedResource
*mr
;
652 struct ModifiedResource
*tmpmr
;
657 for(mr
=(struct ModifiedResource
*)rd
->modifiedOpened
.lh_Head
;
658 mr
->node
.ln_Succ
!=NULL
;
661 tmpmr
= (struct ModifiedResource
*)mr
->node
.ln_Succ
;
662 Remove((struct Node
*)mr
);
670 static struct OpenedResourceNode
* CopyResourcesNode(const struct OpenedResourceNode
*src
)
672 struct OpenedResourceNode
*orn
= get_orn();
675 orn
->name
= StaticStrDup(src
->name
);
676 orn
->addr
= src
->addr
;
677 orn
->count
= src
->count
;
681 static struct TrackedResources
* CopyResourcesState(const struct TrackedResources
*src
)
683 struct TrackedResources
*tr
;
684 struct OpenedResourceNode
*orn
;
690 /* opencount-based stuff */
691 NEWLIST(&tr
->opened
);
693 for(orn
=(struct OpenedResourceNode
*)src
->opened
.lh_Head
;
694 orn
->node
.ln_Succ
!=NULL
;
695 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
697 struct OpenedResourceNode
*nc
;
699 nc
= CopyResourcesNode(orn
);
700 Enqueue(&tr
->opened
, (struct Node
*)nc
);
704 tr
->freeMem
= src
->freeMem
;
713 GadToolsBase
= OpenLibrary ( "gadtools.library", 0L );
718 CloseLibrary ( GadToolsBase
);
723 struct TrackedResources
*crs
= NULL
;
724 struct TrackedResources
*start_rs
= NULL
;
728 struct MsgPort
*port
;
730 port
= CreateMsgPort();
733 port
->mp_Node
.ln_Name
= "LeakWatch";
734 port
->mp_Node
.ln_Pri
= 0;
737 portsig
= 1L << port
->mp_SigBit
;
741 FPuts(Output(), "LeakWatch running, CTRL-C to exit, CTRL-E to watch for leaks since beginning, CTRL-F to watch for leaks since last CTRL-F, CTRL-D for an usage snapshot\n");
743 crs
= NewResourcesState();
747 start_rs
= CopyResourcesState(crs
);
753 signals
= Wait(portsig
| SIGBREAKF_CTRL_F
| SIGBREAKF_CTRL_E
| SIGBREAKF_CTRL_D
| SIGBREAKF_CTRL_C
);
755 if (signals
& SIGBREAKF_CTRL_D
)
757 struct TrackedResources
*tr
;
759 tr
= NewResourcesState();
765 DisplayResourcesState(tr
, numlines
);
766 DeleteResourcesState(tr
);
768 if (signals
& SIGBREAKF_CTRL_E
)
770 struct ResourceDiff
*rd
= NULL
;
772 DeleteResourcesState(crs
);
773 crs
= NewResourcesState();
779 /* DisplayResourcesState(crs); */ /* only for debug */
780 rd
= NewStateDiff(start_rs
, crs
);
781 DisplayStateDiff(rd
, numlines
);
784 if (signals
& SIGBREAKF_CTRL_F
)
786 struct TrackedResources
*ors
= crs
;
787 struct ResourceDiff
*rd
= NULL
;
789 crs
= NewResourcesState();
795 rd
= NewStateDiff(ors
, crs
);
796 DisplayStateDiff(rd
, numlines
);
798 DeleteResourcesState(ors
);
800 if (signals
& SIGBREAKF_CTRL_C
)
804 if (signals
& portsig
)
808 while((msg
= (struct Message
*)GetMsg(port
)))
810 D(bug("Received watch message.\n"));
816 } /* while(!quitme) */
818 DeleteResourcesState(crs
);
819 DeleteResourcesState(start_rs
);