1 /*
2 * Copyright (c) 2014 Sippy Software, Inc., http://www.sippysoft.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28 /*
29 * Simple memory debug layer to track any unallocated memory as well as to
30 * catch any other common mistakes, such as double free or freeing of
31 * unallocated memory. Our attitude here is "fail with core dump early" if
32 * some error of inconsistency is found to aid debugging. Some extra smarts
33 * can be added, such as guard area to detect any buffer overflows.
34 */
35
36 #include <sys/types.h>
37 #include <pthread.h>
38 #include <inttypes.h>
39 #include <stdarg.h>
40 #include <stddef.h>
41 #include <stdint.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "rtpp_log.h"
47 #include "rtpp_types.h"
48 #include "rtpp_refcnt.h"
49 #include "rtpp_log_obj.h"
50 #include "rtpp_memdeb.h"
51 #include "rtpp_memdeb_internal.h"
52 #include "rtpp_memdeb_stats.h"
53
54 #undef malloc
55 #undef free
56 #undef realloc
57 #undef strdup
58 #undef asprintf
59 #undef vasprintf
60 #undef memcpy
61
62 #define _memcpy(dp, sp, len) rtpp_memdeb_memcpy((dp), (sp), (len), \
63 p, __FILE__, __LINE__, __func__)
64
65 #define UNUSED(x) (void)(x)
66
67 #define MEMDEB_SIGNATURE 0x8b26e00041dfdec6UL
68
69 #define MEMDEB_SIGNATURE_ALLOC(x) (MEMDEB_SIGNATURE ^ (uint64_t)(x))
70 #define MEMDEB_SIGNATURE_FREE(x) (~MEMDEB_SIGNATURE_ALLOC(x))
71
72 #define MEMDEB_SIG_PRIV_SALT 0x7d442e4532bb9ef0UL
73 #define MEMDEB_SIGNATURE_PRIV(x) \
74 (MEMDEB_SIG_PRIV_SALT ^ (uint64_t)(x))
75
76 #define MEMDEB_GUARD_SIZE 8
77
78 struct memdeb_loc {
79 const char *fname;
80 int linen;
81 const char *funcn;
82 };
83
84 struct memdeb_node
85 {
86 uint64_t magic;
87 struct memdeb_loc loc;
88 struct memdeb_stats mstats;
89 struct memdeb_node *next;
90 };
91
92 struct memdeb_pfx
93 {
94 struct memdeb_node *mnp;
95 size_t asize;
96 uint64_t magic;
97 /* Use longest datatype to ensure proper alignment */
98 long long real_data[0];
99 };
100
101 struct rtpp_memdeb_au {
102 const char *funcn;
103 int max_nunalloc;
104 const char *why;
105 };
106
107 #define MAX_APPROVED 10
108
109 struct rtpp_memdeb_priv {
110 uint64_t magic;
111 struct memdeb_node *nodes;
112 pthread_mutex_t mutex;
113 struct rtpp_memdeb_au au[MAX_APPROVED];
114 struct rtpp_log *_md_glog;
115 };
116
117 void *
rtpp_memdeb_init()118 rtpp_memdeb_init()
119 {
120 struct rtpp_memdeb_priv *pvt;
121
122 pvt = malloc(sizeof(struct rtpp_memdeb_priv));
123 if (pvt == NULL) {
124 return (NULL);
125 }
126 memset(pvt, '\0', sizeof(struct rtpp_memdeb_priv));
127 pthread_mutex_init(&pvt->mutex, NULL);
128 pvt->magic = MEMDEB_SIGNATURE_PRIV(pvt);
129 return (pvt);
130 }
131
132 #define CHK_PRIV(pvt, p) { \
133 (pvt) = (struct rtpp_memdeb_priv *)(p); \
134 if (pvt->magic != MEMDEB_SIGNATURE_PRIV(pvt)) { \
135 RTPP_MEMDEB_REPORT(NULL, "%s(): bogus private pointer: %p", \
136 __func__, pvt); \
137 abort(); \
138 } \
139 }
140
141 void
rtpp_memdeb_dtor(void * p)142 rtpp_memdeb_dtor(void *p)
143 {
144 struct rtpp_memdeb_priv *pvt;
145
146 CHK_PRIV(pvt, p);
147 if (pvt->_md_glog != NULL) {
148 CALL_SMETHOD(pvt->_md_glog->rcnt, decref);
149 }
150 pvt->magic = MEMDEB_SIGNATURE_FREE(pvt);
151 pthread_mutex_destroy(&pvt->mutex);
152 free(pvt);
153 return;
154 }
155
156 void
rtpp_memdeb_setlog(void * p,struct rtpp_log * log)157 rtpp_memdeb_setlog(void *p, struct rtpp_log *log)
158 {
159 struct rtpp_memdeb_priv *pvt;
160
161 CHK_PRIV(pvt, p);
162 CALL_SMETHOD(log->rcnt, incref);
163 pvt->_md_glog = log;
164 }
165
166 void
rtpp_memdeb_approve(void * p,const char * funcn,int max_nunalloc,const char * why)167 rtpp_memdeb_approve(void *p, const char *funcn, int max_nunalloc,
168 const char *why)
169 {
170 int i;
171 struct rtpp_memdeb_priv *pvt;
172
173 CHK_PRIV(pvt, p);
174 for (i = 0; i < MAX_APPROVED; i++) {
175 if (pvt->au[i].funcn != NULL)
176 continue;
177 pvt->au[i].funcn = funcn;
178 pvt->au[i].max_nunalloc = max_nunalloc;
179 pvt->au[i].why = why;
180 return;
181 }
182 }
183
184 #define RTPP_MEMDEB_REPORT_LOC(handle, mlp, format, args...) { \
185 RTPP_MEMDEB_REPORT(handle, format, ## args); \
186 RTPP_MEMDEB_REPORT(handle, " called from %s+%d, %s()", \
187 (mlp)->fname, (mlp)->linen, (mlp)->funcn); \
188 }
189
190 static struct memdeb_node *
rtpp_memdeb_nget(struct rtpp_memdeb_priv * pvt,struct memdeb_loc * mlp,int doalloc)191 rtpp_memdeb_nget(struct rtpp_memdeb_priv *pvt, struct memdeb_loc *mlp,
192 int doalloc)
193 {
194 struct memdeb_node *rval, *mnp, *lastnode;
195
196 pthread_mutex_lock(&pvt->mutex);
197 lastnode = NULL;
198 for (mnp = pvt->nodes; mnp != NULL; mnp = mnp->next) {
199 if (mnp->magic != MEMDEB_SIGNATURE) {
200 /* nodelist is corrupt */
201 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, mlp, "Nodelist %p is corrupt", mnp);
202 abort();
203 }
204 if (mnp->loc.fname == mlp->fname && mnp->loc.linen == mlp->linen && \
205 mnp->loc.funcn == mlp->funcn)
206 return (mnp);
207 lastnode = mnp;
208 }
209 if (doalloc == 0) {
210 pthread_mutex_unlock(&pvt->mutex);
211 return (NULL);
212 }
213 rval = malloc(sizeof(struct memdeb_node));
214 if (rval == NULL) {
215 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, mlp, "Allocation for the new nodelist failed");
216 abort();
217 }
218 memset(rval, '\0', sizeof(struct memdeb_node));
219 rval->magic = MEMDEB_SIGNATURE;
220 rval->loc = *mlp;
221 if (pvt->nodes == NULL) {
222 pvt->nodes = rval;
223 } else {
224 lastnode->next = rval;
225 }
226 return (rval);
227 }
228
229 #define CHK_PRIV_VRB(pvt, p, mlp) { \
230 (pvt) = (struct rtpp_memdeb_priv *)(p); \
231 if (pvt->magic != MEMDEB_SIGNATURE_PRIV(pvt)) { \
232 RTPP_MEMDEB_REPORT_LOC(NULL, mlp, "%s(): bogus private pointer: %p", \
233 __func__, pvt); \
234 abort(); \
235 } \
236 }
237
238 void *
rtpp_memdeb_malloc(size_t size,void * p,const char * fname,int linen,const char * funcn)239 rtpp_memdeb_malloc(size_t size, void *p, const char *fname, int linen, const char *funcn)
240 {
241 struct memdeb_node *mnp;
242 struct memdeb_pfx *mpf;
243 unsigned char *gp;
244 uint64_t guard;
245 struct rtpp_memdeb_priv *pvt;
246 struct memdeb_loc ml;
247
248 ml.fname = fname;
249 ml.linen = linen;
250 ml.funcn = funcn;
251
252 CHK_PRIV_VRB(pvt, p, &ml);
253 mpf = malloc(offsetof(struct memdeb_pfx, real_data) + size + MEMDEB_GUARD_SIZE);
254 mnp = rtpp_memdeb_nget(pvt, &ml, 1);
255 if (mpf == NULL) {
256 mnp->mstats.afails++;
257 pthread_mutex_unlock(&pvt->mutex);
258 return (NULL);
259 }
260 mnp->mstats.nalloc++;
261 mnp->mstats.balloc += size;
262 mpf->magic = MEMDEB_SIGNATURE_ALLOC(mpf);
263 pthread_mutex_unlock(&pvt->mutex);
264 mpf->asize = size;
265 mpf->mnp = mnp;
266 gp = (unsigned char *)mpf->real_data + size;
267 guard = MEMDEB_SIGNATURE_ALLOC(gp);
268 _memcpy(gp, &guard, MEMDEB_GUARD_SIZE);
269 return (mpf->real_data);
270 }
271
272 static struct memdeb_pfx *
ptr2mpf(struct rtpp_memdeb_priv * pvt,void * ptr,struct memdeb_loc * mlp)273 ptr2mpf(struct rtpp_memdeb_priv *pvt, void *ptr, struct memdeb_loc *mlp)
274 {
275 char *cp;
276 struct memdeb_pfx *mpf;
277
278 cp = ptr;
279 cp -= offsetof(struct memdeb_pfx, real_data);
280 mpf = (struct memdeb_pfx *)cp;
281
282 if (mpf->magic != MEMDEB_SIGNATURE_ALLOC(mpf)) {
283 /* Random or de-allocated pointer */
284 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, mlp, "Random or de-allocated pointer");
285 abort();
286 }
287 if (mpf->mnp->magic != MEMDEB_SIGNATURE) {
288 /* Free of unallocated pointer or nodelist is corrupt */
289 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, mlp, "Nodelist %p is corrupt", mpf->mnp);
290 abort();
291 }
292 return (mpf);
293 }
294
295 void
rtpp_memdeb_free(void * ptr,void * p,const char * fname,int linen,const char * funcn)296 rtpp_memdeb_free(void *ptr, void *p, const char *fname, int linen, const char *funcn)
297 {
298 UNUSED(fname);
299 UNUSED(linen);
300 UNUSED(funcn);
301 struct memdeb_pfx *mpf;
302 unsigned char *gp;
303 uint64_t guard;
304 struct rtpp_memdeb_priv *pvt;
305 struct memdeb_loc ml;
306
307 ml.fname = fname;
308 ml.linen = linen;
309 ml.funcn = funcn;
310
311 CHK_PRIV_VRB(pvt, p, &ml);
312 mpf = ptr2mpf(pvt, ptr, &ml);
313 gp = (unsigned char *)mpf->real_data + mpf->asize;
314 guard = MEMDEB_SIGNATURE_ALLOC(gp);
315 if (memcmp(gp, &guard, MEMDEB_GUARD_SIZE) != 0) {
316 /* Guard is b0rken, probably out-of-bound write */
317 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, &ml, "Guard is b0rken, probably out-of-bound write");
318 abort();
319 }
320 pthread_mutex_lock(&pvt->mutex);
321 mpf->mnp->mstats.nfree++;
322 mpf->mnp->mstats.bfree += mpf->asize;
323 mpf->magic = MEMDEB_SIGNATURE_FREE(mpf);
324 pthread_mutex_unlock(&pvt->mutex);
325 return free(mpf);
326 }
327
328 void *
rtpp_memdeb_realloc(void * ptr,size_t size,void * p,const char * fname,int linen,const char * funcn)329 rtpp_memdeb_realloc(void *ptr, size_t size, void *p, const char *fname, int linen,
330 const char *funcn)
331 {
332 UNUSED(fname);
333 UNUSED(linen);
334 UNUSED(funcn);
335 struct memdeb_pfx *mpf, *new_mpf;
336 char *cp;
337 uint64_t sig_save;
338 unsigned char *gp;
339 uint64_t guard;
340 struct rtpp_memdeb_priv *pvt;
341 struct memdeb_loc ml;
342
343 ml.fname = fname;
344 ml.linen = linen;
345 ml.funcn = funcn;
346
347 CHK_PRIV_VRB(pvt, p, &ml);
348 if (ptr == NULL) {
349 return (rtpp_memdeb_malloc(size, pvt, fname, linen, funcn));
350 }
351 mpf = ptr2mpf(pvt, ptr, &ml);
352 sig_save = MEMDEB_SIGNATURE_ALLOC(mpf);
353 pthread_mutex_lock(&pvt->mutex);
354 mpf->magic = MEMDEB_SIGNATURE_FREE(mpf);
355 pthread_mutex_unlock(&pvt->mutex);
356 cp = realloc(mpf, size + offsetof(struct memdeb_pfx, real_data) + MEMDEB_GUARD_SIZE);
357 if (cp == NULL) {
358 pthread_mutex_lock(&pvt->mutex);
359 mpf->magic = sig_save;
360 mpf->mnp->mstats.afails++;
361 pthread_mutex_unlock(&pvt->mutex);
362 return (cp);
363 }
364 new_mpf = (struct memdeb_pfx *)cp;
365 if (new_mpf != mpf) {
366 sig_save = MEMDEB_SIGNATURE_ALLOC(new_mpf);
367 }
368 pthread_mutex_lock(&pvt->mutex);
369 new_mpf->magic = sig_save;
370 new_mpf->mnp->mstats.nrealloc++;
371 new_mpf->mnp->mstats.brealloc += size;
372 new_mpf->mnp->mstats.balloc += size - new_mpf->asize;
373 pthread_mutex_unlock(&pvt->mutex);
374 new_mpf->asize = size;
375 gp = (unsigned char *)new_mpf->real_data + size;
376 guard = MEMDEB_SIGNATURE_ALLOC(gp);
377 _memcpy(gp, &guard, MEMDEB_GUARD_SIZE);
378 return (new_mpf->real_data);
379 }
380
381 char *
rtpp_memdeb_strdup(const char * ptr,void * p,const char * fname,int linen,const char * funcn)382 rtpp_memdeb_strdup(const char *ptr, void *p, const char *fname, int linen, \
383 const char *funcn)
384 {
385 struct memdeb_node *mnp;
386 struct memdeb_pfx *mpf;
387 size_t size;
388 unsigned char *gp;
389 uint64_t guard;
390 struct rtpp_memdeb_priv *pvt;
391 struct memdeb_loc ml;
392
393 ml.fname = fname;
394 ml.linen = linen;
395 ml.funcn = funcn;
396
397 CHK_PRIV_VRB(pvt, p, &ml);
398 size = strlen(ptr) + 1;
399 mpf = malloc(size + offsetof(struct memdeb_pfx, real_data) + MEMDEB_GUARD_SIZE);
400 mnp = rtpp_memdeb_nget(pvt, &ml, 1);
401 if (mpf == NULL) {
402 mnp->mstats.afails++;
403 pthread_mutex_unlock(&pvt->mutex);
404 return (NULL);
405 }
406 mnp->mstats.nalloc++;
407 mnp->mstats.balloc += size;
408 pthread_mutex_unlock(&pvt->mutex);
409 mpf->mnp = mnp;
410 mpf->asize = size;
411 _memcpy(mpf->real_data, ptr, size);
412 mpf->magic = MEMDEB_SIGNATURE_ALLOC(mpf);
413 gp = (unsigned char *)mpf->real_data + size;
414 guard = MEMDEB_SIGNATURE_ALLOC(gp);
415 _memcpy(gp, &guard, MEMDEB_GUARD_SIZE);
416 return ((char *)mpf->real_data);
417 }
418
419 int
rtpp_memdeb_asprintf(char ** pp,const char * fmt,void * p,const char * fname,int linen,const char * funcn,...)420 rtpp_memdeb_asprintf(char **pp, const char *fmt, void *p, const char *fname,
421 int linen, const char *funcn, ...)
422 {
423 va_list ap;
424 int rval;
425
426 va_start(ap, funcn);
427 rval = rtpp_memdeb_vasprintf(pp, fmt, p, fname, linen, funcn, ap);
428 va_end(ap);
429 return (rval);
430 }
431
432 int
rtpp_memdeb_vasprintf(char ** pp,const char * fmt,void * p,const char * fname,int linen,const char * funcn,va_list ap)433 rtpp_memdeb_vasprintf(char **pp, const char *fmt, void *p, const char *fname,
434 int linen, const char *funcn, va_list ap)
435 {
436 int rval;
437 void *tp;
438
439 rval = vasprintf(pp, fmt, ap);
440 if (rval <= 0) {
441 return (rval);
442 }
443 tp = rtpp_memdeb_malloc(rval + 1, p, fname, linen, funcn);
444 if (tp == NULL) {
445 free(*pp);
446 *pp = NULL;
447 return (-1);
448 }
449 _memcpy(tp, *pp, rval + 1);
450 free(*pp);
451 *pp = tp;
452 return (rval);
453 }
454
455 void *
rtpp_memdeb_memcpy(void * dst,const void * src,size_t len,void * p,const char * fname,int linen,const char * funcn)456 rtpp_memdeb_memcpy(void *dst, const void *src, size_t len, void *p,
457 const char *fname, int linen, const char *funcn)
458 {
459 struct memdeb_loc ml;
460 struct rtpp_memdeb_priv *pvt;
461
462 CHK_PRIV(pvt, p);
463
464 if ((dst < src && src < (dst + len)) || (src < dst && dst < (src + len))) {
465 ml.fname = fname;
466 ml.linen = linen;
467 ml.funcn = funcn;
468 RTPP_MEMDEB_REPORT_LOC(pvt->_md_glog, &ml, "memcpy(%p, %p, %ld) overlapping regions, use memmove()",
469 dst, src, (long)len);
470 abort();
471 }
472 return (memcpy(dst, src, len));
473 }
474
475 static int
is_approved(struct rtpp_memdeb_priv * pvt,const char * funcn)476 is_approved(struct rtpp_memdeb_priv *pvt, const char *funcn)
477 {
478 int i;
479
480 for (i = 0; pvt->au[i].funcn != NULL && i < MAX_APPROVED; i++) {
481 if (strcmp(pvt->au[i].funcn, funcn) != 0)
482 continue;
483 return (pvt->au[i].max_nunalloc);
484 }
485 return (0);
486 }
487
488 int
rtpp_memdeb_dumpstats(void * p,int nostdout)489 rtpp_memdeb_dumpstats(void *p, int nostdout)
490 {
491 struct memdeb_node *mnp;
492 int errors_found, max_nunalloc;
493 int64_t nunalloc;
494 struct rtpp_log *log;
495 struct rtpp_memdeb_priv *pvt;
496
497 CHK_PRIV(pvt, p);
498 errors_found = 0;
499 log = pvt->_md_glog;
500 pthread_mutex_lock(&pvt->mutex);
501 for (mnp = pvt->nodes; mnp != NULL; mnp = mnp->next) {
502 nunalloc = mnp->mstats.nalloc - mnp->mstats.nfree;
503 if (mnp->mstats.afails == 0) {
504 if (mnp->mstats.nalloc == 0)
505 continue;
506 if (mnp->mstats.nalloc == mnp->mstats.nfree)
507 continue;
508 if (nunalloc <= mnp->mstats.nunalloc_baseln)
509 continue;
510 }
511 if (nunalloc > 0) {
512 max_nunalloc = is_approved(pvt, mnp->loc.funcn);
513 if (max_nunalloc > 0 && nunalloc <= max_nunalloc)
514 continue;
515 }
516 if (errors_found == 0) {
517 RTPP_MEMDEB_REPORT2(log, nostdout,
518 "MEMDEB suspicious allocations:");
519 }
520 errors_found++;
521 RTPP_MEMDEB_REPORT2(log, nostdout,
522 " %s+%d, %s(): nalloc = %" PRId64 ", balloc = %" PRId64 ", nfree = %"
523 PRId64 ", bfree = %" PRId64 ", afails = %" PRId64 ", nunalloc_baseln"
524 " = %" PRId64, mnp->loc.fname, mnp->loc.linen, mnp->loc.funcn, mnp->mstats.nalloc,
525 mnp->mstats.balloc, mnp->mstats.nfree, mnp->mstats.bfree,
526 mnp->mstats.afails, mnp->mstats.nunalloc_baseln);
527 }
528 pthread_mutex_unlock(&pvt->mutex);
529 if (errors_found == 0) {
530 RTPP_MEMDEB_REPORT2(log, nostdout,
531 "MEMDEB: all clear");
532 } else {
533 RTPP_MEMDEB_REPORT2(log, nostdout,
534 "MEMDEB: errors found: %d", errors_found);
535 }
536 return (errors_found);
537 }
538
539 void
rtpp_memdeb_setbaseln(void * p)540 rtpp_memdeb_setbaseln(void *p)
541 {
542
543 struct memdeb_node *mnp;
544 struct rtpp_memdeb_priv *pvt;
545
546 CHK_PRIV(pvt, p);
547 pthread_mutex_lock(&pvt->mutex);
548 for (mnp = pvt->nodes; mnp != NULL; mnp = mnp->next) {
549 if (mnp->magic != MEMDEB_SIGNATURE) {
550 /* Nodelist is corrupt */
551 RTPP_MEMDEB_REPORT(pvt->_md_glog, "Nodelist %p is corrupt", mnp);
552 abort();
553 }
554 if (mnp->mstats.nalloc == 0)
555 continue;
556 mnp->mstats.nunalloc_baseln = mnp->mstats.nalloc - mnp->mstats.nfree;
557 mnp->mstats.bunalloc_baseln = mnp->mstats.balloc - mnp->mstats.bfree;
558 }
559 pthread_mutex_unlock(&pvt->mutex);
560 }
561
562 int
rtpp_memdeb_get_stats(void * p,const char * fname,const char * funcn,struct memdeb_stats * mstatp)563 rtpp_memdeb_get_stats(void *p, const char *fname, const char *funcn,
564 struct memdeb_stats *mstatp)
565 {
566 struct memdeb_node *mnp;
567 int nmatches;
568 struct rtpp_memdeb_priv *pvt;
569
570 CHK_PRIV(pvt, p);
571 nmatches = 0;
572 pthread_mutex_lock(&pvt->mutex);
573 for (mnp = pvt->nodes; mnp != NULL; mnp = mnp->next) {
574 if (mnp->magic != MEMDEB_SIGNATURE) {
575 /* Nodelist is corrupt */
576 RTPP_MEMDEB_REPORT(pvt->_md_glog, "Nodelist %p is corrupt", mnp);
577 abort();
578 }
579 if (funcn != NULL && strcmp(funcn, mnp->loc.funcn) != 0) {
580 continue;
581 }
582 if (fname != NULL && strcmp(fname, mnp->loc.fname) != 0) {
583 continue;
584 }
585 RTPP_MD_STATS_ADD(mstatp, &mnp->mstats);
586 nmatches += 1;
587 }
588 pthread_mutex_unlock(&pvt->mutex);
589 return (nmatches);
590 }
591