2 Copyright © 2002-2007, The AROS Development Team. All rights reserved.
5 Desc: show deltas of resources usage
9 /* It's like doing avail flush before and after executing a program to detect
10 * memleaks, excepted that this program tries to detect much more leaks.
11 * It has to not leak itself, and not even allocate memory between checks.
12 * That's why lists and nodes are statically allocated (in a system that could
13 * be improved to allow reuse of nodes :) (like using an Allocate-based own
14 * memory allocator using a huge static array as heap)
16 * Use : launch in a new shell, and use ctrl-f whenever you want to check
18 * See the Aminet "Scout" program to see other things to track in system lists.
22 #include <exec/memory.h>
23 #include <exec/tasks.h>
24 #ifndef EXEC_SEMAPHORES
25 # include <exec/semaphores.h>
27 #include <exec/execbase.h>
28 #include <graphics/gfxbase.h>
29 #include <graphics/text.h>
30 #include <intuition/classes.h>
31 #include <proto/exec.h>
32 #include <proto/graphics.h>
33 #include <dos/dosextens.h>
34 #include <proto/dos.h>
35 #include <clib/alib_protos.h>
40 #include <aros/debug.h>
42 const TEXT version
[] = "$VER: LeakWatch 0.1 (25.12.2002)\n";
44 static struct Library
*GadToolsBase
;
46 struct GadToolsBase_intern
48 struct Library library
;
49 struct ExecBase
* sysbase
;
52 struct IntuitionBase
* intuibase
;
53 struct Library
* dosbase
;
54 struct GfxBase
* gfxbase
;
55 struct Library
* layersbase
;
56 struct Library
* utilitybase
;
61 Class
* scrollerclass
;
64 Class
* listviewclass
;
65 Class
* checkboxclass
;
70 /* Semaphore to protect the bevel object. */
71 struct SignalSemaphore bevelsema
;
72 /* Actually an Object *. The image used for bevel boxes. */
74 struct SignalSemaphore classsema
;
79 /* struct arrays : ORN = OpenedResourceNode, TR = TrackedResources, RD = ResourceDiff
80 MR = ModifiedResource STR=stringbuf
85 #define MAX_STR 300000
88 /* All resources that have an opencount, eg. libs, devs, fonts */
89 struct OpenedResourceNode
{
97 /* All resources that LeakWatch handles. */
98 struct TrackedResources
{
99 struct List opened
; /* List of OpenedResourceNode */
100 ULONG freeMem
; /* total free memory */
103 struct ModifiedResource
{
112 /* store eg. new libraries, or libraries with modified opencount
115 struct ResourceDiff
{
117 struct List modifiedOpened
; /* contains ModifiedResource */
120 /* static storage to avoid interfering with memleaks debugging */
121 static struct OpenedResourceNode _ornbuf
[MAX_ORN
];
122 static struct TrackedResources _trbuf
[MAX_TR
];
123 static struct ResourceDiff _rdbuf
[MAX_RD
];
124 static UBYTE _strbuf
[MAX_STR
];
125 static struct ModifiedResource _mrbuf
[MAX_MR
];
127 static int next_orn
= 0;
128 static int next_tr
= 0;
129 static int next_rd
= 0;
130 static int next_str
= 0;
131 static int next_mr
= 0;
133 static struct OpenedResourceNode
*get_orn()
135 if (next_orn
== MAX_ORN
)
137 return &_ornbuf
[next_orn
++];
140 static void release_orn (struct OpenedResourceNode
*orn
)
144 static struct TrackedResources
*get_tr()
146 if (next_tr
== MAX_TR
)
148 return &_trbuf
[next_tr
++];
151 static void release_tr (struct TrackedResources
*tr
)
155 static struct ResourceDiff
*get_rd()
157 if (next_rd
== MAX_RD
)
159 return &_rdbuf
[next_rd
++];
162 static void release_rd (struct ResourceDiff
*rd
)
166 static struct ModifiedResource
*get_mr()
168 if (next_mr
== MAX_MR
)
170 return &_mrbuf
[next_mr
++];
173 static void release_mr (struct ModifiedResource
*mr
)
177 CONST_STRPTR
StaticStrDup (CONST_STRPTR str
)
179 UBYTE
*start
= &_strbuf
[next_str
];
187 if (len
+ next_str
+ 1 > MAX_STR
)
190 while ((*t
++ = *str
++))
192 next_str
+= t
- start
;
193 return (CONST_STRPTR
)start
;
196 void StrFree (CONST_STRPTR str
)
200 /***********************************************************************/
202 static struct TrackedResources
*NewResourcesState(void);
203 static void DeleteResourcesState(struct TrackedResources
*rs
);
204 static struct ResourceDiff
*NewStateDiff(const struct TrackedResources
*old
,
205 const struct TrackedResources
*new);
206 static void DisplayStateDiff(const struct ResourceDiff
*rd
, int pagelines
);
207 static void DeleteStateDiff(struct ResourceDiff
*rd
);
208 static struct TrackedResources
* CopyResourcesState(const struct TrackedResources
*src
);
210 static BOOL
AddLibs(struct List
*opened
)
215 for(lib
=(struct Library
*)SysBase
->LibList
.lh_Head
;
216 lib
->lib_Node
.ln_Succ
!=NULL
;
217 lib
=(struct Library
*)lib
->lib_Node
.ln_Succ
)
219 struct OpenedResourceNode
*orn
= get_orn();
225 orn
->type
= "Library";
226 orn
->name
= StaticStrDup(lib
->lib_Node
.ln_Name
);
228 orn
->count
= lib
->lib_OpenCnt
;
229 Enqueue(opened
, (struct Node
*)orn
);
235 static BOOL
AddDevs(struct List
*opened
)
240 for(dev
=(struct Device
*)SysBase
->DeviceList
.lh_Head
;
241 dev
->dd_Library
.lib_Node
.ln_Succ
!=NULL
;
242 dev
=(struct Device
*)dev
->dd_Library
.lib_Node
.ln_Succ
)
244 struct OpenedResourceNode
*orn
= get_orn();
250 orn
->type
= "Device";
251 orn
->name
= StaticStrDup(dev
->dd_Library
.lib_Node
.ln_Name
);
253 orn
->count
= dev
->dd_Library
.lib_OpenCnt
;
254 Enqueue(opened
, (struct Node
*)orn
);
260 static BOOL
AddFonts(struct List
*opened
)
265 for(tf
=(struct TextFont
*)GfxBase
->TextFonts
.lh_Head
;
266 tf
->tf_Message
.mn_Node
.ln_Succ
!=NULL
;
267 tf
=(struct TextFont
*)tf
->tf_Message
.mn_Node
.ln_Succ
)
269 struct OpenedResourceNode
*orn
= get_orn();
276 orn
->name
= StaticStrDup(tf
->tf_Message
.mn_Node
.ln_Name
);
278 orn
->count
= tf
->tf_Accessors
;
279 Enqueue(opened
, (struct Node
*)orn
);
285 static BOOL
AddNodeNames(struct List
*opened
, struct List
*list
, CONST_STRPTR type
)
290 for(lib
=(struct Node
*)list
->lh_Head
;
292 lib
=(struct Node
*)lib
->ln_Succ
)
294 struct OpenedResourceNode
*orn
= get_orn();
301 orn
->name
= StaticStrDup(lib
->ln_Name
);
304 Enqueue(opened
, (struct Node
*)orn
);
310 static BOOL
AddASemaphore(struct List
*opened
,
311 struct SignalSemaphore
*ss
,
314 struct OpenedResourceNode
*orn
= get_orn();
322 orn
->type
= "Semaphore";
323 orn
->name
= StaticStrDup(name
);
325 orn
->count
= ss
->ss_NestCount
;
328 Enqueue(opened
, (struct Node
*)orn
);
333 static BOOL
AddAClass(struct List
*opened
,
334 Class
*cl
, CONST_STRPTR name
)
336 struct OpenedResourceNode
*orn
;
351 orn
->name
= StaticStrDup(name
);
353 orn
->count
= cl
->cl_ObjectCount
;
356 Enqueue(opened
, (struct Node
*)orn
);
361 /* Add opencount-based resources to the tracking list. */
362 static BOOL
AddOpenedResources(struct List
*opened
)
364 struct DosInfo
*di
= BADDR(DOSBase
->dl_Root
->rn_Info
);
366 if (!AddLibs(opened
))
368 if (!AddDevs(opened
))
370 if (!AddFonts(opened
))
372 if (!AddNodeNames(opened
, &SysBase
->ResourceList
, "Resource"))
374 if (!AddNodeNames(opened
, &SysBase
->IntrList
, "Interrupt"))
376 if (!AddNodeNames(opened
, &SysBase
->PortList
, "Port"))
378 if (!AddNodeNames(opened
, &SysBase
->SemaphoreList
, "Semaphore"))
380 if (!AddASemaphore(opened
, &di
->di_DevLock
, "di_DevLock"))
382 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->buttonclass
, "button"))
384 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->textclass
, "text"))
386 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->sliderclass
, "slider"))
388 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->scrollerclass
, "scroller"))
390 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->arrowclass
, "arrow"))
392 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->stringclass
, "string"))
394 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->listviewclass
, "listview"))
396 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->checkboxclass
, "checkbox"))
398 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->cycleclass
, "cycle"))
400 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->mxclass
, "mx"))
402 if (!AddAClass(opened
, ((struct GadToolsBase_intern
*)GadToolsBase
)->paletteclass
, "palette"))
407 /* Get a snapshot of current resources */
408 static struct TrackedResources
*NewResourcesState(void)
410 struct TrackedResources
*tr
;
417 FreeVec(AllocVec(~0ul/2, MEMF_ANY
));
419 /* opencount-based stuff */
420 NEWLIST(&tr
->opened
);
421 if (!AddOpenedResources(&tr
->opened
))
425 tr
->freeMem
= AvailMem(MEMF_ANY
);
430 static void DeleteResourceNode(struct OpenedResourceNode
*orn
)
434 StrFree((APTR
)orn
->name
);
439 /* Free snapshot of current resources */
440 static void DeleteResourcesState(struct TrackedResources
*rs
)
442 struct OpenedResourceNode
*orn
;
443 struct OpenedResourceNode
*tmp
;
448 for(orn
=(struct OpenedResourceNode
*)rs
->opened
.lh_Head
;
449 orn
->node
.ln_Succ
!=NULL
;
452 tmp
= (struct OpenedResourceNode
*)orn
->node
.ln_Succ
;
453 Remove((struct Node
*)orn
);
454 DeleteResourceNode(orn
);
459 void DisplayResourcesState(const struct TrackedResources
*rs
, int pagelines
)
462 struct OpenedResourceNode
*orn
;
470 FPuts(Output(), "LeakWatch snapshot:\n");
472 mem
[0] = rs
->freeMem
;
473 VFPrintf(Output(), " Free memory : %ld bytes\n", mem
);
475 FPuts(Output(), " Opened resources:\n");
477 for(orn
=(struct OpenedResourceNode
*)rs
->opened
.lh_Head
;
478 orn
->node
.ln_Succ
!=NULL
;
479 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
481 tmp
[0] = (IPTR
)orn
->type
;
482 tmp
[1] = (IPTR
)orn
->name
;
483 tmp
[2] = (IPTR
)orn
->addr
;
484 tmp
[3] = (IPTR
)orn
->count
;
486 if (currentlines
>= (pagelines
- 2))
490 FPuts(Output(), "--- Press a key to continue ---\n");
492 Read(Input(), &buf
, 1);
495 VFPrintf(Output(), " %s: %s (0x%lx) : %lu\n", tmp
);
498 FPuts(Output(), "-- end of state\n");
501 /* Compute the delta between 2 resources snapshots.
502 * the ResourceDiff can have dangling pointers in old and nu, so dont clear
503 * them before being done with rd in the processing loop
505 static struct ResourceDiff
*NewStateDiff(const struct TrackedResources
*old
,
506 const struct TrackedResources
*nu
)
509 struct OpenedResourceNode
*orn
;
510 struct ResourceDiff
*rd
;
516 NEWLIST(&rd
->modifiedOpened
);
518 for(orn
=(struct OpenedResourceNode
*)nu
->opened
.lh_Head
;
519 orn
->node
.ln_Succ
!=NULL
;
520 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
522 struct OpenedResourceNode
*other
;
525 for(other
=(struct OpenedResourceNode
*)old
->opened
.lh_Head
;
526 other
->node
.ln_Succ
!=NULL
;
527 other
=(struct OpenedResourceNode
*)other
->node
.ln_Succ
)
529 if (orn
->addr
== other
->addr
)
531 if (!strcmp(orn
->name
, other
->name
))
534 if (orn
->count
!= other
->count
)
536 struct ModifiedResource
*mr
= get_mr();
539 mr
->type
= other
->type
;
540 mr
->name
= other
->name
;
541 mr
->addr
= other
->addr
;
542 mr
->before_count
= other
->count
;
543 mr
->after_count
= orn
->count
;
544 Enqueue(&rd
->modifiedOpened
, (struct Node
*)mr
);
551 struct ModifiedResource
*mr
= get_mr();
555 mr
->type
= orn
->type
;
556 mr
->name
= orn
->name
;
557 mr
->addr
= orn
->addr
;
558 mr
->before_count
= 0;
559 mr
->after_count
= orn
->count
;
561 Enqueue(&rd
->modifiedOpened
, (struct Node
*)mr
);
566 rd
->memLost
= old
->freeMem
- nu
->freeMem
;
571 static void DisplayStateDiff(const struct ResourceDiff
*rd
, int pagelines
)
576 struct ModifiedResource
*mr
;
579 FPuts(Output(), "LeakWatch report:\n");
580 mem
[0] = rd
->memLost
;
581 mem
[1] = (IPTR
)((rd
->memLost
> 1) ? "s" : "");
582 VFPrintf(Output(), " Memory lost : %ld byte%s\n", mem
);
584 FPuts(Output(), " Open count:\n");
586 for(mr
=(struct ModifiedResource
*)rd
->modifiedOpened
.lh_Head
;
587 mr
->node
.ln_Succ
!=NULL
;
588 mr
=(struct ModifiedResource
*)mr
->node
.ln_Succ
)
590 modified
[0] = (IPTR
)mr
->type
;
591 modified
[1] = (IPTR
)mr
->name
;
592 modified
[2] = (IPTR
)mr
->addr
;
593 modified
[3] = mr
->before_count
;
594 modified
[4] = mr
->after_count
;
596 if (currentlines
>= (pagelines
- 2))
600 FPuts(Output(), "--- Press a key to continue ---\n");
602 Read(Input(), &buf
, 1);
605 VFPrintf(Output(), " %s: %s (0x%lx) : %lu -> %lu\n", modified
);
609 FPuts(Output(), "-- end of diff\n");
613 static void DeleteStateDiff(struct ResourceDiff
*rd
)
616 struct ModifiedResource
*mr
;
617 struct ModifiedResource
*tmpmr
;
622 for(mr
=(struct ModifiedResource
*)rd
->modifiedOpened
.lh_Head
;
623 mr
->node
.ln_Succ
!=NULL
;
626 tmpmr
= (struct ModifiedResource
*)mr
->node
.ln_Succ
;
627 Remove((struct Node
*)mr
);
635 static struct OpenedResourceNode
* CopyResourcesNode(const struct OpenedResourceNode
*src
)
637 struct OpenedResourceNode
*orn
= get_orn();
640 orn
->name
= StaticStrDup(src
->name
);
641 orn
->addr
= src
->addr
;
642 orn
->count
= src
->count
;
646 static struct TrackedResources
* CopyResourcesState(const struct TrackedResources
*src
)
648 struct TrackedResources
*tr
;
649 struct OpenedResourceNode
*orn
;
655 /* opencount-based stuff */
656 NEWLIST(&tr
->opened
);
658 for(orn
=(struct OpenedResourceNode
*)src
->opened
.lh_Head
;
659 orn
->node
.ln_Succ
!=NULL
;
660 orn
=(struct OpenedResourceNode
*)orn
->node
.ln_Succ
)
662 struct OpenedResourceNode
*nc
;
664 nc
= CopyResourcesNode(orn
);
665 Enqueue(&tr
->opened
, (struct Node
*)nc
);
669 tr
->freeMem
= src
->freeMem
;
678 GadToolsBase
= OpenLibrary ( "gadtools.library", 0L );
683 CloseLibrary ( GadToolsBase
);
688 struct TrackedResources
*crs
= NULL
;
689 struct TrackedResources
*start_rs
= NULL
;
694 struct MsgPort
*port
;
696 this = FindTask(NULL
);
698 port
= CreateMsgPort();
701 port
->mp_Node
.ln_Name
= "LeakWatch";
702 port
->mp_Node
.ln_Pri
= 0;
705 portsig
= 1L << port
->mp_SigBit
;
709 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");
711 crs
= NewResourcesState();
715 start_rs
= CopyResourcesState(crs
);
721 signals
= Wait(portsig
| SIGBREAKF_CTRL_F
| SIGBREAKF_CTRL_E
| SIGBREAKF_CTRL_D
| SIGBREAKF_CTRL_C
);
723 if (signals
& SIGBREAKF_CTRL_D
)
725 struct TrackedResources
*tr
;
727 tr
= NewResourcesState();
733 DisplayResourcesState(tr
, numlines
);
734 DeleteResourcesState(tr
);
736 if (signals
& SIGBREAKF_CTRL_E
)
738 struct ResourceDiff
*rd
= NULL
;
740 DeleteResourcesState(crs
);
741 crs
= NewResourcesState();
747 /* DisplayResourcesState(crs); */ /* only for debug */
748 rd
= NewStateDiff(start_rs
, crs
);
749 DisplayStateDiff(rd
, numlines
);
752 if (signals
& SIGBREAKF_CTRL_F
)
754 struct TrackedResources
*ors
= crs
;
755 struct ResourceDiff
*rd
= NULL
;
757 crs
= NewResourcesState();
763 rd
= NewStateDiff(ors
, crs
);
764 DisplayStateDiff(rd
, numlines
);
766 DeleteResourcesState(ors
);
768 if (signals
& SIGBREAKF_CTRL_C
)
772 if (signals
& portsig
)
776 while((msg
= (struct Message
*)GetMsg(port
)))
778 D(bug("Received watch message.\n"));
784 } /* while(!quitme) */
786 DeleteResourcesState(crs
);
787 DeleteResourcesState(start_rs
);