Prefs/ScreenMode: change the way depth is selected
[AROS.git] / workbench / c / LeakWatch.c
blob29fa6ec7f3ff274c9068e467c6d6ecc27e3c6a5a
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 ULONG 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 /* FIXME */
497 struct OpenedResourceNode *orn;
498 IPTR tmp[4];
499 IPTR mem[1];
500 int currentlines;
502 if (!rs)
503 return;
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");
511 currentlines = 3;
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))
523 ULONG buf;
524 currentlines = 0;
525 FPuts(Output(), "--- Press a key to continue ---\n");
526 Flush(Input());
527 Read(Input(), &buf, 1);
528 Flush(Input());
530 VFPrintf(Output(), " %s: %s (0x%lx) : %lu\n", tmp);
531 currentlines++;
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)
543 /* FIXME */
544 struct OpenedResourceNode *orn;
545 struct ResourceDiff *rd;
547 rd = get_rd();
548 if (!rd)
549 return NULL;
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;
558 BOOL seen = FALSE;
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))
568 seen = TRUE;
569 if (orn->count != other->count)
571 struct ModifiedResource *mr = get_mr();
572 if (!mr)
573 return NULL;
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);
584 if (!seen)
586 struct ModifiedResource *mr = get_mr();
587 if (!mr)
588 return NULL;
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;
603 return rd;
606 static void DisplayStateDiff(const struct ResourceDiff *rd, int pagelines)
608 /* FIXME */
609 IPTR mem[2];
610 IPTR modified[5];
611 struct ModifiedResource *mr;
612 int currentlines;
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");
620 currentlines = 3;
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))
633 ULONG buf;
634 currentlines = 0;
635 FPuts(Output(), "--- Press a key to continue ---\n");
636 Flush(Input());
637 Read(Input(), &buf, 1);
638 Flush(Input());
640 VFPrintf(Output(), " %s: %s (0x%lx) : %lu -> %lu\n", modified);
641 currentlines++;
644 FPuts(Output(), "-- end of diff\n");
648 static void DeleteStateDiff(struct ResourceDiff *rd)
650 /* FIXME */
651 struct ModifiedResource *mr;
652 struct ModifiedResource *tmpmr;
654 if (!rd)
655 return;
657 for(mr=(struct ModifiedResource *)rd->modifiedOpened.lh_Head;
658 mr->node.ln_Succ!=NULL;
659 mr=tmpmr)
661 tmpmr = (struct ModifiedResource *)mr->node.ln_Succ;
662 Remove((struct Node *)mr);
664 release_mr(mr);
667 release_rd(rd);
670 static struct OpenedResourceNode * CopyResourcesNode(const struct OpenedResourceNode *src)
672 struct OpenedResourceNode *orn = get_orn();
673 if (!orn)
674 return NULL;
675 orn->name = StaticStrDup(src->name);
676 orn->addr = src->addr;
677 orn->count = src->count;
678 return orn;
681 static struct TrackedResources * CopyResourcesState(const struct TrackedResources *src)
683 struct TrackedResources *tr;
684 struct OpenedResourceNode *orn;
686 tr = get_tr();
687 if (!tr)
688 return NULL;
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);
703 /* memory */
704 tr->freeMem = src->freeMem;
706 return tr;
709 int __nocommandline;
711 void open_libs()
713 GadToolsBase = OpenLibrary ( "gadtools.library", 0L );
716 void close_libs()
718 CloseLibrary ( GadToolsBase );
721 int main(void)
723 struct TrackedResources *crs = NULL;
724 struct TrackedResources *start_rs = NULL;
725 BOOL quitme = FALSE;
726 int numlines = 30;
727 ULONG portsig;
728 struct MsgPort *port;
730 port = CreateMsgPort();
731 if (!port)
732 return 2;
733 port->mp_Node.ln_Name = "LeakWatch";
734 port->mp_Node.ln_Pri = 0;
735 AddPort(port);
737 portsig = 1L << port->mp_SigBit;
739 open_libs();
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();
744 if (NULL == crs)
745 quitme = TRUE;
746 else
747 start_rs = CopyResourcesState(crs);
749 while(!quitme)
751 ULONG signals;
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();
760 if (NULL == tr)
762 quitme = TRUE;
763 break;
765 DisplayResourcesState(tr, numlines);
766 DeleteResourcesState(tr);
768 if (signals & SIGBREAKF_CTRL_E)
770 struct ResourceDiff *rd = NULL;
772 DeleteResourcesState(crs);
773 crs = NewResourcesState();
774 if (NULL == crs)
776 quitme = TRUE;
777 break;
779 /* DisplayResourcesState(crs); */ /* only for debug */
780 rd = NewStateDiff(start_rs, crs);
781 DisplayStateDiff(rd, numlines);
782 DeleteStateDiff(rd);
784 if (signals & SIGBREAKF_CTRL_F)
786 struct TrackedResources *ors = crs;
787 struct ResourceDiff *rd = NULL;
789 crs = NewResourcesState();
790 if (NULL == crs)
792 quitme = TRUE;
793 break;
795 rd = NewStateDiff(ors, crs);
796 DisplayStateDiff(rd, numlines);
797 DeleteStateDiff(rd);
798 DeleteResourcesState(ors);
800 if (signals & SIGBREAKF_CTRL_C)
802 quitme = TRUE;
804 if (signals & portsig)
806 struct Message *msg;
808 while((msg = (struct Message *)GetMsg(port)))
810 D(bug("Received watch message.\n"));
812 ReplyMsg(msg);
816 } /* while(!quitme) */
818 DeleteResourcesState(crs);
819 DeleteResourcesState(start_rs);
821 close_libs();
823 if (port)
825 RemPort(port);
826 DeleteMsgPort(port);
828 return 0;