disable float support on m68k to prevent unwanted symbols being used in rom code...
[AROS.git] / workbench / c / LeakWatch.c
blob1d1667f958cdd3f91b2fea933242568f6a3b503e
1 /*
2 Copyright © 2002-2013, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: show deltas of resources usage
6 Lang: english
7 */
9 /******************************************************************************
12 NAME
14 LeakWatch
16 SYNOPSIS
18 (N/A)
20 LOCATION
24 FUNCTION
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.
31 INPUTS
33 RESULT
35 NOTES
37 EXAMPLE
39 BUGS
41 SEE ALSO
43 INTERNALS
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)
50 HISTORY
52 ******************************************************************************/
54 #include <exec/memory.h>
55 #include <exec/tasks.h>
56 #ifndef EXEC_SEMAPHORES
57 # include <exec/semaphores.h>
58 #endif
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>
69 #include <string.h>
71 #define DEBUG 1
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
80 struct Library lib;
82 Class * buttonclass;
83 Class * textclass;
84 Class * sliderclass;
85 Class * scrollerclass;
86 Class * arrowclass;
87 Class * stringclass;
88 Class * listviewclass;
89 Class * checkboxclass;
90 Class * cycleclass;
91 Class * mxclass;
92 Class * paletteclass;
94 /* Semaphore to protect the bevel object. */
95 struct SignalSemaphore bevelsema;
96 /* Actually an Object *. The image used for bevel boxes. */
97 struct Image * bevel;
99 /* RenderHook for GTListView class */
100 struct Hook lv_RenderHook;
102 /* Seglist pointer */
103 BPTR gt_SegList;
105 /* Required libraies */
106 APTR gt_IntuitionBase;
107 APTR gt_UtilityBase;
108 APTR gt_GfxBase;
109 APTR gt_LayersBase;
114 /* struct arrays : ORN = OpenedResourceNode, TR = TrackedResources, RD = ResourceDiff
115 MR = ModifiedResource STR=stringbuf
117 #define MAX_ORN 30000
118 #define MAX_TR 3000
119 #define MAX_RD 300
120 #define MAX_STR 300000
121 #define MAX_MR 2000
123 /* All resources that have an opencount, eg. libs, devs, fonts */
124 struct OpenedResourceNode {
125 struct Node node;
126 CONST_STRPTR type;
127 CONST_STRPTR name;
128 APTR addr;
129 ULONG count;
132 /* All resources that LeakWatch handles. */
133 struct TrackedResources {
134 struct List opened; /* List of OpenedResourceNode */
135 IPTR freeMem; /* total free memory */
138 struct ModifiedResource {
139 struct Node node;
140 CONST_STRPTR type;
141 CONST_STRPTR name;
142 APTR addr;
143 ULONG before_count;
144 ULONG after_count;
147 /* store eg. new libraries, or libraries with modified opencount
150 struct ResourceDiff {
151 LONG memLost;
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)
171 return NULL;
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)
182 return NULL;
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)
193 return NULL;
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)
204 return NULL;
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];
215 UBYTE *t = start;
216 int len;
218 if (NULL == str)
219 str = "<unnamed>";
221 len = strlen(str);
222 if (len + next_str + 1 > MAX_STR)
223 return NULL;
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)
247 struct Library *lib;
249 Forbid();
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();
255 if (!orn)
257 Permit();
258 return FALSE;
260 orn->type = "Library";
261 orn->name = StaticStrDup(lib->lib_Node.ln_Name);
262 orn->addr = lib;
263 orn->count = lib->lib_OpenCnt;
264 Enqueue(opened, (struct Node *)orn);
266 Permit();
267 return TRUE;
270 static BOOL AddDevs(struct List *opened)
272 struct Device *dev;
274 Forbid();
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();
280 if (!orn)
282 Permit();
283 return FALSE;
285 orn->type = "Device";
286 orn->name = StaticStrDup(dev->dd_Library.lib_Node.ln_Name);
287 orn->addr = dev;
288 orn->count = dev->dd_Library.lib_OpenCnt;
289 Enqueue(opened, (struct Node *)orn);
291 Permit();
292 return TRUE;
295 static BOOL AddFonts(struct List *opened)
297 struct TextFont *tf;
299 Forbid();
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();
305 if (!orn)
307 Permit();
308 return FALSE;
310 orn->type = "Font";
311 orn->name = StaticStrDup(tf->tf_Message.mn_Node.ln_Name);
312 orn->addr = tf;
313 orn->count = tf->tf_Accessors;
314 Enqueue(opened, (struct Node *)orn);
316 Permit();
317 return TRUE;
320 static BOOL AddNodeNames(struct List *opened, struct List *list, CONST_STRPTR type)
322 struct Node *lib;
324 Forbid();
325 for(lib=(struct Node *)list->lh_Head;
326 lib->ln_Succ!=NULL;
327 lib=(struct Node *)lib->ln_Succ)
329 struct OpenedResourceNode *orn = get_orn();
330 if (!orn)
332 Permit();
333 return FALSE;
335 orn->type = type;
336 orn->name = StaticStrDup(lib->ln_Name);
337 orn->addr = lib;
338 orn->count = 0;
339 Enqueue(opened, (struct Node *)orn);
341 Permit();
342 return TRUE;
345 static BOOL AddASemaphore(struct List *opened,
346 struct SignalSemaphore *ss,
347 CONST_STRPTR name)
349 struct OpenedResourceNode *orn = get_orn();
351 if (!orn)
353 return FALSE;
356 Forbid();
357 orn->type = "Semaphore";
358 orn->name = StaticStrDup(name);
359 orn->addr = ss;
360 orn->count = ss->ss_NestCount;
361 Permit();
363 Enqueue(opened, (struct Node *)orn);
364 return TRUE;
368 static BOOL AddAClass(struct List *opened,
369 Class *cl, CONST_STRPTR name)
371 struct OpenedResourceNode *orn;
373 if (NULL == cl)
374 return TRUE;
376 orn = get_orn();
377 if (!orn)
379 return FALSE;
382 Forbid();
383 orn->type = "Class";
384 if (NULL == name)
385 name = cl->cl_ID;
386 orn->name = StaticStrDup(name);
387 orn->addr = cl;
388 orn->count = cl->cl_ObjectCount;
389 Permit();
391 Enqueue(opened, (struct Node *)orn);
392 return TRUE;
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))
402 return FALSE;
403 if (!AddDevs(opened))
404 return FALSE;
405 if (!AddFonts(opened))
406 return FALSE;
407 if (!AddNodeNames(opened, &SysBase->ResourceList, "Resource"))
408 return FALSE;
409 if (!AddNodeNames(opened, &SysBase->IntrList, "Interrupt"))
410 return FALSE;
411 if (!AddNodeNames(opened, &SysBase->PortList, "Port"))
412 return FALSE;
413 if (!AddNodeNames(opened, &SysBase->SemaphoreList, "Semaphore"))
414 return FALSE;
415 if (!AddASemaphore(opened, &di->di_DevLock, "di_DevLock"))
416 return FALSE;
417 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->buttonclass, "button"))
418 return FALSE;
419 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->textclass, "text"))
420 return FALSE;
421 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->sliderclass, "slider"))
422 return FALSE;
423 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->scrollerclass, "scroller"))
424 return FALSE;
425 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->arrowclass, "arrow"))
426 return FALSE;
427 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->stringclass, "string"))
428 return FALSE;
429 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->listviewclass, "listview"))
430 return FALSE;
431 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->checkboxclass, "checkbox"))
432 return FALSE;
433 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->cycleclass, "cycle"))
434 return FALSE;
435 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->mxclass, "mx"))
436 return FALSE;
437 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->paletteclass, "palette"))
438 return FALSE;
439 return TRUE;
442 /* Get a snapshot of current resources */
443 static struct TrackedResources *NewResourcesState(void)
445 struct TrackedResources *tr;
447 tr = get_tr();
448 if (!tr)
449 return NULL;
451 /* flush */
452 FreeVec(AllocVec((ULONG)(~0ul/2), MEMF_ANY));
454 /* opencount-based stuff */
455 NEWLIST(&tr->opened);
456 if (!AddOpenedResources(&tr->opened))
457 return NULL;
459 /* memory */
460 tr->freeMem = AvailMem(MEMF_ANY);
462 return tr;
465 static void DeleteResourceNode(struct OpenedResourceNode *orn)
467 if (!orn)
468 return;
469 StrFree((APTR)orn->name);
470 orn->name = NULL;
471 release_orn(orn);
474 /* Free snapshot of current resources */
475 static void DeleteResourcesState(struct TrackedResources *rs)
477 struct OpenedResourceNode *orn;
478 struct OpenedResourceNode *tmp;
480 if (!rs)
481 return;
483 for(orn=(struct OpenedResourceNode *)rs->opened.lh_Head;
484 orn->node.ln_Succ!=NULL;
485 orn=tmp)
487 tmp = (struct OpenedResourceNode *)orn->node.ln_Succ;
488 Remove((struct Node *)orn);
489 DeleteResourceNode(orn);
491 release_tr(rs);
494 void DisplayResourcesState(const struct TrackedResources *rs, int pagelines)
496 struct OpenedResourceNode *orn;
497 int currentlines;
499 if (!rs)
500 return;
502 FPuts(Output(), "LeakWatch snapshot:\n");
504 FPrintf(Output(), " Free memory : %id bytes\n", rs->freeMem);
506 FPuts(Output(), " Opened resources:\n");
507 currentlines = 3;
508 for(orn=(struct OpenedResourceNode *)rs->opened.lh_Head;
509 orn->node.ln_Succ!=NULL;
510 orn=(struct OpenedResourceNode *)orn->node.ln_Succ)
512 if (currentlines >= (pagelines - 2))
514 ULONG buf;
515 currentlines = 0;
516 FPuts(Output(), "--- Press a key to continue ---\n");
517 Flush(Input());
518 Read(Input(), &buf, 1);
519 Flush(Input());
521 FPrintf(Output(), " %s: %s (0x%ix) : %lu\n",
522 orn->type, orn->name, orn->addr, orn->count);
523 currentlines++;
525 FPuts(Output(), "-- end of state\n");
528 /* Compute the delta between 2 resources snapshots.
529 * the ResourceDiff can have dangling pointers in old and nu, so dont clear
530 * them before being done with rd in the processing loop
532 static struct ResourceDiff *NewStateDiff(const struct TrackedResources *old,
533 const struct TrackedResources *nu)
535 /* FIXME */
536 struct OpenedResourceNode *orn;
537 struct ResourceDiff *rd;
539 rd = get_rd();
540 if (!rd)
541 return NULL;
543 NEWLIST(&rd->modifiedOpened);
545 for(orn=(struct OpenedResourceNode *)nu->opened.lh_Head;
546 orn->node.ln_Succ!=NULL;
547 orn=(struct OpenedResourceNode *)orn->node.ln_Succ)
549 struct OpenedResourceNode *other;
550 BOOL seen = FALSE;
552 for(other=(struct OpenedResourceNode *)old->opened.lh_Head;
553 other->node.ln_Succ!=NULL;
554 other=(struct OpenedResourceNode *)other->node.ln_Succ)
556 if (orn->addr == other->addr)
558 if (!strcmp(orn->name, other->name))
560 seen = TRUE;
561 if (orn->count != other->count)
563 struct ModifiedResource *mr = get_mr();
564 if (!mr)
565 return NULL;
566 mr->type = other->type;
567 mr->name = other->name;
568 mr->addr = other->addr;
569 mr->before_count = other->count;
570 mr->after_count = orn->count;
571 Enqueue(&rd->modifiedOpened, (struct Node *)mr);
576 if (!seen)
578 struct ModifiedResource *mr = get_mr();
579 if (!mr)
580 return NULL;
582 mr->type = orn->type;
583 mr->name = orn->name;
584 mr->addr = orn->addr;
585 mr->before_count = 0;
586 mr->after_count = orn->count;
588 Enqueue(&rd->modifiedOpened, (struct Node *)mr);
593 rd->memLost = old->freeMem - nu->freeMem;
595 return rd;
598 static void DisplayStateDiff(const struct ResourceDiff *rd, int pagelines)
600 struct ModifiedResource *mr;
601 int currentlines;
603 FPuts(Output(), "LeakWatch report:\n");
604 FPrintf(Output(), " Memory lost : %ld byte%s\n", rd->memLost, (rd->memLost > 1) ? "s" : "");
606 FPuts(Output(), " Open count:\n");
607 currentlines = 3;
608 for(mr=(struct ModifiedResource *)rd->modifiedOpened.lh_Head;
609 mr->node.ln_Succ!=NULL;
610 mr=(struct ModifiedResource *)mr->node.ln_Succ)
612 if (currentlines >= (pagelines - 2))
614 ULONG buf;
615 currentlines = 0;
616 FPuts(Output(), "--- Press a key to continue ---\n");
617 Flush(Input());
618 Read(Input(), &buf, 1);
619 Flush(Input());
621 FPrintf(Output(), " %s: %s (0x%ix) : %lu -> %lu\n",
622 mr->type, mr->name, mr->addr, mr->before_count,
623 mr->after_count);
624 currentlines++;
627 FPuts(Output(), "-- end of diff\n");
631 static void DeleteStateDiff(struct ResourceDiff *rd)
633 /* FIXME */
634 struct ModifiedResource *mr;
635 struct ModifiedResource *tmpmr;
637 if (!rd)
638 return;
640 for(mr=(struct ModifiedResource *)rd->modifiedOpened.lh_Head;
641 mr->node.ln_Succ!=NULL;
642 mr=tmpmr)
644 tmpmr = (struct ModifiedResource *)mr->node.ln_Succ;
645 Remove((struct Node *)mr);
647 release_mr(mr);
650 release_rd(rd);
653 static struct OpenedResourceNode * CopyResourcesNode(const struct OpenedResourceNode *src)
655 struct OpenedResourceNode *orn = get_orn();
656 if (!orn)
657 return NULL;
658 orn->name = StaticStrDup(src->name);
659 orn->addr = src->addr;
660 orn->count = src->count;
661 return orn;
664 static struct TrackedResources * CopyResourcesState(const struct TrackedResources *src)
666 struct TrackedResources *tr;
667 struct OpenedResourceNode *orn;
669 tr = get_tr();
670 if (!tr)
671 return NULL;
673 /* opencount-based stuff */
674 NEWLIST(&tr->opened);
676 for(orn=(struct OpenedResourceNode *)src->opened.lh_Head;
677 orn->node.ln_Succ!=NULL;
678 orn=(struct OpenedResourceNode *)orn->node.ln_Succ)
680 struct OpenedResourceNode *nc;
682 nc = CopyResourcesNode(orn);
683 Enqueue(&tr->opened, (struct Node *)nc);
686 /* memory */
687 tr->freeMem = src->freeMem;
689 return tr;
692 int __nocommandline;
694 void open_libs()
696 GadToolsBase = OpenLibrary ( "gadtools.library", 0L );
699 void close_libs()
701 CloseLibrary ( GadToolsBase );
704 int main(void)
706 struct TrackedResources *crs = NULL;
707 struct TrackedResources *start_rs = NULL;
708 BOOL quitme = FALSE;
709 int numlines = 30;
710 ULONG portsig;
711 struct MsgPort *port;
713 port = CreateMsgPort();
714 if (!port)
715 return 2;
716 port->mp_Node.ln_Name = "LeakWatch";
717 port->mp_Node.ln_Pri = 0;
718 AddPort(port);
720 portsig = 1L << port->mp_SigBit;
722 open_libs();
724 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");
726 crs = NewResourcesState();
727 if (NULL == crs)
728 quitme = TRUE;
729 else
730 start_rs = CopyResourcesState(crs);
732 while(!quitme)
734 ULONG signals;
736 signals = Wait(portsig | SIGBREAKF_CTRL_F | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_C);
738 if (signals & SIGBREAKF_CTRL_D)
740 struct TrackedResources *tr;
742 tr = NewResourcesState();
743 if (NULL == tr)
745 quitme = TRUE;
746 break;
748 DisplayResourcesState(tr, numlines);
749 DeleteResourcesState(tr);
751 if (signals & SIGBREAKF_CTRL_E)
753 struct ResourceDiff *rd = NULL;
755 DeleteResourcesState(crs);
756 crs = NewResourcesState();
757 if (NULL == crs)
759 quitme = TRUE;
760 break;
762 /* DisplayResourcesState(crs); */ /* only for debug */
763 rd = NewStateDiff(start_rs, crs);
764 DisplayStateDiff(rd, numlines);
765 DeleteStateDiff(rd);
767 if (signals & SIGBREAKF_CTRL_F)
769 struct TrackedResources *ors = crs;
770 struct ResourceDiff *rd = NULL;
772 crs = NewResourcesState();
773 if (NULL == crs)
775 quitme = TRUE;
776 break;
778 rd = NewStateDiff(ors, crs);
779 DisplayStateDiff(rd, numlines);
780 DeleteStateDiff(rd);
781 DeleteResourcesState(ors);
783 if (signals & SIGBREAKF_CTRL_C)
785 quitme = TRUE;
787 if (signals & portsig)
789 struct Message *msg;
791 while((msg = (struct Message *)GetMsg(port)))
793 D(bug("Received watch message.\n"));
795 ReplyMsg(msg);
799 } /* while(!quitme) */
801 DeleteResourcesState(crs);
802 DeleteResourcesState(start_rs);
804 close_libs();
806 if (port)
808 RemPort(port);
809 DeleteMsgPort(port);
811 return 0;