1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "config.h"
15#include <stdint.h>
16#ifdef HAVE_INTPTR_T
17#define INT2PTR(t,v) ((t)(intptr_t)(v))
18#else
19#define INT2PTR(t,v) ((t)(v))
20#endif
21
22#include "vmhdr.h"
23
24/* Method to help with debugging. This does rigorous checks on
25** addresses and arena integrity.
26**
27** Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
28*/
29
30/* structure to keep track of file names */
31typedef struct _dbfile_s Dbfile_t;
32struct _dbfile_s {
33 Dbfile_t *next;
34 char file[1];
35};
36static Dbfile_t *Dbfile;
37
38/* global watch list */
39#define S_WATCH 32
40static int Dbnwatch;
41static void *Dbwatch[S_WATCH];
42
43/* types of warnings reported by dbwarn() */
44#define DB_CHECK 0
45#define DB_ALLOC 1
46#define DB_FREE 2
47#define DB_RESIZE 3
48#define DB_WATCH 4
49#define DB_RESIZED 5
50
51static int Dbinit = 0;
52#define DBINIT() (Dbinit ? 0 : (dbinit(), Dbinit=1) )
53static void dbinit(void)
54{
55 int fd;
56 if ((fd = vmtrace(-1)) >= 0)
57 vmtrace(fd);
58}
59
60/* just an entry point to make it easy to set break point */
61static void vmdbwarn(Vmalloc_t * vm, char *mesg, int n)
62{
63 reg Vmdata_t *vd = vm->data;
64
65 write(2, mesg, n);
66 if (vd->mode & VM_DBABORT)
67 abort();
68}
69
70/* issue a warning of some type */
71/**
72 * @param vm region holding the block
73 * @param data data block
74 * @param where byte that was corrupted
75 * @param file file where call originates
76 * @param line line number of call
77 * @param type operation being done
78 */
79static void dbwarn(Vmalloc_t * vm, void * data, int where, char *file,
80 int line, int type)
81{
82 char buf[1024], *bufp, *endbuf, *s;
83#define SLOP 64 /* enough for a message and an int */
84
85 DBINIT();
86
87 bufp = buf;
88 endbuf = buf + sizeof(buf);
89
90 if (type == DB_ALLOC)
91 bufp = (*_Vmstrcpy) (bufp, "alloc error", ':');
92 else if (type == DB_FREE)
93 bufp = (*_Vmstrcpy) (bufp, "free error", ':');
94 else if (type == DB_RESIZE)
95 bufp = (*_Vmstrcpy) (bufp, "resize error", ':');
96 else if (type == DB_CHECK)
97 bufp = (*_Vmstrcpy) (bufp, "corrupted data", ':');
98 else if (type == DB_WATCH)
99 bufp = (*_Vmstrcpy) (bufp, "alert", ':');
100
101 /* region info */
102 bufp = (*_Vmstrcpy) (bufp, "region", '=');
103 bufp = (*_Vmstrcpy) (bufp, (*_Vmitoa) (VLONG(vm), 0), ':');
104
105 if (data) {
106 bufp = (*_Vmstrcpy) (bufp, "block", '=');
107 bufp = (*_Vmstrcpy) (bufp, (*_Vmitoa) (VLONG(data), 0), ':');
108 }
109
110 if (!data) {
111 if (where == DB_ALLOC)
112 bufp = (*_Vmstrcpy) (bufp, "can't get memory", ':');
113 else
114 bufp = (*_Vmstrcpy) (bufp, "region is locked", ':');
115 } else if (type == DB_FREE || type == DB_RESIZE) {
116 if (where == 0)
117 bufp = (*_Vmstrcpy) (bufp, "unallocated block", ':');
118 else
119 bufp = (*_Vmstrcpy) (bufp, "already freed", ':');
120 } else if (type == DB_WATCH) {
121 bufp = (*_Vmstrcpy) (bufp, "size", '=');
122 bufp = (*_Vmstrcpy) (bufp, (*_Vmitoa) (DBSIZE(data), -1), ':');
123 if (where == DB_ALLOC)
124 bufp = (*_Vmstrcpy) (bufp, "just allocated", ':');
125 else if (where == DB_FREE)
126 bufp = (*_Vmstrcpy) (bufp, "being freed", ':');
127 else if (where == DB_RESIZE)
128 bufp = (*_Vmstrcpy) (bufp, "being resized", ':');
129 else if (where == DB_RESIZED)
130 bufp = (*_Vmstrcpy) (bufp, "just resized", ':');
131 } else if (type == DB_CHECK) {
132 bufp = (*_Vmstrcpy) (bufp, "bad byte at", '=');
133 bufp =
134 (*_Vmstrcpy) (bufp,
135 (*_Vmitoa) (VLONG(INT2PTR(char *, where)), -1),
136 ':');
137 if ((s = DBFILE(data)) && (bufp + strlen(s) + SLOP) < endbuf) {
138 bufp = (*_Vmstrcpy) (bufp, "allocated at", '=');
139 bufp = (*_Vmstrcpy) (bufp, s, ',');
140 bufp =
141 (*_Vmstrcpy) (bufp,
142 (*_Vmitoa) (VLONG
143 (INT2PTR(char *, DBLINE(data))),
144 -1), ':');
145 }
146 }
147
148 /* location where offending call originates from */
149 if (file && file[0] && line > 0
150 && (bufp + strlen(file) + SLOP) < endbuf) {
151 bufp = (*_Vmstrcpy) (bufp, "detected at", '=');
152 bufp = (*_Vmstrcpy) (bufp, file, ',');
153 bufp =
154 (*_Vmstrcpy) (bufp,
155 (*_Vmitoa) (VLONG(INT2PTR(char *, where)), -1),
156 ':');
157 }
158
159 *bufp++ = '\n';
160 *bufp = '\0';
161
162 vmdbwarn(vm, buf, (bufp - buf));
163}
164
165/* check for watched address and issue warnings */
166static void dbwatch(Vmalloc_t * vm, void * data, char *file, int line,
167 int type)
168{
169 reg int n;
170
171 for (n = Dbnwatch; n >= 0; --n) {
172 if (Dbwatch[n] == data) {
173 dbwarn(vm, data, type, file, line, DB_WATCH);
174 return;
175 }
176 }
177}
178
179/* record information about the block */
180/**
181 * @param data real address not the one from Vmbest
182 * @param size the actual requested size
183 * @param file file where the request came from
184 * @param line and line number
185 */
186static void dbsetinfo(Vmuchar_t * data, size_t size, char *file, int line)
187{
188 reg Vmuchar_t *begp, *endp;
189 reg Dbfile_t *last, *db;
190
191 DBINIT();
192
193 /* find the file structure */
194 if (!file || !file[0])
195 db = NIL(Dbfile_t *);
196 else {
197 for (last = NIL(Dbfile_t *), db = Dbfile; db;
198 last = db, db = db->next)
199 if (strcmp(db->file, file) == 0)
200 break;
201 if (!db) {
202 db = (Dbfile_t *) vmalloc(Vmheap,
203 sizeof(Dbfile_t) + strlen(file));
204 if (db) {
205 (*_Vmstrcpy) (db->file, file, 0);
206 db->next = Dbfile;
207 Dbfile = db->next;
208 }
209 } else if (last) { /* move-to-front heuristic */
210 last->next = db->next;
211 db->next = Dbfile;
212 Dbfile = db->next;
213 }
214 }
215
216 DBSETFL(data, (db ? db->file : NIL(char *)), line);
217 DBSIZE(data) = size;
218 DBSEG(data) = SEG(DBBLOCK(data));
219
220 DBHEAD(data, begp, endp);
221 while (begp < endp)
222 *begp++ = DB_MAGIC;
223 DBTAIL(data, begp, endp);
224 while (begp < endp)
225 *begp++ = DB_MAGIC;
226}
227
228/* Check to see if an address is in some data block of a region.
229** This returns -(offset+1) if block is already freed, +(offset+1)
230** if block is live, 0 if no match.
231*/
232static long dbaddr(Vmalloc_t * vm, void * addr)
233{
234 reg Block_t *b = NULL, *endb = NULL;
235 reg Seg_t *seg;
236 reg Vmuchar_t *data;
237 reg long offset = -1L;
238 reg Vmdata_t *vd = vm->data;
239 reg int local;
240
241 GETLOCAL(vd, local);
242 if (ISLOCK(vd, local) || !addr)
243 return -1L;
244 SETLOCK(vd, local);
245
246 for (seg = vd->seg; seg; seg = seg->next) {
247 b = SEGBLOCK(seg);
248 endb = (Block_t *) (seg->baddr - sizeof(Head_t));
249 if ((Vmuchar_t *) addr > (Vmuchar_t *) b &&
250 (Vmuchar_t *) addr < (Vmuchar_t *) endb)
251 break;
252 }
253 if (!seg)
254 goto done;
255
256 if (local) { /* must be vmfree or vmresize checking address */
257 if (DBSEG(addr) == seg) {
258 b = DBBLOCK(addr);
259 if (ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)))
260 offset = 0;
261 else
262 offset = -2L;
263 }
264 goto done;
265 }
266
267 while (b < endb) {
268 data = (Vmuchar_t *) DATA(b);
269 if ((Vmuchar_t *) addr >= data
270 && (Vmuchar_t *) addr < data + SIZE(b)) {
271 if (ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b))) {
272 data = DB2DEBUG(data);
273 if ((Vmuchar_t *) addr >= data &&
274 (Vmuchar_t *) addr < data + DBSIZE(data))
275 offset = (Vmuchar_t *) addr - data;
276 }
277 goto done;
278 }
279
280 b = (Block_t *) ((Vmuchar_t *) DATA(b) + (SIZE(b) & ~BITS));
281 }
282
283 done:
284 CLRLOCK(vd, local);
285 return offset;
286}
287
288
289static long dbsize(Vmalloc_t * vm, void * addr)
290{
291 reg Block_t *b, *endb;
292 reg Seg_t *seg;
293 reg long size;
294 reg Vmdata_t *vd = vm->data;
295
296 if (ISLOCK(vd, 0))
297 return -1L;
298 SETLOCK(vd, 0);
299
300 size = -1L;
301 for (seg = vd->seg; seg; seg = seg->next) {
302 b = SEGBLOCK(seg);
303 endb = (Block_t *) (seg->baddr - sizeof(Head_t));
304 if ((Vmuchar_t *) addr <= (Vmuchar_t *) b ||
305 (Vmuchar_t *) addr >= (Vmuchar_t *) endb)
306 continue;
307 while (b < endb) {
308 if (addr == (void *) DB2DEBUG(DATA(b))) {
309 if (ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)))
310 size = (long) DBSIZE(addr);
311 goto done;
312 }
313
314 b = (Block_t *) ((Vmuchar_t *) DATA(b) + (SIZE(b) & ~BITS));
315 }
316 }
317 done:
318 CLRLOCK(vd, 0);
319 return size;
320}
321
322static void *dballoc(Vmalloc_t * vm, size_t size)
323{
324 reg size_t s;
325 reg Vmuchar_t *data;
326 reg char *file;
327 reg int line;
328 reg Vmdata_t *vd = vm->data;
329
330 VMFILELINE(vm, file, line);
331
332 if (ISLOCK(vd, 0)) {
333 dbwarn(vm, NIL(Vmuchar_t *), 0, file, line, DB_ALLOC);
334 return NIL(void *);
335 }
336 SETLOCK(vd, 0);
337
338 if (vd->mode & VM_DBCHECK)
339 vmdbcheck(vm);
340
341 s = ROUND(size, ALIGN) + DB_EXTRA;
342 if (s < sizeof(Body_t)) /* no tiny blocks during Vmdebug */
343 s = sizeof(Body_t);
344
345 if (!(data = (Vmuchar_t *) KPVALLOC(vm, s, (*(Vmbest->allocf))))) {
346 dbwarn(vm, NIL(Vmuchar_t *), DB_ALLOC, file, line, DB_ALLOC);
347 goto done;
348 }
349
350 data = DB2DEBUG(data);
351 dbsetinfo(data, size, file, line);
352
353 if ((vd->mode & VM_TRACE) && _Vmtrace) {
354 vm->file = file;
355 vm->line = line;
356 (*_Vmtrace) (vm, NIL(Vmuchar_t *), data, size, 0);
357 }
358
359 if (Dbnwatch > 0)
360 dbwatch(vm, data, file, line, DB_ALLOC);
361
362 done:
363 CLRLOCK(vd, 0);
364 return (void *) data;
365}
366
367
368static int dbfree(Vmalloc_t * vm, void * data)
369{
370 char *file;
371 int line;
372 reg long offset;
373 reg int *ip, *endip;
374 reg Vmdata_t *vd = vm->data;
375
376 VMFILELINE(vm, file, line);
377
378 if (!data)
379 return 0;
380
381 if (ISLOCK(vd, 0)) {
382 dbwarn(vm, NIL(Vmuchar_t *), 0, file, line, DB_FREE);
383 return -1;
384 }
385 SETLOCK(vd, 0);
386
387 if (vd->mode & VM_DBCHECK)
388 vmdbcheck(vm);
389
390 if ((offset = KPVADDR(vm, data, dbaddr)) != 0) {
391 if (vm->disc->exceptf)
392 (void) (*vm->disc->exceptf) (vm, VM_BADADDR, data, vm->disc);
393 dbwarn(vm, (Vmuchar_t *) data, offset == -1L ? 0 : 1, file, line,
394 DB_FREE);
395 CLRLOCK(vd, 0);
396 return -1;
397 }
398
399 if (Dbnwatch > 0)
400 dbwatch(vm, data, file, line, DB_FREE);
401
402 if ((vd->mode & VM_TRACE) && _Vmtrace) {
403 vm->file = file;
404 vm->line = line;
405 (*_Vmtrace) (vm, (Vmuchar_t *) data, NIL(Vmuchar_t *),
406 DBSIZE(data), 0);
407 }
408
409 /* clear free space */
410 ip = (int *) data;
411 endip = ip + (DBSIZE(data) + sizeof(int) - 1) / sizeof(int);
412 while (ip < endip)
413 *ip++ = 0;
414
415 CLRLOCK(vd, 0);
416 return (*(Vmbest->freef)) (vm, (void *) DB2BEST(data));
417}
418
419/* Resizing an existing block */
420/**
421 * @param vm region allocating from
422 * @param addr old block of data
423 * @param size new size
424 * @param type !=0 for movable, >0 for copy
425 */
426static void *dbresize(Vmalloc_t * vm, void * addr, reg size_t size,
427 int type)
428{
429 reg Vmuchar_t *data;
430 reg size_t s, oldsize;
431 reg long offset;
432 char *file, *oldfile;
433 int line, oldline;
434 reg Vmdata_t *vd = vm->data;
435
436 if (!addr) {
437 oldsize = 0;
438 data = (Vmuchar_t *) dballoc(vm, size);
439 goto done;
440 }
441 if (size == 0) {
442 (void) dbfree(vm, addr);
443 return NIL(void *);
444 }
445
446 VMFILELINE(vm, file, line);
447
448 if (ISLOCK(vd, 0)) {
449 dbwarn(vm, NIL(Vmuchar_t *), 0, file, line, DB_RESIZE);
450 return NIL(void *);
451 }
452 SETLOCK(vd, 0);
453
454 if (vd->mode & VM_DBCHECK)
455 vmdbcheck(vm);
456
457 if ((offset = KPVADDR(vm, addr, dbaddr)) != 0) {
458 if (vm->disc->exceptf)
459 (void) (*vm->disc->exceptf) (vm, VM_BADADDR, addr, vm->disc);
460 dbwarn(vm, (Vmuchar_t *) addr, offset == -1L ? 0 : 1, file, line,
461 DB_RESIZE);
462 CLRLOCK(vd, 0);
463 return NIL(void *);
464 }
465
466 if (Dbnwatch > 0)
467 dbwatch(vm, addr, file, line, DB_RESIZE);
468
469 /* Vmbest data block */
470 data = DB2BEST(addr);
471 oldsize = DBSIZE(addr);
472 oldfile = DBFILE(addr);
473 oldline = DBLINE(addr);
474
475 /* do the resize */
476 s = ROUND(size, ALIGN) + DB_EXTRA;
477 if (s < sizeof(Body_t))
478 s = sizeof(Body_t);
479 data = (Vmuchar_t *) KPVRESIZE(vm, (void *) data, s,
480 (type & ~VM_RSZERO),
481 (*(Vmbest->resizef)));
482 if (!data) { /* failed, reset data for old block */
483 dbwarn(vm, NIL(Vmuchar_t *), DB_ALLOC, file, line, DB_RESIZE);
484 dbsetinfo((Vmuchar_t *) addr, oldsize, oldfile, oldline);
485 } else {
486 data = DB2DEBUG(data);
487 dbsetinfo(data, size, file, line);
488
489 if ((vd->mode & VM_TRACE) && _Vmtrace) {
490 vm->file = file;
491 vm->line = line;
492 (*_Vmtrace) (vm, (Vmuchar_t *) addr, data, size, 0);
493 }
494 if (Dbnwatch > 0)
495 dbwatch(vm, data, file, line, DB_RESIZED);
496 }
497
498 CLRLOCK(vd, 0);
499
500 done:if (data && (type & VM_RSZERO) && size > oldsize) {
501 reg Vmuchar_t *d = data + oldsize, *ed = data + size;
502 do {
503 *d++ = 0;
504 } while (d < ed);
505 }
506 return (void *) data;
507}
508
509/* compact any residual free space */
510static int dbcompact(Vmalloc_t * vm)
511{
512 return (*(Vmbest->compactf)) (vm);
513}
514
515/* check for memory overwrites over all live blocks */
516int vmdbcheck(Vmalloc_t * vm)
517{
518 reg Block_t *b, *endb;
519 reg Seg_t *seg;
520 int rv;
521 reg Vmdata_t *vd = vm->data;
522
523 if (!(vd->mode & VM_MTDEBUG))
524 return -1;
525
526 rv = 0;
527 for (seg = vd->seg; seg; seg = seg->next) {
528 b = SEGBLOCK(seg);
529 endb = (Block_t *) (seg->baddr - sizeof(Head_t));
530 while (b < endb) {
531 reg Vmuchar_t *data, *begp, *endp;
532
533 if (ISJUNK(SIZE(b)) || !ISBUSY(SIZE(b)))
534 goto next;
535
536 data = DB2DEBUG(DATA(b));
537 if (DBISBAD(data)) { /* seen this before */
538 rv += 1;
539 goto next;
540 }
541
542 DBHEAD(data, begp, endp);
543 for (; begp < endp; ++begp)
544 if (*begp != DB_MAGIC)
545 goto set_bad;
546
547 DBTAIL(data, begp, endp);
548 for (; begp < endp; ++begp) {
549 if (*begp == DB_MAGIC)
550 continue;
551 set_bad:
552 dbwarn(vm, data, begp - data, NIL(char *), 0, DB_CHECK);
553 DBSETBAD(data);
554 rv += 1;
555 goto next;
556 }
557
558 next:b = (Block_t *) ((Vmuchar_t *) DATA(b) +
559 (SIZE(b) & ~BITS));
560 }
561 }
562
563 return rv;
564}
565
566/* set/delete an address to watch */
567/**
568 * set/delete an address to watch
569 *
570 * @param addr address to insert
571 */
572void *vmdbwatch(void * addr)
573{
574 reg int n;
575 reg void *out;
576
577 out = NIL(void *);
578 if (!addr)
579 Dbnwatch = 0;
580 else {
581 for (n = Dbnwatch - 1; n >= 0; --n)
582 if (Dbwatch[n] == addr)
583 break;
584 if (n < 0) { /* insert */
585 if (Dbnwatch == S_WATCH) { /* delete left-most */
586 out = Dbwatch[0];
587 Dbnwatch -= 1;
588 for (n = 0; n < Dbnwatch; ++n)
589 Dbwatch[n] = Dbwatch[n + 1];
590 }
591 Dbwatch[Dbnwatch] = addr;
592 Dbnwatch += 1;
593 }
594 }
595 return out;
596}
597
598static void *dbalign(Vmalloc_t * vm, size_t size, size_t align)
599{
600 reg Vmuchar_t *data;
601 reg size_t s;
602 reg char *file;
603 reg int line;
604 reg Vmdata_t *vd = vm->data;
605
606 VMFILELINE(vm, file, line);
607
608 if (size <= 0 || align <= 0)
609 return NIL(void *);
610
611 if (!(vd->mode & VM_TRUST)) {
612 if (ISLOCK(vd, 0))
613 return NIL(void *);
614 SETLOCK(vd, 0);
615 }
616
617 if ((s = ROUND(size, ALIGN) + DB_EXTRA) < sizeof(Body_t))
618 s = sizeof(Body_t);
619
620 if (!
621 (data = (Vmuchar_t *) KPVALIGN(vm, s, align, (*(Vmbest->alignf)))))
622 goto done;
623
624 data += DB_HEAD;
625 dbsetinfo(data, size, file, line);
626
627 if ((vd->mode & VM_TRACE) && _Vmtrace) {
628 vm->file = file;
629 vm->line = line;
630 (*_Vmtrace) (vm, NIL(Vmuchar_t *), data, size, align);
631 }
632
633 done:
634 CLRLOCK(vd, 0);
635 return (void *) data;
636}
637
638static Vmethod_t _Vmdebug = {
639 dballoc,
640 dbresize,
641 dbfree,
642 dbaddr,
643 dbsize,
644 dbcompact,
645 dbalign,
646 VM_MTDEBUG
647};
648
649Vmethod_t* Vmdebug = &_Vmdebug;
650