[BACK]Return to mandoc_dbg.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/mandoc_dbg.c, Revision 1.1

1.1     ! schwarze    1: /* $Id$ */
        !             2: /*
        !             3:  * Copyright (c) 2021, 2022 Ingo Schwarze <schwarze@openbsd.org>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #include "config.h"
        !            18:
        !            19: #include <sys/types.h>
        !            20:
        !            21: #if HAVE_ERR
        !            22: #include <err.h>
        !            23: #endif
        !            24: #include <stdarg.h>
        !            25: #include <stddef.h>
        !            26: #include <stdint.h>
        !            27: #include <stdio.h>
        !            28: #include <stdlib.h>
        !            29: #include <string.h>
        !            30: #include <unistd.h>
        !            31:
        !            32: #if HAVE_OHASH
        !            33: #include <ohash.h>
        !            34: #else
        !            35: #include "compat_ohash.h"
        !            36: #endif
        !            37:
        !            38: #define DEBUG_NODEF 1
        !            39: #include "mandoc_aux.h"
        !            40: #include "mandoc.h"
        !            41:
        !            42: /* Store information about one allocation. */
        !            43: struct dhash_entry {
        !            44:        const char      *file;
        !            45:        int              line;
        !            46:        const char      *func;
        !            47:        size_t           num;
        !            48:        size_t           size;
        !            49:        void            *ptr;
        !            50: };
        !            51:
        !            52: /* Store information about all allocations. */
        !            53: static struct ohash      dhash_table;
        !            54: static FILE             *dhash_fp;
        !            55: static int               dhash_aflag;
        !            56: static int               dhash_fflag;
        !            57: static int               dhash_lflag;
        !            58: static int               dhash_nflag;
        !            59: static int               dhash_sflag;
        !            60:
        !            61: static void             *dhash_alloc(size_t, void *);
        !            62: static void             *dhash_calloc(size_t, size_t, void *);
        !            63: static void              dhash_free(void *, void *);
        !            64: static unsigned int      dhash_slot(void *);
        !            65: static void              dhash_register(const char *, int, const char *,
        !            66:                                size_t, size_t, void *, const char *);
        !            67: static void              dhash_print(struct dhash_entry *);
        !            68: static void              dhash_purge(const char *, int, const char *, void *);
        !            69:
        !            70:
        !            71: /* *** Debugging wrappers of public API functions. ************************ */
        !            72:
        !            73: int
        !            74: mandoc_dbg_asprintf(const char *file, int line,
        !            75:     char **dest, const char *fmt, ...)
        !            76: {
        !            77:        va_list  ap;
        !            78:        int      ret;
        !            79:
        !            80:        va_start(ap, fmt);
        !            81:        ret = vasprintf(dest, fmt, ap);
        !            82:        va_end(ap);
        !            83:
        !            84:        if (ret == -1)
        !            85:                err((int)MANDOCLEVEL_SYSERR, NULL);
        !            86:
        !            87:        dhash_register(file, line, "asprintf", 1, strlen(*dest) + 1,
        !            88:            *dest, *dest);
        !            89:
        !            90:        return ret;
        !            91: }
        !            92:
        !            93: void *
        !            94: mandoc_dbg_calloc(size_t num, size_t size, const char *file, int line)
        !            95: {
        !            96:        void *ptr = mandoc_calloc(num, size);
        !            97:        dhash_register(file, line, "calloc", num, size, ptr, NULL);
        !            98:        return ptr;
        !            99: }
        !           100:
        !           101: void *
        !           102: mandoc_dbg_malloc(size_t size, const char *file, int line)
        !           103: {
        !           104:        void *ptr = mandoc_malloc(size);
        !           105:        dhash_register(file, line, "malloc", 1, size, ptr, NULL);
        !           106:        return ptr;
        !           107: }
        !           108:
        !           109: void *
        !           110: mandoc_dbg_realloc(void *ptr, size_t size, const char *file, int line)
        !           111: {
        !           112:        dhash_purge(file, line, "realloc", ptr);
        !           113:        ptr = mandoc_realloc(ptr, size);
        !           114:        dhash_register(file, line, "realloc", 1, size, ptr, NULL);
        !           115:        return ptr;
        !           116: }
        !           117:
        !           118: void *
        !           119: mandoc_dbg_reallocarray(void *ptr, size_t num, size_t size,
        !           120:     const char *file, int line)
        !           121: {
        !           122:        dhash_purge(file, line, "reallocarray", ptr);
        !           123:        ptr = mandoc_reallocarray(ptr, num, size);
        !           124:        dhash_register(file, line, "reallocarray", num, size, ptr, NULL);
        !           125:        return ptr;
        !           126: }
        !           127:
        !           128: void *
        !           129: mandoc_dbg_recallocarray(void *ptr, size_t oldnum, size_t num, size_t size,
        !           130:     const char *file, int line)
        !           131: {
        !           132:        dhash_purge(file, line, "recallocarray", ptr);
        !           133:        ptr = mandoc_recallocarray(ptr, oldnum, num, size);
        !           134:        dhash_register(file, line, "recallocarray", num, size, ptr, NULL);
        !           135:        return ptr;
        !           136: }
        !           137:
        !           138: char *
        !           139: mandoc_dbg_strdup(const char *ptr, const char *file, int line)
        !           140: {
        !           141:        char *p = mandoc_strdup(ptr);
        !           142:        dhash_register(file, line, "strdup", 1, strlen(p) + 1, p, ptr);
        !           143:        return p;
        !           144: }
        !           145:
        !           146: char *
        !           147: mandoc_dbg_strndup(const char *ptr, size_t sz, const char *file, int line)
        !           148: {
        !           149:        char *p = mandoc_strndup(ptr, sz);
        !           150:        dhash_register(file, line, "strndup", 1, strlen(p) + 1, p, NULL);
        !           151:        return p;
        !           152: }
        !           153:
        !           154: void
        !           155: mandoc_dbg_free(void *ptr, const char *file, int line)
        !           156: {
        !           157:        dhash_purge(file, line, "free", ptr);
        !           158:        free(ptr);
        !           159: }
        !           160:
        !           161:
        !           162: /* *** Memory allocation callbacks for the debugging table. *************** */
        !           163:
        !           164: static void *
        !           165: dhash_alloc(size_t sz, void *arg)
        !           166: {
        !           167:         return malloc(sz);
        !           168: }
        !           169:
        !           170: static void *
        !           171: dhash_calloc(size_t n, size_t sz, void *arg)
        !           172: {
        !           173:         return calloc(n, sz);
        !           174: }
        !           175:
        !           176: static void
        !           177: dhash_free(void *p, void *arg)
        !           178: {
        !           179:         free(p);
        !           180: }
        !           181:
        !           182:
        !           183: /* *** Debugging utility functions. *************************************** */
        !           184:
        !           185: /* Initialize the debugging table, to be called from the top of main(). */
        !           186: void
        !           187: mandoc_dbg_init(int argc, char *argv[])
        !           188: {
        !           189:        struct ohash_info         info;
        !           190:        char                     *dhash_fn;
        !           191:        int                       argi;
        !           192:
        !           193:        info.alloc = dhash_alloc;
        !           194:        info.calloc = dhash_calloc;
        !           195:        info.free = dhash_free;
        !           196:        info.data = NULL;
        !           197:        info.key_offset = offsetof(struct dhash_entry, ptr);
        !           198:        ohash_init(&dhash_table, 18, &info);
        !           199:
        !           200:        dhash_fp = stderr;
        !           201:        if ((dhash_fn = getenv("DEBUG_MEMORY")) == NULL)
        !           202:                return;
        !           203:
        !           204:        dhash_sflag = 1;
        !           205:        for(;; dhash_fn++) {
        !           206:                switch (*dhash_fn) {
        !           207:                case '\0':
        !           208:                        break;
        !           209:                case 'A':
        !           210:                        dhash_aflag = 1;
        !           211:                        continue;
        !           212:                case 'F':
        !           213:                        dhash_fflag = 1;
        !           214:                        continue;
        !           215:                case 'L':
        !           216:                        dhash_lflag = 1;
        !           217:                        continue;
        !           218:                case 'N':
        !           219:                        dhash_nflag = 1;
        !           220:                        continue;
        !           221:                case '/':
        !           222:                        if ((dhash_fp = fopen(dhash_fn, "a+e")) == NULL)
        !           223:                                err((int)MANDOCLEVEL_SYSERR, "%s", dhash_fn);
        !           224:                        break;
        !           225:                default:
        !           226:                        errx((int)MANDOCLEVEL_BADARG,
        !           227:                            "invalid char '%c' in $DEBUG_MEMORY",
        !           228:                            *dhash_fn);
        !           229:                }
        !           230:                break;
        !           231:        }
        !           232:        if (setvbuf(dhash_fp, NULL, _IOLBF, 0) != 0)
        !           233:                err((int)MANDOCLEVEL_SYSERR, "setvbuf");
        !           234:
        !           235:        fprintf(dhash_fp, "P %d", getpid());
        !           236:        for (argi = 0; argi < argc; argi++)
        !           237:                fprintf(dhash_fp, " [%s]", argv[argi]);
        !           238:        fprintf(dhash_fp, "\n");
        !           239: }
        !           240:
        !           241: void
        !           242: mandoc_dbg_name(const char *name)
        !           243: {
        !           244:        if (dhash_nflag)
        !           245:                fprintf(dhash_fp, "N %s\n", name);
        !           246: }
        !           247:
        !           248: /* Hash a pointer and return the table slot currently used for it. */
        !           249: static unsigned int
        !           250: dhash_slot(void *ptr)
        !           251: {
        !           252:        const char      *ks, *ke;
        !           253:        uint32_t         hv;
        !           254:
        !           255:        ks = (const char *)&ptr;
        !           256:        ke = ks + sizeof(ptr);
        !           257:        hv = ohash_interval(ks, &ke);
        !           258:        return ohash_lookup_memory(&dhash_table, ks, sizeof(ptr), hv);
        !           259: }
        !           260:
        !           261: /* Record one allocation in the debugging table. */
        !           262: static void
        !           263: dhash_register(const char *file, int line, const char *func,
        !           264:     size_t num, size_t size, void *ptr, const char *str)
        !           265: {
        !           266:        struct dhash_entry      *e;
        !           267:        unsigned int             slot;
        !           268:
        !           269:        slot = dhash_slot(ptr);
        !           270:        e = ohash_find(&dhash_table, slot);
        !           271:        if (dhash_aflag || e != NULL) {
        !           272:                fprintf(dhash_fp, "A %s:%d %s(%zu, %zu) = %p",
        !           273:                    file, line, func, num, size, ptr);
        !           274:                if (str != NULL)
        !           275:                        fprintf(dhash_fp, " \"%s\"", str);
        !           276:                fprintf(dhash_fp, "\n");
        !           277:        }
        !           278:        if (e != NULL) {
        !           279:                dhash_print(e);
        !           280:                fprintf(dhash_fp, "E duplicate address %p\n", e->ptr);
        !           281:                errx((int)MANDOCLEVEL_BADARG, "duplicate address %p", e->ptr);
        !           282:        }
        !           283:
        !           284:        if ((e = malloc(sizeof(*e))) == NULL)
        !           285:                err(1, NULL);
        !           286:        e->file = file;
        !           287:        e->line = line;
        !           288:        e->func = func;
        !           289:        e->num  = num;
        !           290:        e->size = size;
        !           291:        e->ptr  = ptr;
        !           292:
        !           293:        ohash_insert(&dhash_table, slot, e);
        !           294: }
        !           295:
        !           296: /* Remove one allocation from the debugging table. */
        !           297: static void
        !           298: dhash_purge(const char *file, int line, const char *func, void *ptr)
        !           299: {
        !           300:        struct dhash_entry      *e;
        !           301:        unsigned int             slot;
        !           302:
        !           303:        if (ptr == NULL)
        !           304:                return;
        !           305:
        !           306:        if (dhash_fflag)
        !           307:                fprintf(dhash_fp, "F %s:%d %s(%p)\n", file, line, func, ptr);
        !           308:
        !           309:        slot = dhash_slot(ptr);
        !           310:        e = ohash_remove(&dhash_table, slot);
        !           311:        free(e);
        !           312: }
        !           313:
        !           314: /* Pretty-print information about one allocation. */
        !           315: static void
        !           316: dhash_print(struct dhash_entry *e)
        !           317: {
        !           318:        fprintf(dhash_fp, "L %s:%d %s(%zu, %zu) = %p\n",
        !           319:            e->file, e->line, e->func, e->num, e->size, e->ptr);
        !           320: }
        !           321:
        !           322: /* Pretty-print information about all active allocations. */
        !           323: void
        !           324: mandoc_dbg_finish(void)
        !           325: {
        !           326:        struct dhash_entry      *e;
        !           327:        unsigned int             errcount, slot;
        !           328:
        !           329:        errcount = ohash_entries(&dhash_table);
        !           330:        e = ohash_first(&dhash_table, &slot);
        !           331:        while (e != NULL) {
        !           332:                if (dhash_lflag)
        !           333:                        dhash_print(e);
        !           334:                free(e);
        !           335:                e = ohash_next(&dhash_table, &slot);
        !           336:        }
        !           337:        ohash_delete(&dhash_table);
        !           338:        if (dhash_sflag)
        !           339:                fprintf(dhash_fp, "S %u memory leaks found\n", errcount);
        !           340:        if (dhash_fp != stderr)
        !           341:                fclose(dhash_fp);
        !           342: }

CVSweb