├── Makefile ├── README ├── avl.c ├── avl.h ├── mdname.c ├── mdump.c ├── mdump.h ├── mleak.c ├── mleak.h ├── mnew.cc └── mx.c /Makefile: -------------------------------------------------------------------------------- 1 | OPT=-g 2 | INC=-I/usr/include/libiberty 3 | CFLAGS:=$(CFLAGS) $(OPT) $(INC) 4 | 5 | LIBS=mleak.so mleak++.so 6 | PRGS=mdump 7 | OBJS=mleak.o mnew.o mdump.o mdname.o avl.o 8 | 9 | all: $(LIBS) $(PRGS) 10 | 11 | clean: 12 | rm -f $(LIBS) $(PRGS) $(OBJS) 13 | 14 | mleak.o: mleak.c mleak.h 15 | $(CC) $(CFLAGS) -fPIC -c $< 16 | 17 | mnew.o: mnew.cc 18 | $(CXX) $(CFLAGS) -fPIC -c $< 19 | 20 | mleak.so: mleak.o 21 | $(CC) $(CFLAGS) -shared -o $@ $^ -ldl -lunwind 22 | 23 | mleak++.so: mleak.o mnew.o 24 | $(CXX) $(CFLAGS) -shared -o $@ $^ -ldl -lunwind 25 | 26 | mdump: mdump.o avl.o mdname.o 27 | $(CC) -o $@ mdump.o avl.o mdname.o -lbfd 28 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a small memory leak tracer for C programs. It is much faster than the 2 | heap checker built into glibc or tcmalloc, 5-8x faster typically. It will not 3 | detect any other type of malloc errors besides memory leaks, so this won't 4 | help you if you have other memory issues. 5 | 6 | It uses libunwind to retrieve stack traces for each allocation. Stack traces 7 | are prepended to the malloc'd memory blocks, to avoid needing a separate data 8 | structure to track alloc'd and free'd stack traces. This obviously increases 9 | the amount of memory being used by the application, but it's overall 10 | insignificant. Storing this way also allows the tracer to stash its info 11 | without requiring use of any mutexes in threaded code. 12 | 13 | Output is generated by an atexit() handler, so your program must exit cleanly 14 | in order to get any results. The output is a binary file containing a list of 15 | all of the unfreed blocks and the stack trace where they were allocated. The 16 | mdump tool is provided for reading this data and generating a text report of 17 | the leaks. The dumper uses libbfd to look up symbol names. 18 | 19 | You must have both libunwind and libbfd to build this code. Most of the code 20 | is portable to generic POSIX but the scan for heap space is Linux-specific. 21 | Also, code for reading the linker map uses the Linux convention of 22 | dlopen(NULL) to get a handle to the current program. Most POSIX systems let 23 | you use RTLD_SELF instead but for some reason glibc stopped supporting this 24 | many years ago. 25 | 26 | To use the tracer: 27 | make 28 | cp mleak.so mdump /tmp 29 | LD_PRELOAD=/tmp/mleak.so my_buggy_program 30 | /tmp/mdump my_buggy_program ml.* 31 | 32 | The mleak.so wrapper generates two output files: ml.info, which contains the 33 | map of dynamically loaded objects and ml.data, which contains the record of 34 | leaked memory blocks. The mdump program reads both of these files to produce 35 | its report. 36 | 37 | -- Howard Chu 2015-03-24 38 | 39 | 40 | Snapshot feature: send a SIGPROF to the traced program, to get a dump of the 41 | memory in use at that time. Note that there is no thread locking protection 42 | on this action so your program should be idle when you send it the signal. 43 | The ml.data and ml.info files will be generated, and can be processed using 44 | mdump. Diffing the output from successive snapshots will let you see how 45 | memory use changes over time. 46 | 47 | -- Howard Chu 2016-12-26 48 | -------------------------------------------------------------------------------- /avl.c: -------------------------------------------------------------------------------- 1 | /* avl.c - routines to implement an avl tree */ 2 | /* $OpenLDAP: pkg/ldap/libraries/libavl/avl.c,v 1.20 2001/12/07 03:03:30 hyc Exp $ */ 3 | /* 4 | * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved. 5 | * COPYING RESTRICTIONS APPLY, see COPYRIGHT file 6 | */ 7 | /* 8 | * Copyright (c) 1993 Regents of the University of Michigan. 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms are permitted 12 | * provided that this notice is preserved and that due credit is given 13 | * to the University of Michigan at Ann Arbor. The name of the University 14 | * may not be used to endorse or promote products derived from this 15 | * software without specific prior written permission. This software 16 | * is provided ``as is'' without express or implied warranty. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #define ber_memalloc malloc 23 | #define ber_memrealloc realloc 24 | 25 | #define AVL_INTERNAL 26 | #include "avl.h" 27 | 28 | #define ROTATERIGHT(x) { \ 29 | Avlnode *tmp;\ 30 | if ( *(x) == NULL || (*(x))->avl_left == NULL ) {\ 31 | (void) fputs("RR error\n", stderr); exit( EXIT_FAILURE ); \ 32 | }\ 33 | tmp = (*(x))->avl_left;\ 34 | (*(x))->avl_left = tmp->avl_right;\ 35 | tmp->avl_right = *(x);\ 36 | *(x) = tmp;\ 37 | } 38 | #define ROTATELEFT(x) { \ 39 | Avlnode *tmp;\ 40 | if ( *(x) == NULL || (*(x))->avl_right == NULL ) {\ 41 | (void) fputs("RL error\n", stderr); exit( EXIT_FAILURE ); \ 42 | }\ 43 | tmp = (*(x))->avl_right;\ 44 | (*(x))->avl_right = tmp->avl_left;\ 45 | tmp->avl_left = *(x);\ 46 | *(x) = tmp;\ 47 | } 48 | 49 | /* 50 | * ravl_insert - called from avl_insert() to do a recursive insert into 51 | * and balance of an avl tree. 52 | */ 53 | 54 | static int 55 | ravl_insert( 56 | Avlnode **iroot, 57 | void* data, 58 | int *taller, 59 | AVL_CMP fcmp, /* comparison function */ 60 | AVL_DUP fdup, /* function to call for duplicates */ 61 | int depth 62 | ) 63 | { 64 | int rc, cmp, tallersub; 65 | Avlnode *l, *r; 66 | 67 | if ( *iroot == 0 ) { 68 | if ( (*iroot = (Avlnode *) ber_memalloc( sizeof( Avlnode ) )) 69 | == NULL ) { 70 | return( -1 ); 71 | } 72 | (*iroot)->avl_left = 0; 73 | (*iroot)->avl_right = 0; 74 | (*iroot)->avl_bf = 0; 75 | (*iroot)->avl_data = data; 76 | *taller = 1; 77 | return( 0 ); 78 | } 79 | 80 | cmp = (*fcmp)( data, (*iroot)->avl_data ); 81 | 82 | /* equal - duplicate name */ 83 | if ( cmp == 0 ) { 84 | *taller = 0; 85 | return( (*fdup)( (*iroot)->avl_data, data ) ); 86 | } 87 | 88 | /* go right */ 89 | else if ( cmp > 0 ) { 90 | rc = ravl_insert( &((*iroot)->avl_right), data, &tallersub, 91 | fcmp, fdup, depth ); 92 | if ( tallersub ) 93 | switch ( (*iroot)->avl_bf ) { 94 | case LH : /* left high - balance is restored */ 95 | (*iroot)->avl_bf = EH; 96 | *taller = 0; 97 | break; 98 | case EH : /* equal height - now right heavy */ 99 | (*iroot)->avl_bf = RH; 100 | *taller = 1; 101 | break; 102 | case RH : /* right heavy to start - right balance */ 103 | r = (*iroot)->avl_right; 104 | switch ( r->avl_bf ) { 105 | case LH : /* double rotation left */ 106 | l = r->avl_left; 107 | switch ( l->avl_bf ) { 108 | case LH : (*iroot)->avl_bf = EH; 109 | r->avl_bf = RH; 110 | break; 111 | case EH : (*iroot)->avl_bf = EH; 112 | r->avl_bf = EH; 113 | break; 114 | case RH : (*iroot)->avl_bf = LH; 115 | r->avl_bf = EH; 116 | break; 117 | } 118 | l->avl_bf = EH; 119 | ROTATERIGHT( (&r) ) 120 | (*iroot)->avl_right = r; 121 | ROTATELEFT( iroot ) 122 | *taller = 0; 123 | break; 124 | case EH : /* This should never happen */ 125 | break; 126 | case RH : /* single rotation left */ 127 | (*iroot)->avl_bf = EH; 128 | r->avl_bf = EH; 129 | ROTATELEFT( iroot ) 130 | *taller = 0; 131 | break; 132 | } 133 | break; 134 | } 135 | else 136 | *taller = 0; 137 | } 138 | 139 | /* go left */ 140 | else { 141 | rc = ravl_insert( &((*iroot)->avl_left), data, &tallersub, 142 | fcmp, fdup, depth ); 143 | if ( tallersub ) 144 | switch ( (*iroot)->avl_bf ) { 145 | case LH : /* left high to start - left balance */ 146 | l = (*iroot)->avl_left; 147 | switch ( l->avl_bf ) { 148 | case LH : /* single rotation right */ 149 | (*iroot)->avl_bf = EH; 150 | l->avl_bf = EH; 151 | ROTATERIGHT( iroot ) 152 | *taller = 0; 153 | break; 154 | case EH : /* this should never happen */ 155 | break; 156 | case RH : /* double rotation right */ 157 | r = l->avl_right; 158 | switch ( r->avl_bf ) { 159 | case LH : (*iroot)->avl_bf = RH; 160 | l->avl_bf = EH; 161 | break; 162 | case EH : (*iroot)->avl_bf = EH; 163 | l->avl_bf = EH; 164 | break; 165 | case RH : (*iroot)->avl_bf = EH; 166 | l->avl_bf = LH; 167 | break; 168 | } 169 | r->avl_bf = EH; 170 | ROTATELEFT( (&l) ) 171 | (*iroot)->avl_left = l; 172 | ROTATERIGHT( iroot ) 173 | *taller = 0; 174 | break; 175 | } 176 | break; 177 | case EH : /* equal height - now left heavy */ 178 | (*iroot)->avl_bf = LH; 179 | *taller = 1; 180 | break; 181 | case RH : /* right high - balance is restored */ 182 | (*iroot)->avl_bf = EH; 183 | *taller = 0; 184 | break; 185 | } 186 | else 187 | *taller = 0; 188 | } 189 | 190 | return( rc ); 191 | } 192 | 193 | /* 194 | * avl_insert -- insert a node containing data data into the avl tree 195 | * with root root. fcmp is a function to call to compare the data portion 196 | * of two nodes. it should take two arguments and return <, >, or == 0, 197 | * depending on whether its first argument is <, >, or == its second 198 | * argument (like strcmp, e.g.). fdup is a function to call when a duplicate 199 | * node is inserted. it should return 0, or -1 and its return value 200 | * will be the return value from avl_insert in the case of a duplicate node. 201 | * the function will be called with the original node's data as its first 202 | * argument and with the incoming duplicate node's data as its second 203 | * argument. this could be used, for example, to keep a count with each 204 | * node. 205 | * 206 | * NOTE: this routine may malloc memory 207 | */ 208 | 209 | int 210 | avl_insert( Avlnode **root, void* data, AVL_CMP fcmp, AVL_DUP fdup ) 211 | { 212 | int taller; 213 | 214 | return( ravl_insert( root, data, &taller, fcmp, fdup, 0 ) ); 215 | } 216 | 217 | /* 218 | * right_balance() - called from delete when root's right subtree has 219 | * been shortened because of a deletion. 220 | */ 221 | 222 | static int 223 | right_balance( Avlnode **root ) 224 | { 225 | int shorter = -1; 226 | Avlnode *r, *l; 227 | 228 | switch( (*root)->avl_bf ) { 229 | case RH: /* was right high - equal now */ 230 | (*root)->avl_bf = EH; 231 | shorter = 1; 232 | break; 233 | case EH: /* was equal - left high now */ 234 | (*root)->avl_bf = LH; 235 | shorter = 0; 236 | break; 237 | case LH: /* was right high - balance */ 238 | l = (*root)->avl_left; 239 | switch ( l->avl_bf ) { 240 | case RH : /* double rotation left */ 241 | r = l->avl_right; 242 | switch ( r->avl_bf ) { 243 | case RH : 244 | (*root)->avl_bf = EH; 245 | l->avl_bf = LH; 246 | break; 247 | case EH : 248 | (*root)->avl_bf = EH; 249 | l->avl_bf = EH; 250 | break; 251 | case LH : 252 | (*root)->avl_bf = RH; 253 | l->avl_bf = EH; 254 | break; 255 | } 256 | r->avl_bf = EH; 257 | ROTATELEFT( (&l) ) 258 | (*root)->avl_left = l; 259 | ROTATERIGHT( root ) 260 | shorter = 1; 261 | break; 262 | case EH : /* right rotation */ 263 | (*root)->avl_bf = LH; 264 | l->avl_bf = RH; 265 | ROTATERIGHT( root ); 266 | shorter = 0; 267 | break; 268 | case LH : /* single rotation right */ 269 | (*root)->avl_bf = EH; 270 | l->avl_bf = EH; 271 | ROTATERIGHT( root ) 272 | shorter = 1; 273 | break; 274 | } 275 | break; 276 | } 277 | 278 | return( shorter ); 279 | } 280 | 281 | /* 282 | * left_balance() - called from delete when root's left subtree has 283 | * been shortened because of a deletion. 284 | */ 285 | 286 | static int 287 | left_balance( Avlnode **root ) 288 | { 289 | int shorter = -1; 290 | Avlnode *r, *l; 291 | 292 | switch( (*root)->avl_bf ) { 293 | case LH: /* was left high - equal now */ 294 | (*root)->avl_bf = EH; 295 | shorter = 1; 296 | break; 297 | case EH: /* was equal - right high now */ 298 | (*root)->avl_bf = RH; 299 | shorter = 0; 300 | break; 301 | case RH: /* was right high - balance */ 302 | r = (*root)->avl_right; 303 | switch ( r->avl_bf ) { 304 | case LH : /* double rotation left */ 305 | l = r->avl_left; 306 | switch ( l->avl_bf ) { 307 | case LH : 308 | (*root)->avl_bf = EH; 309 | r->avl_bf = RH; 310 | break; 311 | case EH : 312 | (*root)->avl_bf = EH; 313 | r->avl_bf = EH; 314 | break; 315 | case RH : 316 | (*root)->avl_bf = LH; 317 | r->avl_bf = EH; 318 | break; 319 | } 320 | l->avl_bf = EH; 321 | ROTATERIGHT( (&r) ) 322 | (*root)->avl_right = r; 323 | ROTATELEFT( root ) 324 | shorter = 1; 325 | break; 326 | case EH : /* single rotation left */ 327 | (*root)->avl_bf = RH; 328 | r->avl_bf = LH; 329 | ROTATELEFT( root ); 330 | shorter = 0; 331 | break; 332 | case RH : /* single rotation left */ 333 | (*root)->avl_bf = EH; 334 | r->avl_bf = EH; 335 | ROTATELEFT( root ) 336 | shorter = 1; 337 | break; 338 | } 339 | break; 340 | } 341 | 342 | return( shorter ); 343 | } 344 | 345 | /* 346 | * ravl_delete() - called from avl_delete to do recursive deletion of a 347 | * node from an avl tree. It finds the node recursively, deletes it, 348 | * and returns shorter if the tree is shorter after the deletion and 349 | * rebalancing. 350 | */ 351 | 352 | static void* 353 | ravl_delete( Avlnode **root, void* data, AVL_CMP fcmp, int *shorter ) 354 | { 355 | int shortersubtree = 0; 356 | int cmp; 357 | void* savedata; 358 | Avlnode *minnode, *savenode; 359 | 360 | if ( *root == NULLAVL ) 361 | return( 0 ); 362 | 363 | cmp = (*fcmp)( data, (*root)->avl_data ); 364 | 365 | /* found it! */ 366 | if ( cmp == 0 ) { 367 | savenode = *root; 368 | savedata = savenode->avl_data; 369 | 370 | /* simple cases: no left child */ 371 | if ( (*root)->avl_left == 0 ) { 372 | *root = (*root)->avl_right; 373 | *shorter = 1; 374 | free( (char *) savenode ); 375 | return( savedata ); 376 | /* no right child */ 377 | } else if ( (*root)->avl_right == 0 ) { 378 | *root = (*root)->avl_left; 379 | *shorter = 1; 380 | free( (char *) savenode ); 381 | return( savedata ); 382 | } 383 | 384 | /* 385 | * avl_getmin will return to us the smallest node greater 386 | * than the one we are trying to delete. deleting this node 387 | * from the right subtree is guaranteed to end in one of the 388 | * simple cases above. 389 | */ 390 | 391 | minnode = (*root)->avl_right; 392 | while ( minnode->avl_left != NULLAVL ) 393 | minnode = minnode->avl_left; 394 | 395 | /* swap the data */ 396 | (*root)->avl_data = minnode->avl_data; 397 | minnode->avl_data = savedata; 398 | 399 | savedata = ravl_delete( &(*root)->avl_right, data, fcmp, 400 | &shortersubtree ); 401 | 402 | if ( shortersubtree ) 403 | *shorter = right_balance( root ); 404 | else 405 | *shorter = 0; 406 | /* go left */ 407 | } else if ( cmp < 0 ) { 408 | if ( (savedata = ravl_delete( &(*root)->avl_left, data, fcmp, 409 | &shortersubtree )) == 0 ) { 410 | *shorter = 0; 411 | return( 0 ); 412 | } 413 | 414 | /* left subtree shorter? */ 415 | if ( shortersubtree ) 416 | *shorter = left_balance( root ); 417 | else 418 | *shorter = 0; 419 | /* go right */ 420 | } else { 421 | if ( (savedata = ravl_delete( &(*root)->avl_right, data, fcmp, 422 | &shortersubtree )) == 0 ) { 423 | *shorter = 0; 424 | return( 0 ); 425 | } 426 | 427 | if ( shortersubtree ) 428 | *shorter = right_balance( root ); 429 | else 430 | *shorter = 0; 431 | } 432 | 433 | return( savedata ); 434 | } 435 | 436 | /* 437 | * avl_delete() - deletes the node containing data (according to fcmp) from 438 | * the avl tree rooted at root. 439 | */ 440 | 441 | void* 442 | avl_delete( Avlnode **root, void* data, AVL_CMP fcmp ) 443 | { 444 | int shorter; 445 | 446 | return( ravl_delete( root, data, fcmp, &shorter ) ); 447 | } 448 | 449 | static int 450 | avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) 451 | { 452 | if ( root == 0 ) 453 | return( AVL_NOMORE ); 454 | 455 | if ( root->avl_left != 0 ) 456 | if ( avl_inapply( root->avl_left, fn, arg, stopflag ) 457 | == stopflag ) 458 | return( stopflag ); 459 | 460 | if ( (*fn)( root->avl_data, arg ) == stopflag ) 461 | return( stopflag ); 462 | 463 | if ( root->avl_right == 0 ) 464 | return( AVL_NOMORE ); 465 | else 466 | return( avl_inapply( root->avl_right, fn, arg, stopflag ) ); 467 | } 468 | 469 | static int 470 | avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) 471 | { 472 | if ( root == 0 ) 473 | return( AVL_NOMORE ); 474 | 475 | if ( root->avl_left != 0 ) 476 | if ( avl_postapply( root->avl_left, fn, arg, stopflag ) 477 | == stopflag ) 478 | return( stopflag ); 479 | 480 | if ( root->avl_right != 0 ) 481 | if ( avl_postapply( root->avl_right, fn, arg, stopflag ) 482 | == stopflag ) 483 | return( stopflag ); 484 | 485 | return( (*fn)( root->avl_data, arg ) ); 486 | } 487 | 488 | static int 489 | avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) 490 | { 491 | if ( root == 0 ) 492 | return( AVL_NOMORE ); 493 | 494 | if ( (*fn)( root->avl_data, arg ) == stopflag ) 495 | return( stopflag ); 496 | 497 | if ( root->avl_left != 0 ) 498 | if ( avl_preapply( root->avl_left, fn, arg, stopflag ) 499 | == stopflag ) 500 | return( stopflag ); 501 | 502 | if ( root->avl_right == 0 ) 503 | return( AVL_NOMORE ); 504 | else 505 | return( avl_preapply( root->avl_right, fn, arg, stopflag ) ); 506 | } 507 | 508 | /* 509 | * avl_apply -- avl tree root is traversed, function fn is called with 510 | * arguments arg and the data portion of each node. if fn returns stopflag, 511 | * the traversal is cut short, otherwise it continues. Do not use -6 as 512 | * a stopflag, as this is what is used to indicate the traversal ran out 513 | * of nodes. 514 | */ 515 | 516 | int 517 | avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type ) 518 | { 519 | switch ( type ) { 520 | case AVL_INORDER: 521 | return( avl_inapply( root, fn, arg, stopflag ) ); 522 | case AVL_PREORDER: 523 | return( avl_preapply( root, fn, arg, stopflag ) ); 524 | case AVL_POSTORDER: 525 | return( avl_postapply( root, fn, arg, stopflag ) ); 526 | default: 527 | fprintf( stderr, "Invalid traversal type %d\n", type ); 528 | return( -1 ); 529 | } 530 | 531 | /* NOTREACHED */ 532 | } 533 | 534 | /* 535 | * avl_prefixapply - traverse avl tree root, applying function fprefix 536 | * to any nodes that match. fcmp is called with data as its first arg 537 | * and the current node's data as its second arg. it should return 538 | * 0 if they match, < 0 if data is less, and > 0 if data is greater. 539 | * the idea is to efficiently find all nodes that are prefixes of 540 | * some key... Like avl_apply, this routine also takes a stopflag 541 | * and will return prematurely if fmatch returns this value. Otherwise, 542 | * AVL_NOMORE is returned. 543 | */ 544 | 545 | int 546 | avl_prefixapply( 547 | Avlnode *root, 548 | void* data, 549 | AVL_CMP fmatch, 550 | void* marg, 551 | AVL_CMP fcmp, 552 | void* carg, 553 | int stopflag 554 | ) 555 | { 556 | int cmp; 557 | 558 | if ( root == 0 ) 559 | return( AVL_NOMORE ); 560 | 561 | cmp = (*fcmp)( data, root->avl_data /* , carg */); 562 | if ( cmp == 0 ) { 563 | if ( (*fmatch)( root->avl_data, marg ) == stopflag ) 564 | return( stopflag ); 565 | 566 | if ( root->avl_left != 0 ) 567 | if ( avl_prefixapply( root->avl_left, data, fmatch, 568 | marg, fcmp, carg, stopflag ) == stopflag ) 569 | return( stopflag ); 570 | 571 | if ( root->avl_right != 0 ) 572 | return( avl_prefixapply( root->avl_right, data, fmatch, 573 | marg, fcmp, carg, stopflag ) ); 574 | else 575 | return( AVL_NOMORE ); 576 | 577 | } else if ( cmp < 0 ) { 578 | if ( root->avl_left != 0 ) 579 | return( avl_prefixapply( root->avl_left, data, fmatch, 580 | marg, fcmp, carg, stopflag ) ); 581 | } else { 582 | if ( root->avl_right != 0 ) 583 | return( avl_prefixapply( root->avl_right, data, fmatch, 584 | marg, fcmp, carg, stopflag ) ); 585 | } 586 | 587 | return( AVL_NOMORE ); 588 | } 589 | 590 | /* 591 | * avl_free -- traverse avltree root, freeing the memory it is using. 592 | * the dfree() is called to free the data portion of each node. The 593 | * number of items actually freed is returned. 594 | */ 595 | 596 | int 597 | avl_free( Avlnode *root, AVL_FREE dfree ) 598 | { 599 | int nleft, nright; 600 | 601 | if ( root == 0 ) 602 | return( 0 ); 603 | 604 | nleft = nright = 0; 605 | if ( root->avl_left != 0 ) 606 | nleft = avl_free( root->avl_left, dfree ); 607 | 608 | if ( root->avl_right != 0 ) 609 | nright = avl_free( root->avl_right, dfree ); 610 | 611 | if ( dfree ) 612 | (*dfree)( root->avl_data ); 613 | free( root ); 614 | 615 | return( nleft + nright + 1 ); 616 | } 617 | 618 | /* 619 | * avl_find -- search avltree root for a node with data data. the function 620 | * cmp is used to compare things. it is called with data as its first arg 621 | * and the current node data as its second. it should return 0 if they match, 622 | * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. 623 | */ 624 | 625 | Avlnode* 626 | avl_find( Avlnode *root, const void* data, AVL_CMP fcmp ) 627 | { 628 | int cmp; 629 | 630 | while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { 631 | if ( cmp < 0 ) 632 | root = root->avl_left; 633 | else 634 | root = root->avl_right; 635 | } 636 | 637 | return( root ); 638 | } 639 | 640 | /* 641 | * avl_find_lin -- search avltree root linearly for a node with data data. 642 | * the function cmp is used to compare things. it is called with data as its 643 | * first arg and the current node data as its second. it should return 0 if 644 | * they match, non-zero otherwise. 645 | */ 646 | 647 | void* 648 | avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp ) 649 | { 650 | void* res; 651 | 652 | if ( root == 0 ) 653 | return( NULL ); 654 | 655 | if ( (*fcmp)( data, root->avl_data ) == 0 ) 656 | return( root->avl_data ); 657 | 658 | if ( root->avl_left != 0 ) 659 | if ( (res = avl_find_lin( root->avl_left, data, fcmp )) 660 | != NULL ) 661 | return( res ); 662 | 663 | if ( root->avl_right == 0 ) 664 | return( NULL ); 665 | else 666 | return( avl_find_lin( root->avl_right, data, fcmp ) ); 667 | } 668 | 669 | /* NON-REENTRANT INTERFACE */ 670 | 671 | static void* *avl_list; 672 | static int avl_maxlist; 673 | static int avl_nextlist; 674 | 675 | #define AVL_GRABSIZE 100 676 | 677 | /* ARGSUSED */ 678 | static int 679 | avl_buildlist( void* data, void* arg ) 680 | { 681 | static int slots; 682 | 683 | if ( avl_list == (void* *) 0 ) { 684 | avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*)); 685 | slots = AVL_GRABSIZE; 686 | avl_maxlist = 0; 687 | } else if ( avl_maxlist == slots ) { 688 | slots += AVL_GRABSIZE; 689 | avl_list = (void* *) ber_memrealloc( (char *) avl_list, 690 | (unsigned) slots * sizeof(void*)); 691 | } 692 | 693 | avl_list[ avl_maxlist++ ] = data; 694 | 695 | return( 0 ); 696 | } 697 | 698 | /* 699 | * avl_getfirst() and avl_getnext() are provided as alternate tree 700 | * traversal methods, to be used when a single function cannot be 701 | * provided to be called with every node in the tree. avl_getfirst() 702 | * traverses the tree and builds a linear list of all the nodes, 703 | * returning the first node. avl_getnext() returns the next thing 704 | * on the list built by avl_getfirst(). This means that avl_getfirst() 705 | * can take a while, and that the tree should not be messed with while 706 | * being traversed in this way, and that multiple traversals (even of 707 | * different trees) cannot be active at once. 708 | */ 709 | 710 | void* 711 | avl_getfirst( Avlnode *root ) 712 | { 713 | if ( avl_list ) { 714 | free( (char *) avl_list); 715 | avl_list = (void* *) 0; 716 | } 717 | avl_maxlist = 0; 718 | avl_nextlist = 0; 719 | 720 | if ( root == 0 ) 721 | return( 0 ); 722 | 723 | (void) avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER ); 724 | 725 | return( avl_list[ avl_nextlist++ ] ); 726 | } 727 | 728 | void* 729 | avl_getnext( void ) 730 | { 731 | if ( avl_list == 0 ) 732 | return( 0 ); 733 | 734 | if ( avl_nextlist == avl_maxlist ) { 735 | free( (void*) avl_list); 736 | avl_list = (void* *) 0; 737 | return( 0 ); 738 | } 739 | 740 | return( avl_list[ avl_nextlist++ ] ); 741 | } 742 | 743 | /* end non-reentrant code */ 744 | 745 | 746 | int 747 | avl_dup_error( void* left, void* right ) 748 | { 749 | return( -1 ); 750 | } 751 | 752 | int 753 | avl_dup_ok( void* left, void* right ) 754 | { 755 | return( 0 ); 756 | } 757 | -------------------------------------------------------------------------------- /avl.h: -------------------------------------------------------------------------------- 1 | /* $OpenLDAP: pkg/ldap/include/avl.h,v 1.19 2001/05/29 01:29:57 kurt Exp $ */ 2 | /* 3 | * Copyright 1998-2001 The OpenLDAP Foundation, Redwood City, California, USA 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. A copy of this license is available at 9 | * http://www.OpenLDAP.org/license.html or in file LICENSE in the 10 | * top-level directory of the distribution. 11 | */ 12 | /* Portions 13 | * Copyright (c) 1993 Regents of the University of Michigan. 14 | * All rights reserved. 15 | * 16 | * Redistribution and use in source and binary forms are permitted 17 | * provided that this notice is preserved and that due credit is given 18 | * to the University of Michigan at Ann Arbor. The name of the University 19 | * may not be used to endorse or promote products derived from this 20 | * software without specific prior written permission. This software 21 | * is provided ``as is'' without express or implied warranty. 22 | */ 23 | /* avl.h - avl tree definitions */ 24 | 25 | 26 | #ifndef _AVL 27 | #define _AVL 28 | 29 | /* 30 | * this structure represents a generic avl tree node. 31 | */ 32 | 33 | typedef struct avlnode Avlnode; 34 | 35 | struct avlnode { 36 | void* avl_data; 37 | signed int avl_bf; 38 | struct avlnode *avl_left; 39 | struct avlnode *avl_right; 40 | }; 41 | 42 | #define NULLAVL ((Avlnode *) NULL) 43 | 44 | /* balance factor values */ 45 | #define LH (-1) 46 | #define EH 0 47 | #define RH 1 48 | 49 | /* avl routines */ 50 | #define avl_getone(x) ((x) == 0 ? 0 : (x)->avl_data) 51 | #define avl_onenode(x) ((x) == 0 || ((x)->avl_left == 0 && (x)->avl_right == 0)) 52 | 53 | typedef int (*AVL_APPLY) (void *, void*); 54 | typedef int (*AVL_CMP) (const void*, const void*); 55 | typedef int (*AVL_DUP) (void*, void*); 56 | typedef void (*AVL_FREE) (void*); 57 | 58 | int 59 | avl_free ( Avlnode *root, AVL_FREE dfree ); 60 | 61 | int 62 | avl_insert (Avlnode **, void*, AVL_CMP, AVL_DUP); 63 | 64 | void* 65 | avl_delete (Avlnode **, void*, AVL_CMP); 66 | 67 | Avlnode* 68 | avl_find (Avlnode *, const void*, AVL_CMP); 69 | 70 | void* 71 | avl_find_lin (Avlnode *, const void*, AVL_CMP); 72 | 73 | #ifdef AVL_NONREENTRANT 74 | void* 75 | avl_getfirst (Avlnode *); 76 | 77 | void* 78 | avl_getnext (void); 79 | #endif 80 | 81 | int 82 | avl_dup_error (void*, void*); 83 | 84 | int 85 | avl_dup_ok (void*, void*); 86 | 87 | int 88 | avl_apply (Avlnode *, AVL_APPLY, void*, int, int); 89 | 90 | int 91 | avl_prefixapply (Avlnode *, void*, AVL_CMP, void*, AVL_CMP, void*, int); 92 | 93 | /* apply traversal types */ 94 | #define AVL_PREORDER 1 95 | #define AVL_INORDER 2 96 | #define AVL_POSTORDER 3 97 | /* what apply returns if it ran out of nodes */ 98 | #define AVL_NOMORE (-6) 99 | 100 | #endif /* _AVL */ 101 | -------------------------------------------------------------------------------- /mdname.c: -------------------------------------------------------------------------------- 1 | /* malloc tracer for memory leak tracking 2 | * This file contains routines to map addresses to object files, 3 | * symbol names, and source locations. 4 | * -- Howard Chu, hyc@symas.com 2015-03-24 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | /* FIXME: this file is a part of binutils. But binutils 14 | doesn't install it in a standard include/ directory! 15 | Might have to modify the include path to locate it. 16 | (needed for function cplus_demangle()) */ 17 | #include 18 | 19 | #include "avl.h" 20 | #include "mdump.h" 21 | 22 | /* texts for special values (functions, files...) */ 23 | char *md_ndef_function=""; 24 | char *md_ndef_file=""; 25 | char *md_ndef_object=""; 26 | char *md_mem_function=""; 27 | char *md_mem_file=""; 28 | char *md_mem_object=""; 29 | char *md_before_function=""; 30 | char *md_after_function=""; 31 | char *md_baddr_function=""; 32 | char *md_aaddr_function=""; 33 | char *md_err_memory=""; 34 | char *md_err_ndef=""; 35 | 36 | 37 | 38 | /* struct to keep track of names for demangling purposes */ 39 | typedef struct 40 | { 41 | char *name; 42 | int done; 43 | }MDNym; 44 | 45 | /* address of a line of a function */ 46 | typedef struct 47 | { 48 | void *addr; 49 | unsigned int line; 50 | }MDLin; 51 | 52 | /* temp structure to extract names from .so files */ 53 | typedef struct 54 | { 55 | void *addr; 56 | char *name; 57 | char *file; 58 | MDLin *lines; 59 | int nb_lines; 60 | flagword flag; /* corresponding flag */ 61 | MDNym *xname; 62 | }MDSym; 63 | MDSym *md_syms=NULL; 64 | int md_nb_syms=0; 65 | 66 | 67 | /* this structure is devoted to contains each address of 68 | symbols to be treated. corresponding name, file and line 69 | are specified here, in order to be updated by the program */ 70 | typedef struct 71 | { 72 | MD_Loc *loc; 73 | MDNym *xname; /* address of master name ptr */ 74 | int done; 75 | }HXRequest; 76 | 77 | 78 | /* add an entry to the request list */ 79 | HXRequest* md_add_request(HXRequest *reqlist, int *nb, MD_Loc *loc) 80 | { 81 | if (*nb == 0) 82 | { 83 | reqlist = malloc(sizeof(HXRequest)); 84 | } 85 | else 86 | { 87 | reqlist = realloc(reqlist, sizeof(HXRequest)*((*nb)+1)); 88 | } 89 | if (reqlist == NULL) 90 | { 91 | fprintf(stderr, "Out of memory in name extraction\n"); 92 | return(NULL); 93 | } 94 | 95 | loc->name = NULL; 96 | loc->object = NULL; 97 | loc->file = NULL; 98 | loc->line = 0; 99 | loc->valid = 0; 100 | 101 | reqlist[*nb].loc = loc; 102 | reqlist[*nb].done = 0; 103 | reqlist[*nb].xname = NULL; 104 | 105 | (*nb)++; 106 | return(reqlist); 107 | } 108 | 109 | 110 | 111 | /* return pointer on beginning of file basename */ 112 | char *md_get_basename(char *name) 113 | { 114 | char *ptr; 115 | 116 | if (name == NULL) 117 | return(NULL); 118 | if (name[0] == 0) 119 | return(name); 120 | /* return the 1st '/' from the end */ 121 | ptr = strrchr(name, '/'); 122 | if (ptr && ptr[1]) 123 | return ptr+1; 124 | return(name); 125 | } 126 | 127 | 128 | /* add a 'm' before the name */ 129 | int md_compute_new_name(char *buf, char *oname) 130 | { 131 | int i; 132 | 133 | /* search for the '/' (if exist) */ 134 | for(i=strlen(oname)-1; i>=0; i--) 135 | { 136 | if (oname[i] == '/') 137 | break; 138 | } 139 | /* no '/'. just do m%s */ 140 | if (oname[i] != '/') 141 | { 142 | sprintf(buf, "m%s", oname); 143 | return(1); 144 | } 145 | /* copy the starting elements */ 146 | strncpy(buf, oname, i+1); 147 | buf[i+1] = '\0'; 148 | strcat(buf, "m"); 149 | strcat(buf, &(oname[i+1])); 150 | return(1); 151 | } 152 | 153 | void md_demangle(MDNym *xname, int options) 154 | { 155 | char *tmp; 156 | 157 | if (xname->done) 158 | return; 159 | xname->done = 1; 160 | if (options & MD_NO_DEMANGLE) 161 | return; 162 | tmp = cplus_demangle(xname->name, DMGL_ANSI | DMGL_PARAMS); 163 | if (tmp && tmp != xname->name) 164 | { 165 | free(xname->name); 166 | xname->name = tmp; 167 | } 168 | } 169 | 170 | int md_open_bfd_file(char *name, bfd **core_bfd, int *core_num_syms, 171 | asection **core_text_sect, asymbol ***core_syms) 172 | { 173 | *core_bfd = bfd_openr(name, 0); 174 | if (!(*core_bfd)) 175 | { 176 | fprintf(stderr, "fncdump: Cant open %s\n", name); 177 | return(0); 178 | } 179 | /* check format */ 180 | if (!bfd_check_format(*core_bfd, bfd_object)) 181 | { 182 | fprintf(stderr, "fncdump: File %s is not an object.", name); 183 | return(0); 184 | } 185 | /* get TEXT section */ 186 | *core_text_sect = bfd_get_section_by_name(*core_bfd, ".text"); 187 | if (!(*core_text_sect)) 188 | { 189 | *core_text_sect = bfd_get_section_by_name (*core_bfd, "$CODE$"); 190 | if (!(*core_text_sect)) 191 | { 192 | fprintf(stderr, "fncdump: No TEXT section in object %s.\n", name); 193 | return(0); 194 | } 195 | } 196 | /* read symbol table */ 197 | *core_num_syms = bfd_get_symtab_upper_bound(*core_bfd); 198 | if (*core_num_syms < 0) 199 | { 200 | fprintf(stderr, "fncdump: %s\n", bfd_errmsg(bfd_get_error())); 201 | return(0); 202 | } 203 | *core_syms = (asymbol **) malloc(sizeof(asymbol*)*(*core_num_syms)); 204 | if (*core_syms == NULL) 205 | { 206 | fprintf(stderr, "fncdump: Memory error while allocating %d bytes.\n", (int)sizeof(asymbol*)*(*core_num_syms)); 207 | fprintf(stderr, "fncdump: Fatal error!\n"); 208 | exit(9); 209 | } 210 | *core_num_syms = bfd_canonicalize_symtab(*core_bfd, *core_syms); 211 | if (*core_num_syms < 0) 212 | { 213 | free(*core_syms); 214 | fprintf(stderr, "fncdump: %s\n", bfd_errmsg(bfd_get_error())); 215 | return(0); 216 | } 217 | return(1); 218 | } 219 | 220 | int md_compare_names(const void *e1, const void *e2) 221 | { 222 | char *n1 = ((MDSym *)e1)->name; 223 | char *n2 = ((MDSym *)e2)->name; 224 | if (!n1) n1 = ""; 225 | if (!n2) n2 = ""; 226 | return strcmp(n1, n2); 227 | } 228 | int md_compare_pointers(const void *e1, const void *e2) 229 | { 230 | long l = ((MDSym*)e1)->addr - ((MDSym*)e2)->addr; 231 | return l < 0 ? -1 : l > 0; 232 | } 233 | 234 | /* sort in descending order */ 235 | int md_compare_dynobj(const void *e1, const void *e2) 236 | { 237 | MD_DynObj **d1 = (MD_DynObj **)e1, **d2 = (MD_DynObj **)e2; 238 | long l = (*d2)->base - (*d1)->base; 239 | return l < 0 ? -1 : l > 0; 240 | } 241 | 242 | int md_init_extract_dynamic(int core_num_syms, asymbol **core_syms, unsigned long base, int sortn) 243 | { 244 | int i, j; 245 | 246 | if ((md_syms = malloc(sizeof(MDSym)*core_num_syms)) == NULL) 247 | { 248 | fprintf(stderr, "fncdump: Recoverable memory error.\n"); 249 | return(0); 250 | } 251 | /* put addr/names in table */ 252 | for(i=0,j=0; iflags & BSF_FILE) continue; 255 | md_syms[j].name = (char*)bfd_asymbol_name(core_syms[i]); 256 | md_syms[j].addr = (void*)bfd_asymbol_value(core_syms[i]) + base; 257 | md_syms[j].flag = core_syms[i]->flags; 258 | md_syms[j].xname = NULL; 259 | md_syms[j].lines = NULL; 260 | md_syms[j].nb_lines = 0; 261 | md_syms[j].file = NULL; 262 | #if 0 263 | printf("%p %s ", md_syms[j].addr, md_syms[j].name ? md_syms[j].name : "??"); 264 | if (core_syms[i]->flags & BSF_GLOBAL) 265 | printf("BFD_GLOBAL "); 266 | if (core_syms[i]->flags & BSF_LOCAL) 267 | printf("BSF_LOCAL "); 268 | if (core_syms[i]->flags & BSF_FUNCTION) 269 | printf("BSF_FUNCTION "); 270 | if (core_syms[i]->flags & BSF_WEAK) 271 | printf("BSF_WEAK "); 272 | if (core_syms[i]->flags & BSF_INDIRECT) 273 | printf("BSF_INDIRECT "); 274 | if (core_syms[i]->flags & BSF_FILE) 275 | printf("BSF_FILE "); 276 | if (core_syms[i]->flags & BSF_DYNAMIC) 277 | printf("BSF_DYNAMIC "); 278 | if (core_syms[i]->flags & BSF_OBJECT) 279 | printf("BSF_OBJECT "); 280 | printf("\n"); 281 | #endif 282 | j++; 283 | } 284 | md_nb_syms = j; /* core_num_syms; */ 285 | /* sort it by pointer value */ 286 | if (sortn) 287 | qsort(md_syms, md_nb_syms, sizeof(MDSym), md_compare_pointers); 288 | else 289 | qsort(md_syms, md_nb_syms, sizeof(MDSym), md_compare_names); 290 | return(1); 291 | } 292 | int md_fini_extract_dynamic() 293 | { 294 | if (md_syms != NULL) 295 | free(md_syms); 296 | md_syms = NULL; 297 | md_nb_syms = 0; 298 | return(1); 299 | } 300 | int md_extract_dynamic(HXRequest *req, int *idx) 301 | { 302 | int i, lo, hi; 303 | 304 | req->loc->valid = 0; 305 | if (md_syms == NULL) 306 | return(0); 307 | if (req->loc->addr < md_syms[0].addr || 308 | req->loc->addr > md_syms[md_nb_syms-1].addr) 309 | return(0); 310 | /* I may use a better search method :o) */ 311 | /* Uses binsearch now! */ 312 | for (lo=0, hi=md_nb_syms-1; lo<=hi;) 313 | { 314 | i = (lo+hi) >> 1; 315 | if ((req->loc->addr >= md_syms[i].addr) && 316 | (req->loc->addr < md_syms[i+1].addr)) 317 | { 318 | if (!md_syms[i].xname) 319 | { 320 | md_syms[i].xname = (MDNym *)malloc(sizeof(MDNym)); 321 | md_syms[i].name = strdup(md_syms[i].name); 322 | md_syms[i].xname->name = md_syms[i].name; 323 | md_syms[i].xname->done = 0; 324 | } 325 | req->xname = md_syms[i].xname; 326 | req->loc->name = md_syms[i].name; 327 | req->loc->valid = 1; 328 | *idx = i; 329 | return(1); 330 | } 331 | if (md_syms[i].addr > req->loc->addr) 332 | hi = i-1; 333 | else 334 | lo = i+1; 335 | } 336 | /* may not occur */ 337 | return(0); 338 | } 339 | 340 | static char **files; 341 | static int numfiles; 342 | 343 | char *md_dup_file(char *file, int options) 344 | { 345 | int lo, hi, i, j; 346 | 347 | if (file == NULL) 348 | return md_ndef_file; 349 | 350 | for (j=1, i=0, lo=0, hi=numfiles-1; lo<=hi;) 351 | { 352 | i = (lo+hi) >> 1; 353 | j = strcmp(files[i], file); 354 | if (j == 0) 355 | break; 356 | if (j>0) 357 | hi = i-1; 358 | else 359 | lo = i+1; 360 | } 361 | if (j) 362 | { 363 | files = realloc(files, (numfiles+1)*sizeof(char *)); 364 | file = strdup(file); 365 | if (i>0 && j>0) 366 | --i; 367 | for (j=numfiles;j>i;j--) 368 | files[j] = files[j-1]; 369 | files[j] = file; 370 | numfiles++; 371 | } 372 | file = files[i]; 373 | if (!(options & MD_FULLNAME)) 374 | return md_get_basename(file); 375 | else 376 | return file; 377 | } 378 | char *md_set_object(char *obj, int options) 379 | { 380 | if (obj == NULL) 381 | return(md_ndef_object); 382 | if (!(options & MD_FULLNAME)) 383 | return md_get_basename(obj); 384 | else 385 | return obj; 386 | } 387 | 388 | void md_find_line(bfd *core_bfd, asection *core_text_sect, 389 | asymbol **core_syms, bfd_vma vaddr,HXRequest *req,int sym,int options) 390 | { 391 | int i; 392 | 393 | for (i=0;iloc->addr) 396 | break; 397 | } 398 | if (i == md_syms[sym].nb_lines) 399 | { 400 | char *file, *func; 401 | 402 | md_syms[sym].lines = realloc(md_syms[sym].lines, 403 | (md_syms[sym].nb_lines+1)*sizeof(MDLin)); 404 | md_syms[sym].nb_lines++; 405 | md_syms[sym].lines[i].addr = req->loc->addr; 406 | bfd_find_nearest_line(core_bfd, core_text_sect, core_syms, 407 | (bfd_vma)vaddr - core_text_sect->vma - 1, 408 | (const char**)&file, (const char**)&func, 409 | &md_syms[sym].lines[i].line); 410 | if (file && !md_syms[sym].file) 411 | md_syms[sym].file = md_dup_file(file, options); 412 | } 413 | req->loc->file = md_syms[sym].file; 414 | req->loc->line = md_syms[sym].lines[i].line; 415 | } 416 | 417 | static int nb_reqlist; 418 | 419 | int md_add_memreq(MD_Mem *me, HXRequest **reqlist) 420 | { 421 | int i; 422 | 423 | for(;me;me=me->anext) 424 | { 425 | if (me->where_a != NULL) 426 | { 427 | *reqlist = md_add_request(*reqlist, &nb_reqlist, 428 | (MD_Loc *)&me->where_a); 429 | } 430 | if (me->where_f != NULL) 431 | { 432 | *reqlist = md_add_request(*reqlist, &nb_reqlist, 433 | (MD_Loc *)&me->where_f); 434 | } 435 | /* names for memory stack */ 436 | for(i=0; inb_stack_a; i++) 437 | { 438 | if (me->stack_a[i].addr == NULL) 439 | break; 440 | *reqlist = md_add_request(*reqlist, &nb_reqlist, 441 | &me->stack_a[i]); 442 | } 443 | for(i=0; inb_stack_f; i++) 444 | { 445 | if (me->stack_f[i].addr == NULL) 446 | break; 447 | *reqlist = md_add_request(*reqlist, &nb_reqlist, 448 | &me->stack_f[i]); 449 | } 450 | } 451 | return 0; 452 | } 453 | 454 | /* extract functions name from symbol addresses */ 455 | /* options: options of fncdump 456 | exec: the executable name 457 | */ 458 | int md_extract_names(int options, char *exec) 459 | { 460 | int i, j, k; 461 | HXRequest *reqlist=NULL; 462 | /* BFD data for exec */ 463 | bfd *core_bfd; 464 | asection *core_text_sect; 465 | int core_num_syms; 466 | asymbol **core_syms; 467 | /* temp data */ 468 | int not_all_done=0; 469 | char *object; 470 | 471 | /* read names for memory tracking */ 472 | avl_apply(md_mems, (AVL_APPLY)md_add_memreq, (void *)&reqlist, -1, 473 | AVL_INORDER); 474 | 475 | /* now open the executable */ 476 | if (!md_open_bfd_file(exec, &core_bfd, &core_num_syms, 477 | &core_text_sect, &core_syms)) 478 | { 479 | if (reqlist != NULL) 480 | free(reqlist); 481 | return(0); 482 | } 483 | /* we extract function names with our method, and after we extract 484 | the file:line information. */ 485 | md_init_extract_dynamic(core_num_syms, core_syms, (unsigned long)md_objects[0]->base, 1); 486 | object = md_set_object(exec, options); 487 | for(i=0; iobject = object; 494 | reqlist[i].done = 1; 495 | md_find_line(core_bfd,core_text_sect,core_syms,(bfd_vma)reqlist[i].loc->addr - (unsigned long)md_objects[0]->base, 496 | &reqlist[i],j,options); 497 | } 498 | else 499 | not_all_done = 1; 500 | } 501 | md_fini_extract_dynamic(); 502 | /* close this bfd */ 503 | bfd_close(core_bfd); 504 | free(core_syms); 505 | 506 | /* some symbols are not completed */ 507 | /* the best way, here, would be to obtain the object name from 508 | where the symbol comes... as I dont know how to do that, 509 | I search in all objects */ 510 | if (not_all_done && md_nobjects) 511 | { 512 | /* Sort the objects in descending order */ 513 | qsort(md_objects, md_nobjects, sizeof(MD_DynObj*), md_compare_dynobj); 514 | 515 | for (k=0; not_all_done && kpath[0]) continue; 518 | /* now open the object */ 519 | if (!md_open_bfd_file(md_objects[k]->path, &core_bfd, &core_num_syms, 520 | &core_text_sect, &core_syms)) 521 | { 522 | continue; 523 | } 524 | object = md_set_object(md_objects[k]->path, options); 525 | /* search for unmatched symbols */ 526 | md_init_extract_dynamic(core_num_syms, core_syms, (unsigned long)md_objects[k]->base, 0); 527 | not_all_done = 0; 528 | for(i=0; iaddr < md_objects[k]->base) { 534 | not_all_done = 1; 535 | continue; 536 | } 537 | /* Search for a valid name but missing object/file info */ 538 | if (reqlist[i].loc->valid) 539 | { 540 | int lo, hi, n = 0; 541 | /* search if a symbol matches the given element */ 542 | for(lo=0, hi=md_nb_syms-1; lo<=hi;) 543 | { 544 | j = (lo+hi)>>1; 545 | n = strcmp(md_syms[j].name, reqlist[i].loc->name); 546 | if (n==0) 547 | break; 548 | if (n>0) 549 | hi = j-1; 550 | else 551 | lo = j+1; 552 | } 553 | if (n==0 && 554 | /* (md_syms[j].flag & (BSF_GLOBAL|BSF_WEAK))&& */ 555 | (md_syms[j].flag & BSF_FUNCTION)) 556 | {/* it's the same name AND it is a function that is exported! */ 557 | reqlist[i].loc->object = object; 558 | reqlist[i].done = 1; 559 | md_find_line(core_bfd, core_text_sect, core_syms, 560 | (bfd_vma)reqlist[i].loc->addr - (unsigned long)md_objects[k]->base, &reqlist[i], j, options); 561 | } 562 | else 563 | not_all_done = 1; 564 | } 565 | else 566 | not_all_done = 1; 567 | } 568 | } 569 | if (not_all_done) 570 | { 571 | /* Search for matching addresses, using the object base address */ 572 | not_all_done = 0; 573 | qsort(md_syms, md_nb_syms, sizeof(MDSym), md_compare_pointers); 574 | for (i=0; ivalid) 577 | continue; 578 | /* Only check addresses that can reside in this module */ 579 | if (reqlist[i].loc->addr < md_objects[k]->base) 580 | { 581 | not_all_done = 1; 582 | continue; 583 | } 584 | if (k && reqlist[i].loc->addr < md_objects[k-1]->base) 585 | reqlist[i].loc->object = object; 586 | if (md_extract_dynamic(&reqlist[i], &j) && 587 | (md_syms[j].flag & (BSF_GLOBAL|BSF_LOCAL|BSF_WEAK))) 588 | { 589 | reqlist[i].done = 1; 590 | md_find_line(core_bfd, core_text_sect, core_syms, 591 | (bfd_vma)(reqlist[i].loc->addr - md_objects[k]->base), 592 | &reqlist[i], j, options); 593 | } 594 | else 595 | not_all_done = 1; 596 | } 597 | } 598 | 599 | md_fini_extract_dynamic(); 600 | /* close this bfd */ 601 | bfd_close(core_bfd); 602 | free(core_syms); 603 | } 604 | } 605 | 606 | /* add a default name for all unknown symbols */ 607 | for(i=0; ifile = md_ndef_file; 612 | if (!reqlist[i].loc->object) 613 | reqlist[i].loc->object = md_ndef_object; 614 | } 615 | if (reqlist[i].xname) 616 | { 617 | if (!reqlist[i].xname->done) 618 | md_demangle(reqlist[i].xname, options); 619 | reqlist[i].loc->name = reqlist[i].xname->name; 620 | } 621 | } 622 | 623 | /* terminated */ 624 | if (reqlist != NULL) 625 | free(reqlist); 626 | return(1); 627 | } 628 | -------------------------------------------------------------------------------- /mdump.c: -------------------------------------------------------------------------------- 1 | /* malloc tracer for memory leak tracking 2 | * This program reads the output generated by mleak.so 3 | * and prints the leak report. 4 | * -- Howard Chu, hyc@symas.com 2015-03-24 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "avl.h" 16 | #include "mleak.h" 17 | #include "mdump.h" 18 | 19 | /* mdname.c */ 20 | extern int md_extract_names(int options, char *exec); 21 | 22 | /* object files */ 23 | MD_DynObj **md_objects; 24 | int md_nobjects; 25 | 26 | /* blocks of memory */ 27 | Avlnode *md_mems; 28 | unsigned int blocks; 29 | 30 | /* memory count. after counting, this is the memory leak */ 31 | long md_nb_mem_used=0; 32 | long md_nb_mem_max=0; /* gives the real MAX memory used */ 33 | /* length of the memory used */ 34 | long md_nb_heap_size=0; 35 | 36 | static void *md_heap_start=(void*)-1; 37 | static void *md_heap_end=0; 38 | 39 | static int 40 | md_index_cmp(MD_Mem *m1, MD_Mem *m2) 41 | { 42 | /* sort by stack depth first, then stack addrs */ 43 | long l; 44 | int i; 45 | l = (int)m1->nb_stack_a - (int)m2->nb_stack_a; 46 | if (l) 47 | return l < 0 ? -1 : l > 0; 48 | l = (char *)m1->where_a - (char *)m2->where_a; 49 | if (l) 50 | return l < 0 ? -1 : l > 0; 51 | for (i=0; inb_stack_a; i++) { 52 | l = (char *)m1->stack_a[i].addr - (char *)m2->stack_a[i].addr; 53 | if (l) 54 | return l < 0 ? -1 : l > 0; 55 | } 56 | return l; 57 | } 58 | 59 | /* search for the given record */ 60 | static Avlnode *md_search_pointer(MD_Mem *me) 61 | { 62 | return avl_find(md_mems, me, (AVL_CMP) md_index_cmp); 63 | } 64 | 65 | static void md_update_heap(void *ptr, unsigned int size) 66 | { 67 | md_heap_start = MD_MIN(md_heap_start, ptr); 68 | md_heap_end = MD_MAX(md_heap_end, ptr+size); 69 | md_nb_heap_size = md_heap_end - md_heap_start; 70 | } 71 | 72 | /* add new malloc entry */ 73 | static MD_Mem *md_add_malloc(Avlnode *av, ml_rec2 *mr, MD_Loc *stack) 74 | { 75 | MD_Mem mt = {0}; 76 | MD_Mem *me = &mt; 77 | 78 | me->ptr = mr->addr; 79 | 80 | me->block = 1; 81 | me->where_a = stack->addr; 82 | me->size_a = mr->size; 83 | me->stack_a = stack+1; 84 | me->nb_stack_a = mr->nstk-2; 85 | 86 | if (!av) 87 | av = md_search_pointer(me); 88 | if (!av) { 89 | me = calloc(1, sizeof(*me)); 90 | *me = mt; 91 | avl_insert( &md_mems, me, (AVL_CMP)md_index_cmp, avl_dup_error ); 92 | blocks++; 93 | } else { 94 | me = av->avl_data; 95 | me->block++; 96 | me->size_a += mr->size; 97 | } 98 | 99 | /* the current memory size */ 100 | md_nb_mem_used += mr->size; 101 | /* store the MAX of memory allocated */ 102 | md_nb_mem_max = MD_MAX(md_nb_mem_used, md_nb_mem_max); 103 | /* compute the memory block size */ 104 | md_update_heap(mr->addr, mr->size); 105 | return me; 106 | } 107 | 108 | 109 | static MD_Loc *md_expand_stack(int nstk, void **stk) 110 | { 111 | MD_Loc *ml; 112 | int i; 113 | stk++; /* skip ml_backtrace stack frame */ 114 | nstk--; 115 | ml = malloc(nstk * sizeof(MD_Loc)); 116 | for (i=0; icode) { 144 | void **stk; 145 | if (mr->code == ALLOC) { 146 | ml_rec2 *m2 = (ml_rec2 *)mr; 147 | stk = (void **)(m2+1); 148 | cstack = md_expand_stack(mr->nstk, stk); 149 | md_add_malloc(NULL, m2, cstack); 150 | } 151 | mr = (ml_rec *)(stk + mr->nstk); 152 | } 153 | 154 | /* done */ 155 | return(1); 156 | } 157 | 158 | static void 159 | md_read_info(char *file) 160 | { 161 | void *base; 162 | int len; 163 | int fd = open(file, O_RDONLY); 164 | 165 | while (read(fd, &base, sizeof(base)) == sizeof(base)) { 166 | MD_DynObj *mo; 167 | read(fd, &len, sizeof(len)); 168 | mo = malloc(sizeof(*mo) + len); 169 | mo->base = base; 170 | read(fd, mo->path, len+1); 171 | mo->path[len] = '\0'; 172 | md_objects = realloc(md_objects, (md_nobjects+1) * sizeof(MD_DynObj *)); 173 | md_objects[md_nobjects++] = mo; 174 | } 175 | close(fd); 176 | } 177 | 178 | #include "mx.c" 179 | 180 | int main(int argc, char *argv[]) 181 | { 182 | int i; 183 | char *exec = argv[1]; 184 | 185 | for (i=2; i(b))?(a):(b)) 7 | #define MD_MIN(a,b) (((a)<(b))?(a):(b)) 8 | #define ABS(v) ((v)<0?-(v):(v)) 9 | /* macros to display (potentially null) names */ 10 | /* function name */ 11 | #define MD_DNAME(n) (((n)==NULL)?"??":(n)) 12 | /* file */ 13 | #define MD_DFILE(n) (((n)==NULL)?"??":(n)) 14 | /* object */ 15 | #define MD_DOBJ(n) (((n)==NULL)?"??.so":(n)) 16 | /* string buffers */ 17 | #define MD_MAX_BUFFER 1024 18 | 19 | #define MD_MORE 1 20 | #define MD_NO_DEMANGLE 2 21 | #define MD_FULLNAME 4 22 | #define MD_NO_DECORATION 8 23 | #define MD_MEMORY_LINE 0x10 24 | #define MD_NO_UNRES_MALLOC 0x20 25 | #define MD_NO_UNRES_REALLOC 0x40 26 | #define MD_NO_UNRES_FREE 0x80 27 | 28 | /* Description of a function or call address */ 29 | /* This structure is overlaid on some of the others. */ 30 | typedef struct 31 | { 32 | void *addr; /* address of event */ 33 | char *name; /* name of function containing address */ 34 | char *object; /* path to object containing address */ 35 | char *file; /* source file containing the function */ 36 | int line; /* line number in source file */ 37 | int valid; /* is this name ok? */ 38 | }MD_Loc; 39 | 40 | typedef struct MD_Mem 41 | { 42 | void *ptr; 43 | unsigned int size_a; 44 | unsigned int block; 45 | 46 | void *where_a; /* allocation place */ 47 | char *func_a; /* corresp. function */ 48 | char *object_a; /* corresp. object */ 49 | char *file_a; /* corresp. file:line */ 50 | int line_a; 51 | int valid_a; 52 | 53 | /* call stack */ 54 | MD_Loc *stack_a; 55 | unsigned int nb_stack_a; 56 | 57 | void *where_f; /* freeing place */ 58 | char *func_f; /* corresp. function */ 59 | char *object_f; /* corresp. object */ 60 | char *file_f; /* corresp. file:line */ 61 | int line_f; 62 | int valid_f; 63 | 64 | /* stack at the free */ 65 | MD_Loc *stack_f; 66 | unsigned int nb_stack_f; 67 | 68 | struct MD_Mem *rnext; /* realloc list */ 69 | struct MD_Mem *rprev; /* prior alloc */ 70 | struct MD_Mem *anext; /* next at this addr */ 71 | }MD_Mem; 72 | 73 | /* a dynamic object */ 74 | typedef struct 75 | { 76 | void *base; 77 | char path[1]; 78 | }MD_DynObj; 79 | 80 | /** variables **/ 81 | extern int md_nobjects; 82 | extern MD_DynObj **md_objects; 83 | 84 | extern Avlnode *md_mems; 85 | -------------------------------------------------------------------------------- /mleak.c: -------------------------------------------------------------------------------- 1 | /* malloc tracer for memory leak tracking 2 | * -- Howard Chu, hyc@symas.com 2015-03-24 3 | */ 4 | #define _GNU_SOURCE /* need this to get RTLD_NEXT defined */ 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #define UNW_LOCAL_ONLY 19 | #include 20 | 21 | #include "mleak.h" 22 | 23 | #ifndef ML_STACK 24 | #define ML_STACK 24 25 | #endif 26 | int ml_stacknum = ML_STACK; /* length of stack trace */ 27 | 28 | static int ml_initing; 29 | 30 | /* hooks */ 31 | typedef void *(mallfunc)(size_t); 32 | typedef void *(callfunc)(size_t, size_t); 33 | typedef void *(rallfunc)(void *, size_t); 34 | typedef void (freefunc)(void *); 35 | 36 | /* temporary allocators for rtld init - dlsym uses malloc */ 37 | static mallfunc ml_imalloc; 38 | static callfunc ml_icalloc; 39 | static rallfunc ml_irealloc; 40 | static freefunc ml_ifree; 41 | 42 | /* Global variables used to hold actual function addresses. */ 43 | static mallfunc *ml_malloc = ml_imalloc; 44 | static callfunc *ml_calloc = ml_icalloc; 45 | static rallfunc *ml_realloc = ml_irealloc; 46 | static freefunc *ml_free = ml_ifree; 47 | 48 | /* Helper for using string constants with write() */ 49 | #define WRT(STRCONST) STRCONST, sizeof(STRCONST)-1 50 | 51 | /* Magic constant identifying our malloc'd blocks */ 52 | static const size_t ml_magic = 0x600FBA11DEAFB0B3L; 53 | 54 | /* Store a stacktrace into stk with up to stknum levels */ 55 | static int ml_backtrace(size_t *stk, int stknum) 56 | { 57 | #if 1 58 | unw_cursor_t cursor; 59 | unw_context_t uc; 60 | int i; 61 | 62 | unw_getcontext(&uc); 63 | unw_init_local(&cursor, &uc); 64 | for (i=0; i> 56; 123 | mr.size &= 0xffffffffffffffL; 124 | mr.addr = lp+1; 125 | p2 = (char *)(lp - 1 - ml_stacknum); 126 | write(fd, &mr, sizeof(mr)); 127 | write(fd, p2, mr.nstk * sizeof(void *)); 128 | } 129 | lp += 2; 130 | } 131 | } 132 | 133 | static char ml_mapbuf[128*1024]; 134 | 135 | /* Generate output */ 136 | static void ml_dump() 137 | { 138 | void *h; 139 | struct link_map *lm; 140 | int fd, len; 141 | char *ptr, *end; 142 | 143 | /* Output the loader map */ 144 | fd = open("ml.info", O_CREAT|O_WRONLY|O_TRUNC, 0600); 145 | h = dlopen(NULL, RTLD_LAZY); 146 | dlinfo(h, RTLD_DI_LINKMAP, &lm); 147 | for (; lm && lm->l_prev; lm = lm->l_prev); 148 | for (; lm; lm=lm->l_next) 149 | if (lm->l_addr) { 150 | len = strlen(lm->l_name); 151 | ptr = ml_mapbuf; 152 | memcpy(ptr, &lm->l_addr, sizeof(lm->l_addr)); 153 | ptr += sizeof(lm->l_addr); 154 | memcpy(ptr, &len, sizeof(len)); 155 | ptr += sizeof(len); 156 | memcpy(ptr, lm->l_name, len+1); 157 | ptr += len+1; 158 | write(fd, ml_mapbuf, ptr-ml_mapbuf); 159 | } 160 | close(fd); 161 | 162 | /* Scan the process maps for heap-like memory regions */ 163 | fd = open("/proc/self/maps", O_RDONLY); 164 | len = read(fd, ml_mapbuf, sizeof(ml_mapbuf)); 165 | close(fd); 166 | fd = open("ml.data", O_CREAT|O_WRONLY|O_TRUNC, 0600); 167 | end = ml_mapbuf + len; 168 | *end = '\0'; 169 | ptr = ml_mapbuf; 170 | do { 171 | void *lo, *hi; 172 | int off; 173 | /* Only private read/write regions are heap candidates 174 | * May or may not be executable 175 | */ 176 | if (sscanf(ptr, "%p-%p rw%*1[x-]p %x", &lo, &hi, &off) != 3) { 177 | ptr = strchr(ptr, '\n'); 178 | if (!ptr) break; 179 | ptr++; 180 | continue; 181 | } 182 | ptr = strchr(ptr, '\n'); 183 | if (!ptr) break; 184 | ptr++; 185 | /* anonymous spaces are most likely */ 186 | if (ptr[-2] != ' ') { /* named space */ 187 | if (ptr[-2] != ']') /* don't scan file BSS/data */ 188 | continue; 189 | if (strncmp(ptr-sizeof("[heap]"), "[heap]", sizeof("[heap]")-1)) 190 | continue; /* don't scan stacks or other stuff */ 191 | } 192 | ml_scan(lo, hi, fd); 193 | } while(ptr < end); 194 | close(fd); 195 | } 196 | 197 | static void ml_sigdump(int sig) 198 | { 199 | ml_dump(); 200 | } 201 | 202 | static void ml_fini() 203 | { 204 | ml_dump(); 205 | } 206 | 207 | /* initialisation of malloc's hooks */ 208 | static void ml_init() __attribute__ ((constructor)); 209 | static void ml_init() 210 | { 211 | mallfunc *mall; 212 | callfunc *call; 213 | rallfunc *rall; 214 | freefunc *ff; 215 | 216 | ml_initing = 1; 217 | mall = dlsym( RTLD_NEXT, "malloc"); 218 | if (!mall) { 219 | write(2, WRT("ml_init failed to hook malloc!\n")); 220 | exit(1); 221 | } 222 | call = dlsym( RTLD_NEXT, "calloc"); 223 | rall = dlsym( RTLD_NEXT, "realloc"); 224 | ff = dlsym( RTLD_NEXT, "free"); 225 | atexit(ml_fini); 226 | { 227 | struct sigaction act = {0}; 228 | act.sa_handler = ml_sigdump; 229 | act.sa_flags = SA_RESTART; 230 | sigaction(SIGPROF, &act, NULL); 231 | } 232 | ml_malloc = mall; 233 | ml_calloc = call; 234 | ml_realloc = rall; 235 | ml_free = ff; 236 | ml_initing = 0; 237 | } 238 | 239 | /* my own malloc/realloc/free */ 240 | void *malloc(size_t size) 241 | { 242 | size_t *result, len; 243 | int nstk; 244 | 245 | if (ml_initing) return ml_malloc(size); 246 | 247 | len = ml_stacknum + 1 /* magic */ + 1 /* size + nstk */; 248 | result = ml_malloc(size + len * sizeof(void*)); 249 | if (result) { 250 | nstk = ml_backtrace(result, ml_stacknum); 251 | result += ml_stacknum; 252 | size |= ((long)nstk << 56); 253 | *result++ = size; 254 | *result++ = ml_magic; 255 | } 256 | 257 | /* return the pointer */ 258 | return(result); 259 | } 260 | 261 | void *calloc(size_t nelem, size_t size) 262 | { 263 | size_t *result, len; 264 | int nstk; 265 | 266 | if (ml_initing) return ml_calloc(nelem, size); 267 | 268 | len = ml_stacknum + 1 /* magic */ + 1 /* size + nstk */; 269 | 270 | size *= nelem; 271 | result = ml_calloc(1, size + len * sizeof(void*)); 272 | if (result) { 273 | nstk = ml_backtrace(result, ml_stacknum); 274 | result += ml_stacknum; 275 | size |= ((long)nstk << 56); 276 | *result++ = size; 277 | *result++ = ml_magic; 278 | } 279 | 280 | /* return the pointer */ 281 | return(result); 282 | } 283 | 284 | /* This is really slow.... */ 285 | void *realloc(void *ptr, size_t size) 286 | { 287 | size_t *result, *p2, len; 288 | int nstk; 289 | 290 | if (ml_initing) return ml_realloc(ptr, size); 291 | 292 | if (!ptr) 293 | return malloc(size); 294 | 295 | p2 = ptr; 296 | /* not our pointer? */ 297 | if (p2[-1] != ml_magic) 298 | return ml_realloc(ptr, size); 299 | 300 | p2 -= 2; 301 | p2[1] = 0; 302 | p2 -= ml_stacknum; 303 | 304 | len = ml_stacknum + 1 /* magic */ + 1 /* size + nstk */; 305 | result = ml_realloc(p2, size + len * sizeof(void *)); 306 | if (result) { 307 | nstk = ml_backtrace(result, ml_stacknum); 308 | result += ml_stacknum; 309 | size |= ((long)nstk << 56); 310 | *result++ = size; 311 | *result++ = ml_magic; 312 | } else { 313 | /* on failure original should be unchanged */ 314 | p2[ml_stacknum+1] = ml_magic; 315 | } 316 | 317 | /* return the pointer */ 318 | return(result); 319 | } 320 | 321 | /* Quick'n'dirty stack-like malloc for use while we try to find 322 | * the actual malloc functions 323 | */ 324 | #define HEAPSIZE (1048576*10) 325 | static long ml_hblock[HEAPSIZE]; 326 | 327 | typedef struct ml_heap { 328 | void *mh_base; 329 | void *mh_last; 330 | void *mh_end; 331 | } ml_heap; 332 | 333 | static ml_heap ml_sh = { 334 | ml_hblock, ml_hblock, (char *)ml_hblock + sizeof(ml_hblock) 335 | }; 336 | 337 | static void * 338 | ml_imalloc(size_t size) 339 | { 340 | size_t *new; 341 | int pad = 2*sizeof(int)-1; 342 | 343 | /* round up to doubleword boundary */ 344 | size += pad + sizeof( size_t); 345 | size &= ~pad; 346 | 347 | if ((char *) ml_sh.mh_last + size >= (char *) ml_sh.mh_end) { 348 | write(2, WRT("ml_imalloc exhausted\n")); 349 | return NULL; 350 | } 351 | new = ml_sh.mh_last; 352 | *new++ = size - sizeof(size_t); 353 | ml_sh.mh_last = (char *) ml_sh.mh_last + size; 354 | 355 | return( (void *)new); 356 | } 357 | 358 | static void * 359 | ml_icalloc(size_t n, size_t size) 360 | { 361 | void *new; 362 | 363 | new = ml_imalloc(n*size); 364 | if (new) 365 | memset(new, 0, n*size); 366 | return new; 367 | } 368 | 369 | static void * 370 | ml_irealloc(void *ptr, size_t size) 371 | { 372 | int pad = 2*sizeof(int)-1; 373 | size_t *p = (size_t *)ptr; 374 | size_t *new; 375 | 376 | if ( ptr == NULL) return ml_imalloc(size); 377 | 378 | /* Not our memory? */ 379 | if (ptr < ml_sh.mh_base || ptr >= ml_sh.mh_end) { 380 | write(2, WRT("ml_irealloc - not our memory\n")); 381 | return NULL; 382 | } 383 | 384 | if (!size) { 385 | ml_ifree(ptr); 386 | return NULL; 387 | } 388 | 389 | /* round up to doubleword boundary */ 390 | size += pad + sizeof(size_t); 391 | size &= ~pad; 392 | 393 | /* Never shrink blocks */ 394 | if (size <= p[-1]) { 395 | new = p; 396 | 397 | /* If reallocing the last block, we can grow it */ 398 | } else if ( (char *)ptr + p[-1] == ml_sh.mh_last) { 399 | new = p; 400 | ml_sh.mh_last = (char *) ml_sh.mh_last + size - p[-1]; 401 | p[-1] = size; 402 | 403 | /* Nowhere to grow, need to alloc and copy */ 404 | } else { 405 | new = ml_imalloc(size); 406 | if (new) 407 | memcpy(new, ptr, p[-1]); 408 | } 409 | return new; 410 | } 411 | 412 | static void 413 | ml_ifree(void *ptr) 414 | { 415 | size_t *p = (size_t *)ptr; 416 | 417 | if (!ptr) 418 | return; 419 | 420 | if (ptr < ml_sh.mh_base || ptr >= ml_sh.mh_end) { 421 | write(2, WRT("ml_ifree - not our memory\n")); 422 | exit(1); 423 | } else if ( (char *)ptr + p[-1] == ml_sh.mh_last) { 424 | p--; 425 | ml_sh.mh_last = p; 426 | } 427 | } 428 | 429 | void free(void *ptr) 430 | { 431 | size_t *p2; 432 | 433 | if (!ptr || ml_initing) { 434 | ml_free(ptr); 435 | return; 436 | } 437 | 438 | if (ptr >= ml_sh.mh_base && ptr < ml_sh.mh_end) { 439 | ml_ifree(ptr); 440 | return; 441 | } 442 | 443 | p2 = ptr; 444 | /* not our pointer? */ 445 | if (p2[-1] != ml_magic) { 446 | ml_free(ptr); 447 | return; 448 | } 449 | 450 | p2[-1] = 0; 451 | p2 -= (2+ml_stacknum); 452 | ml_free(p2); 453 | } 454 | -------------------------------------------------------------------------------- /mleak.h: -------------------------------------------------------------------------------- 1 | /* malloc tracer for memory leak tracking 2 | * -- Howard Chu, hyc@symas.com 2015-03-24 3 | */ 4 | typedef enum codes { 5 | ALLOC = 1, FREE, REALLOC } codes; 6 | 7 | typedef struct ml_rec { 8 | codes code; 9 | int nstk; 10 | void *addr; /* the sole address in an alloc or free */ 11 | /* for an alloc, size_t size comes next */ 12 | /* for a realloc, the old address will be recorded here */ 13 | /* actual stack follows */ 14 | } ml_rec; 15 | 16 | typedef struct ml_rec2 { /* used for allocs */ 17 | codes code; 18 | int nstk; 19 | void *addr; 20 | size_t size; 21 | } ml_rec2; 22 | 23 | typedef struct ml_rec3 { /* used for realloc */ 24 | codes code; 25 | int nstk; 26 | void *addr; 27 | size_t size; 28 | void *orig; 29 | } ml_rec3; 30 | 31 | typedef struct ml_info { /* no longer used */ 32 | void **mi_end; 33 | void *mi_tail; 34 | int mi_live; 35 | void *mi_data[0]; 36 | } ml_info; 37 | -------------------------------------------------------------------------------- /mnew.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | extern "C" { 5 | void *malloc(size_t s); 6 | void free(void *p); 7 | } 8 | 9 | void * operator new(size_t size) throw (std::bad_alloc) { 10 | void *ret = malloc(size); 11 | if (!ret) throw std::bad_alloc(); 12 | return ret; 13 | } 14 | void operator delete(void *p) __THROW { free(p); } 15 | void * operator new[](size_t size) throw (std::bad_alloc) { 16 | void *ret = malloc(size); 17 | if (!ret) throw std::bad_alloc(); 18 | return ret; 19 | } 20 | void operator delete[](void *p) __THROW { free(p); } 21 | void* operator new(size_t size, const std::nothrow_t& nt) __THROW {return malloc(size); } 22 | void* operator new[](size_t size, const std::nothrow_t& nt) __THROW {return malloc(size);} 23 | void operator delete(void *ptr, const std::nothrow_t& nt) __THROW {free(ptr);} 24 | void operator delete[](void *ptr, const std::nothrow_t& nt) __THROW {free(ptr);} 25 | 26 | -------------------------------------------------------------------------------- /mx.c: -------------------------------------------------------------------------------- 1 | /* malloc tracer for memory leak tracking 2 | * This fragment actually prints the leak report 3 | * -- Howard Chu, hyc@symas.com 2015-03-24 4 | */ 5 | #define IFDECO if (options & MD_NO_DECORATION) 6 | #define IFNDECO if (!(options & MD_NO_DECORATION)) 7 | 8 | #define MD_TTL_MEM "Memory blocks:\n" 9 | #define MD_TTL_MEM2 "Memory leaks (%ld total):\n", md_nb_mem_used 10 | 11 | void md_display_stack(int nb_stack, MD_Loc *stack, int options) 12 | { 13 | int l; 14 | 15 | if (nb_stack == 0) 16 | return; 17 | 18 | IFDECO 19 | printf("stack "); 20 | for(l=0; lanext && blk->anext->where_f) 67 | { 68 | IFDECO 69 | { 70 | printf("still %p \"%s\" \"%s:%d\" \"%s\" \"%s:%d\"\n", 71 | blk->ptr, MD_DNAME(blk->func_f), 72 | MD_DFILE(blk->file_f), blk->line_f, 73 | MD_DNAME(blk->anext->func_f), 74 | MD_DFILE(blk->anext->file_f), 75 | blk->anext->line_f); 76 | } 77 | else 78 | printf("Manipulation of address %p at %s (%s:%d)," 79 | " which was freed at %s (%s:%d)\n", 80 | blk->ptr, MD_DNAME(blk->func_f), 81 | MD_DFILE(blk->file_f), blk->line_f, 82 | MD_DNAME(blk->anext->func_f), 83 | MD_DFILE(blk->anext->file_f), 84 | blk->anext->line_f); 85 | } 86 | else 87 | if (blk->func_a == NULL) 88 | {/* invalid free */ 89 | IFDECO 90 | printf("free %p \"%s\" \"%s:%d\"\n", 91 | blk->ptr, MD_DNAME(blk->func_f), 92 | MD_DFILE( blk->file_f), blk->line_f); 93 | else 94 | printf(" unreferenced address (%p) for 'free' at %s (%s:%d)\n", 95 | blk->ptr, MD_DNAME(blk->func_f), 96 | MD_DFILE( blk->file_f), blk->line_f); 97 | } 98 | else 99 | {/* invalid realloc */ 100 | IFDECO 101 | printf("realloc %p \"%s\" \"%s:%d\"\n", 102 | blk->ptr, MD_DNAME(blk->func_f), 103 | MD_DFILE(blk->file_f), blk->line_f); 104 | else 105 | printf(" unreferenced address (%p) for 'realloc' at %s (%s:%d)\n", 106 | blk->ptr, MD_DNAME(blk->func_f), 107 | MD_DFILE(blk->file_f), blk->line_f); 108 | } 109 | } 110 | void md_display_valid_leak(MD_Mem *blk, int options) 111 | { 112 | int j; 113 | MD_Mem *m; 114 | 115 | for (j=0,m=blk->rnext;m;m=m->rnext,j++); 116 | /* normal behavior */ 117 | IFDECO 118 | { 119 | printf("%p %u \"%s\" \"%s:%d\" %d\n",blk->ptr, blk->size_a, 120 | MD_DNAME(blk->func_a), MD_DFILE(blk->file_a), blk->line_a, 121 | j); 122 | } 123 | else 124 | { 125 | printf(" Leak, blocks, size: %p,%d,%-8u ", blk->ptr, blk->block, blk->size_a); 126 | printf(" %s (%s:%d)\n", MD_DNAME(blk->func_a), 127 | MD_DFILE(blk->file_a), blk->line_a); 128 | } 129 | /*call-stack */ 130 | md_display_stack(blk->nb_stack_a, blk->stack_a, options); 131 | for (j=0,m=blk->rnext;m;m=m->rnext,j++) 132 | { 133 | IFDECO 134 | { 135 | printf("%p %u \"%s\" \"%s:%d\"\n", m->ptr, m->size_a, 136 | MD_DNAME(m->func_a),MD_DFILE(m->file_a),m->line_a); 137 | } 138 | else 139 | { 140 | printf(" realloc(%3d): %p,%-8u ", j+1, m->ptr, m->size_a); 141 | printf(" %s (%s:%d)\n", 142 | MD_DNAME(m->func_a),MD_DFILE(m->file_a),m->line_a); 143 | } 144 | /* new: call-stack */ 145 | md_display_stack(m->nb_stack_a, m->stack_a, options); 146 | } 147 | if (j > 0) 148 | IFDECO 149 | printf("\n"); 150 | } 151 | 152 | static int md_found_leak; 153 | 154 | int md_display_leak1(MD_Mem *me, int options) 155 | { 156 | for (;me;me=me->anext) 157 | { 158 | if ((options & (MD_NO_UNRES_MALLOC|MD_NO_UNRES_REALLOC))&& 159 | (me->where_a) && (!me->valid_a)) 160 | continue; 161 | if ((options & MD_NO_UNRES_FREE)&& 162 | (me->where_f) && (!me->valid_f)) 163 | continue; 164 | 165 | if (me->where_a) 166 | { 167 | if (!me->where_f && !me->rnext) 168 | { 169 | if (!md_found_leak) 170 | { 171 | md_found_leak = 1; 172 | IFNDECO 173 | { 174 | printf("\n"); 175 | printf(MD_TTL_MEM2); 176 | printf("\n"); 177 | } 178 | } 179 | md_display_valid_leak(me, options); 180 | } 181 | } 182 | else 183 | { 184 | if (!md_found_leak) 185 | { 186 | md_found_leak = 1; 187 | IFNDECO 188 | { 189 | printf("\n"); 190 | printf(MD_TTL_MEM2); 191 | printf("\n"); 192 | } 193 | } 194 | md_display_invalid_block(me, options); 195 | } 196 | } 197 | return 0; 198 | } 199 | 200 | MD_Mem **leaks; 201 | int lcnt; 202 | 203 | int md_linearize_leaks(MD_Mem *me, int foo) 204 | { 205 | leaks[lcnt++] = me; 206 | return 0; 207 | } 208 | 209 | /* sort in descending order of size, #blocks */ 210 | int md_sort_leaks(const void *v1, const void *v2) 211 | { 212 | MD_Mem **p1 = (MD_Mem **)v1, **p2 = (MD_Mem **)v2; 213 | MD_Mem *m1 = *p1, *m2 = *p2; 214 | long l; 215 | 216 | l = (long)m2->size_a - (long)m1->size_a; 217 | if (l) 218 | return l < 0 ? -1 : l > 0; 219 | return m2->block - m1->block; 220 | } 221 | 222 | int md_display_leaks(int options) 223 | { 224 | md_found_leak = 0; 225 | int i; 226 | 227 | leaks = malloc(blocks * sizeof(MD_Mem)); 228 | avl_apply(md_mems, (AVL_APPLY)md_linearize_leaks, NULL, -1, AVL_INORDER); 229 | qsort(leaks, blocks, sizeof(MD_Mem *), md_sort_leaks); 230 | 231 | if (!md_mems) 232 | { 233 | IFDECO 234 | { 235 | printf(">leaks\n"); 236 | printf("leaks\n"); 244 | 245 | for (i=0; i