chiark / gitweb /
Fix README and mLib.3 a bit.
[mLib] / track.c
1 /* -*-c-*-
2  *
3  * $Id: track.c,v 1.5 2004/04/08 01:36:13 mdw Exp $
4  *
5  * Tracing functions for debugging
6  *
7  * (c) 1998 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Header files ------------------------------------------------------*/
31
32 /* --- ANSI headers --- */
33
34 #include <ctype.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 /* --- Local headers --- */
41
42 #include "trace.h"
43 #include "track.h"
44
45 /*----- Type definitions --------------------------------------------------*/
46
47 /* --- A track block --- *
48  *
49  * This gets prefixed to every block I manage.
50  */
51
52 typedef union block {
53   struct {
54     union block *next;                  /* Link to previous block */
55     union block *prev;                  /* Link to next block */
56     size_t sz;                          /* Size of the block */
57     const char *ctx;                    /* Pointer to allocating context */
58   } x;                                  /* Main data area */
59   long double _ld;                      /* Long double for alignment */
60   void *_p;                             /* Void pointer for alignment */
61 } block;
62
63 /*----- Private state -----------------------------------------------------*/
64
65 /* --- Tracking memory usage --- */
66
67 static unsigned int used = 0;           /* Count of bytes occupied */
68 static block *list;                     /* List of allocated blocks */
69
70 /* --- Trace level for verbose messages --- */
71
72 static unsigned int vLevel = 0;
73
74 /* --- Context tracking --- */
75
76 static track_ctx baseContext = {
77   0, "[unknown context]"
78 };
79
80 static track_ctx *context = &baseContext;
81
82 /*----- Functions provided ------------------------------------------------*/
83
84 /* --- @track_setLevel@ --- *
85  *
86  * Arguments:   @unsigned int l@ = tracing level for allocation messages
87  *
88  * Returns:     ---
89  *
90  * Use:         Sets the trace level for allocation messages.
91  */
92
93 void track_setLevel(unsigned int l)
94 {
95   vLevel = l;
96 }
97
98 /* --- @track_pushContext@ --- *
99  *
100  * Arguments:   @track_ctx *ctx@ = context holder to push
101  *
102  * Returns:     ---
103  *
104  * Use:         Pushes the given context block onto the stack.
105  */
106
107 void track_pushContext(track_ctx *ctx)
108 {
109   ctx->next = context;
110   context = ctx;
111 }
112
113 /* --- @track_popContext@ --- *
114  *
115  * Arguments:   @track_ctx *ctx@ = context holder to pop
116  *
117  * Returns:     ---
118  *
119  * Use:         Removes the given context block from the stack.
120  */
121
122 void track_popContext(track_ctx *ctx)
123 {
124   context = ctx->next;
125 }
126
127 /* --- @track_malloc@ --- *
128  *
129  * Arguments:   @size_t sz@ = size requested
130  *
131  * Returns:     Pointer to allocated space, or null
132  *
133  * Use:         Allocates memory, and tracks how much is allocated.
134  */
135
136 void *track_malloc(size_t sz)
137 {
138   block *q = (malloc)(sz + sizeof(block));
139   if (q) {
140     used += sz;
141     if (vLevel) {
142       trace(vLevel, "(track) allocated %lu at %p in %s",
143             (unsigned long)sz, (void *)(q + 1), context->s);
144     }
145     q->x.sz = sz;
146     q->x.next = list;
147     q->x.prev = 0;
148     q->x.ctx = context->s;
149     if (q->x.next)
150       q->x.next->x.prev = q;
151     list = q;
152     return (q + 1);
153   }
154   return (0);
155 }
156
157 /* --- @track_free@ --- *
158  *
159  * Arguments:   @void *p@ = pointer to an allocated block
160  *
161  * Returns:     ---
162  *
163  * Use:         Frees memory, and tracks how much is still allocated.
164  */
165
166 void track_free(void *p)
167 {
168   block *q;
169
170   if (!p)
171     return;
172   q = (block *)p - 1;
173   if (vLevel) {
174     trace(vLevel, "(track) freed %lu at %p for %s in %s",
175           (unsigned long)q->x.sz, (void *)(q + 1),
176           q->x.ctx, context->s);
177   }
178   if (q->x.next)
179     q->x.next->x.prev = q->x.prev;
180   if (q->x.prev)
181     q->x.prev->x.next = q->x.next;
182   else
183     list = q->x.next;
184   used -= q->x.sz;
185   (free)(q);
186 }
187
188 /* --- @track_realloc@ --- *
189  *
190  * Arguments:   @void *p@ = pointer to an allocated block
191  *              @size_t sz@ = how big it wants to be
192  *
193  * Returns:     Pointer to the new block.
194  *
195  * Use:         Reallocates a block, tracking how much memory is still
196  *              available.
197  */
198
199 void *track_realloc(void *p, size_t sz)
200 {
201   size_t osz;
202   block *q, *qq;
203   if (p) {
204     q = (block *)p - 1;
205     osz = q->x.sz;
206     if (q->x.next)
207       q->x.next->x.prev = q->x.prev;
208     if (q->x.prev)
209       q->x.prev->x.next = q->x.next;
210     else
211       list = q->x.next;
212   } else {
213     q = 0;
214     osz = 0;
215   }
216   qq = (realloc)(q, sz + sizeof(block));
217   if (qq) {
218     if (vLevel) {
219       trace(vLevel,
220             "(track) reallocated %lu at %p to %lu for %s in %s",
221             (unsigned long)osz, (void *)(q + 1),
222             (unsigned long)sz,  (void *)(qq + 1),
223             qq->x.ctx, context->s);
224     }
225     qq->x.sz = sz;
226     qq->x.next = list;
227     qq->x.prev = 0;
228     if (qq->x.next)
229       qq->x.next->x.prev = qq;
230     list = qq;
231     used += sz - osz;
232     qq->x.sz = sz;
233     return (qq + 1);
234   }
235   return (0);
236 }
237
238 /* --- @track_used@ --- *
239  *
240  * Arguments:   ---
241  *
242  * Returns:     A count of how much memory is used currently.
243  *
244  * Use:         Returns the amount of memory which the @track_@-functions
245  *              above have counted as being currently allocated.
246  */
247
248 unsigned long track_used(void)
249 {
250   return (used);
251 }
252
253 /* --- @track_list@ --- *
254  *
255  * Arguments:   @unsigned int l@ = trace level to use
256  *
257  * Returns:     ---
258  *
259  * Use:         Traces a dump of the currently known blocks.  Combined with
260  *              a verbose dump of allocations and deallocations, and a
261  *              good idea of which blocks were allocated where, this can
262  *              be useful for locating memory leaks.  It's not exactly a
263  *              picnic, though.
264  */
265
266 void track_list(unsigned int l)
267 {
268   block *q = list;
269
270   if (!(tracing() & l))
271     return;
272
273   trace(l, "(track dump) Dumping all blocks.  Stand well back...");
274   while (q) {
275     trace(l, "(track dump) %p: %lu in %s",
276           (void *)(q + 1), (unsigned long)q->x.sz, q->x.ctx);
277     q = q->x.next;
278   }
279 }
280
281 /*----- That's all, folks -------------------------------------------------*/