Task's tc_TrapCode is now called when a processor exception occurs.
[cake.git] / workbench / c / LeakWatch.c
blobd6d303d274fde7ad388409aea635811a6018847b
1 /*
2 Copyright © 2002-2007, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: show deltas of resources usage
6 Lang: english
7 */
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
17 * for leak.
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>
26 #endif
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>
37 #include <string.h>
39 #define DEBUG 1
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;
50 BPTR seglist;
52 struct IntuitionBase * intuibase;
53 struct Library * dosbase;
54 struct GfxBase * gfxbase;
55 struct Library * layersbase;
56 struct Library * utilitybase;
58 Class * buttonclass;
59 Class * textclass;
60 Class * sliderclass;
61 Class * scrollerclass;
62 Class * arrowclass;
63 Class * stringclass;
64 Class * listviewclass;
65 Class * checkboxclass;
66 Class * cycleclass;
67 Class * mxclass;
68 Class * paletteclass;
70 /* Semaphore to protect the bevel object. */
71 struct SignalSemaphore bevelsema;
72 /* Actually an Object *. The image used for bevel boxes. */
73 struct Image * bevel;
74 struct SignalSemaphore classsema;
79 /* struct arrays : ORN = OpenedResourceNode, TR = TrackedResources, RD = ResourceDiff
80 MR = ModifiedResource STR=stringbuf
82 #define MAX_ORN 30000
83 #define MAX_TR 3000
84 #define MAX_RD 300
85 #define MAX_STR 300000
86 #define MAX_MR 2000
88 /* All resources that have an opencount, eg. libs, devs, fonts */
89 struct OpenedResourceNode {
90 struct Node node;
91 CONST_STRPTR type;
92 CONST_STRPTR name;
93 APTR addr;
94 ULONG count;
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 {
104 struct Node node;
105 CONST_STRPTR type;
106 CONST_STRPTR name;
107 APTR addr;
108 ULONG before_count;
109 ULONG after_count;
112 /* store eg. new libraries, or libraries with modified opencount
115 struct ResourceDiff {
116 LONG memLost;
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)
136 return NULL;
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)
147 return NULL;
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)
158 return NULL;
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)
169 return NULL;
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];
180 UBYTE *t = start;
181 int len;
183 if (NULL == str)
184 str = "<unnamed>";
186 len = strlen(str);
187 if (len + next_str + 1 > MAX_STR)
188 return NULL;
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)
212 struct Library *lib;
214 Forbid();
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();
220 if (!orn)
222 Permit();
223 return FALSE;
225 orn->type = "Library";
226 orn->name = StaticStrDup(lib->lib_Node.ln_Name);
227 orn->addr = lib;
228 orn->count = lib->lib_OpenCnt;
229 Enqueue(opened, (struct Node *)orn);
231 Permit();
232 return TRUE;
235 static BOOL AddDevs(struct List *opened)
237 struct Device *dev;
239 Forbid();
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();
245 if (!orn)
247 Permit();
248 return FALSE;
250 orn->type = "Device";
251 orn->name = StaticStrDup(dev->dd_Library.lib_Node.ln_Name);
252 orn->addr = dev;
253 orn->count = dev->dd_Library.lib_OpenCnt;
254 Enqueue(opened, (struct Node *)orn);
256 Permit();
257 return TRUE;
260 static BOOL AddFonts(struct List *opened)
262 struct TextFont *tf;
264 Forbid();
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();
270 if (!orn)
272 Permit();
273 return FALSE;
275 orn->type = "Font";
276 orn->name = StaticStrDup(tf->tf_Message.mn_Node.ln_Name);
277 orn->addr = tf;
278 orn->count = tf->tf_Accessors;
279 Enqueue(opened, (struct Node *)orn);
281 Permit();
282 return TRUE;
285 static BOOL AddNodeNames(struct List *opened, struct List *list, CONST_STRPTR type)
287 struct Node *lib;
289 Forbid();
290 for(lib=(struct Node *)list->lh_Head;
291 lib->ln_Succ!=NULL;
292 lib=(struct Node *)lib->ln_Succ)
294 struct OpenedResourceNode *orn = get_orn();
295 if (!orn)
297 Permit();
298 return FALSE;
300 orn->type = type;
301 orn->name = StaticStrDup(lib->ln_Name);
302 orn->addr = lib;
303 orn->count = 0;
304 Enqueue(opened, (struct Node *)orn);
306 Permit();
307 return TRUE;
310 static BOOL AddASemaphore(struct List *opened,
311 struct SignalSemaphore *ss,
312 CONST_STRPTR name)
314 struct OpenedResourceNode *orn = get_orn();
316 if (!orn)
318 return FALSE;
321 Forbid();
322 orn->type = "Semaphore";
323 orn->name = StaticStrDup(name);
324 orn->addr = ss;
325 orn->count = ss->ss_NestCount;
326 Permit();
328 Enqueue(opened, (struct Node *)orn);
329 return TRUE;
333 static BOOL AddAClass(struct List *opened,
334 Class *cl, CONST_STRPTR name)
336 struct OpenedResourceNode *orn;
338 if (NULL == cl)
339 return TRUE;
341 orn = get_orn();
342 if (!orn)
344 return FALSE;
347 Forbid();
348 orn->type = "Class";
349 if (NULL == name)
350 name = cl->cl_ID;
351 orn->name = StaticStrDup(name);
352 orn->addr = cl;
353 orn->count = cl->cl_ObjectCount;
354 Permit();
356 Enqueue(opened, (struct Node *)orn);
357 return TRUE;
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))
367 return FALSE;
368 if (!AddDevs(opened))
369 return FALSE;
370 if (!AddFonts(opened))
371 return FALSE;
372 if (!AddNodeNames(opened, &SysBase->ResourceList, "Resource"))
373 return FALSE;
374 if (!AddNodeNames(opened, &SysBase->IntrList, "Interrupt"))
375 return FALSE;
376 if (!AddNodeNames(opened, &SysBase->PortList, "Port"))
377 return FALSE;
378 if (!AddNodeNames(opened, &SysBase->SemaphoreList, "Semaphore"))
379 return FALSE;
380 if (!AddASemaphore(opened, &di->di_DevLock, "di_DevLock"))
381 return FALSE;
382 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->buttonclass, "button"))
383 return FALSE;
384 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->textclass, "text"))
385 return FALSE;
386 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->sliderclass, "slider"))
387 return FALSE;
388 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->scrollerclass, "scroller"))
389 return FALSE;
390 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->arrowclass, "arrow"))
391 return FALSE;
392 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->stringclass, "string"))
393 return FALSE;
394 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->listviewclass, "listview"))
395 return FALSE;
396 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->checkboxclass, "checkbox"))
397 return FALSE;
398 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->cycleclass, "cycle"))
399 return FALSE;
400 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->mxclass, "mx"))
401 return FALSE;
402 if (!AddAClass(opened, ((struct GadToolsBase_intern *)GadToolsBase)->paletteclass, "palette"))
403 return FALSE;
404 return TRUE;
407 /* Get a snapshot of current resources */
408 static struct TrackedResources *NewResourcesState(void)
410 struct TrackedResources *tr;
412 tr = get_tr();
413 if (!tr)
414 return NULL;
416 /* flush */
417 FreeVec(AllocVec(~0ul/2, MEMF_ANY));
419 /* opencount-based stuff */
420 NEWLIST(&tr->opened);
421 if (!AddOpenedResources(&tr->opened))
422 return NULL;
424 /* memory */
425 tr->freeMem = AvailMem(MEMF_ANY);
427 return tr;
430 static void DeleteResourceNode(struct OpenedResourceNode *orn)
432 if (!orn)
433 return;
434 StrFree((APTR)orn->name);
435 orn->name = NULL;
436 release_orn(orn);
439 /* Free snapshot of current resources */
440 static void DeleteResourcesState(struct TrackedResources *rs)
442 struct OpenedResourceNode *orn;
443 struct OpenedResourceNode *tmp;
445 if (!rs)
446 return;
448 for(orn=(struct OpenedResourceNode *)rs->opened.lh_Head;
449 orn->node.ln_Succ!=NULL;
450 orn=tmp)
452 tmp = (struct OpenedResourceNode *)orn->node.ln_Succ;
453 Remove((struct Node *)orn);
454 DeleteResourceNode(orn);
456 release_tr(rs);
459 void DisplayResourcesState(const struct TrackedResources *rs, int pagelines)
461 /* FIXME */
462 struct OpenedResourceNode *orn;
463 IPTR tmp[4];
464 IPTR mem[1];
465 int currentlines;
467 if (!rs)
468 return;
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");
476 currentlines = 3;
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))
488 ULONG buf;
489 currentlines = 0;
490 FPuts(Output(), "--- Press a key to continue ---\n");
491 Flush(Input());
492 Read(Input(), &buf, 1);
493 Flush(Input());
495 VFPrintf(Output(), " %s: %s (0x%lx) : %lu\n", tmp);
496 currentlines++;
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)
508 /* FIXME */
509 struct OpenedResourceNode *orn;
510 struct ResourceDiff *rd;
512 rd = get_rd();
513 if (!rd)
514 return NULL;
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;
523 BOOL seen = FALSE;
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))
533 seen = TRUE;
534 if (orn->count != other->count)
536 struct ModifiedResource *mr = get_mr();
537 if (!mr)
538 return NULL;
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);
549 if (!seen)
551 struct ModifiedResource *mr = get_mr();
552 if (!mr)
553 return NULL;
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;
568 return rd;
571 static void DisplayStateDiff(const struct ResourceDiff *rd, int pagelines)
573 /* FIXME */
574 IPTR mem[2];
575 IPTR modified[5];
576 struct ModifiedResource *mr;
577 int currentlines;
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");
585 currentlines = 3;
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))
598 ULONG buf;
599 currentlines = 0;
600 FPuts(Output(), "--- Press a key to continue ---\n");
601 Flush(Input());
602 Read(Input(), &buf, 1);
603 Flush(Input());
605 VFPrintf(Output(), " %s: %s (0x%lx) : %lu -> %lu\n", modified);
606 currentlines++;
609 FPuts(Output(), "-- end of diff\n");
613 static void DeleteStateDiff(struct ResourceDiff *rd)
615 /* FIXME */
616 struct ModifiedResource *mr;
617 struct ModifiedResource *tmpmr;
619 if (!rd)
620 return;
622 for(mr=(struct ModifiedResource *)rd->modifiedOpened.lh_Head;
623 mr->node.ln_Succ!=NULL;
624 mr=tmpmr)
626 tmpmr = (struct ModifiedResource *)mr->node.ln_Succ;
627 Remove((struct Node *)mr);
629 release_mr(mr);
632 release_rd(rd);
635 static struct OpenedResourceNode * CopyResourcesNode(const struct OpenedResourceNode *src)
637 struct OpenedResourceNode *orn = get_orn();
638 if (!orn)
639 return NULL;
640 orn->name = StaticStrDup(src->name);
641 orn->addr = src->addr;
642 orn->count = src->count;
643 return orn;
646 static struct TrackedResources * CopyResourcesState(const struct TrackedResources *src)
648 struct TrackedResources *tr;
649 struct OpenedResourceNode *orn;
651 tr = get_tr();
652 if (!tr)
653 return NULL;
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);
668 /* memory */
669 tr->freeMem = src->freeMem;
671 return tr;
674 int __nocommandline;
676 void open_libs()
678 GadToolsBase = OpenLibrary ( "gadtools.library", 0L );
681 void close_libs()
683 CloseLibrary ( GadToolsBase );
686 int main(void)
688 struct TrackedResources *crs = NULL;
689 struct TrackedResources *start_rs = NULL;
690 BOOL quitme = FALSE;
691 int numlines = 30;
692 struct Task *this;
693 ULONG portsig;
694 struct MsgPort *port;
696 this = FindTask(NULL);
698 port = CreateMsgPort();
699 if (!port)
700 return 2;
701 port->mp_Node.ln_Name = "LeakWatch";
702 port->mp_Node.ln_Pri = 0;
703 AddPort(port);
705 portsig = 1L << port->mp_SigBit;
707 open_libs();
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();
712 if (NULL == crs)
713 quitme = TRUE;
714 else
715 start_rs = CopyResourcesState(crs);
717 while(!quitme)
719 ULONG signals;
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();
728 if (NULL == tr)
730 quitme = TRUE;
731 break;
733 DisplayResourcesState(tr, numlines);
734 DeleteResourcesState(tr);
736 if (signals & SIGBREAKF_CTRL_E)
738 struct ResourceDiff *rd = NULL;
740 DeleteResourcesState(crs);
741 crs = NewResourcesState();
742 if (NULL == crs)
744 quitme = TRUE;
745 break;
747 /* DisplayResourcesState(crs); */ /* only for debug */
748 rd = NewStateDiff(start_rs, crs);
749 DisplayStateDiff(rd, numlines);
750 DeleteStateDiff(rd);
752 if (signals & SIGBREAKF_CTRL_F)
754 struct TrackedResources *ors = crs;
755 struct ResourceDiff *rd = NULL;
757 crs = NewResourcesState();
758 if (NULL == crs)
760 quitme = TRUE;
761 break;
763 rd = NewStateDiff(ors, crs);
764 DisplayStateDiff(rd, numlines);
765 DeleteStateDiff(rd);
766 DeleteResourcesState(ors);
768 if (signals & SIGBREAKF_CTRL_C)
770 quitme = TRUE;
772 if (signals & portsig)
774 struct Message *msg;
776 while((msg = (struct Message *)GetMsg(port)))
778 D(bug("Received watch message.\n"));
780 ReplyMsg(msg);
784 } /* while(!quitme) */
786 DeleteResourcesState(crs);
787 DeleteResourcesState(start_rs);
789 close_libs();
791 if (port)
793 RemPort(port);
794 DeleteMsgPort(port);
796 return 0;