diff --git a/contrib/scripts/refcounter.py b/contrib/scripts/refcounter.py index de3cda051d83cc5ea1b5956e9499db8873345f7a..36fb34126ad74d66e30c41b84290c4c13c6ca033 100755 --- a/contrib/scripts/refcounter.py +++ b/contrib/scripts/refcounter.py @@ -108,6 +108,10 @@ def process_file(options): else: current_objects[obj]['curcount'] += int(parsed_line['delta']) + if 'destructor' in parsed_line['state']: + # refcounter.py doesn't care about lock-state. + parsed_line['state'] = '**destructor**' + current_objects[obj]['log'].append( "[%s] %s:%s %s: %s %s - [%s]" % ( parsed_line['thread_id'], diff --git a/contrib/scripts/reflocks.py b/contrib/scripts/reflocks.py new file mode 100755 index 0000000000000000000000000000000000000000..7d8edfcd054c79d6d6ab893a9f206d8dc75dc1ee --- /dev/null +++ b/contrib/scripts/reflocks.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +"""Process a ref debug log for lock usage + + This file will process a log file created by Asterisk + that was compiled with REF_DEBUG and DEBUG_THREADS. + + See http://www.asterisk.org for more information about + the Asterisk project. Please do not directly contact + any of the maintainers of this project for assistance; + the project provides a web site, mailing lists and IRC + channels for your use. + + This program is free software, distributed under the terms of + the GNU General Public License Version 2. See the LICENSE file + at the top of the source tree. + + Copyright (C) 2018, CFWare, LLC + Corey Farrell <git@cfware.com> +""" + +from __future__ import print_function +import sys +import os + +from optparse import OptionParser + + +def process_file(options): + """The routine that kicks off processing a ref file""" + + object_types = {} + objects = {} + filename = options.filepath + + with open(filename, 'r') as ref_file: + for line in ref_file: + if 'constructor' not in line and 'destructor' not in line: + continue + # The line format is: + # addr,delta,thread_id,file,line,function,state,tag + # Only addr, file, line, function, state are used by reflocks.py + tokens = line.strip().split(',', 7) + addr = tokens[0] + state = tokens[6] + if 'constructor' in state: + obj_type = '%s:%s:%s' % (tokens[3], tokens[4], tokens[5]) + if obj_type not in object_types: + object_types[obj_type] = { + 'used': 0, + 'unused': 0, + 'lockobj': 0, + 'none': 0 + } + objects[addr] = obj_type + elif 'destructor' in state: + if addr not in objects: + # This error would be reported by refcounter.py. + continue + obj_type = objects[addr] + del objects[addr] + if '**lock-state:unused**' in state: + object_types[obj_type]['unused'] += 1 + elif '**lock-state:used**' in state: + object_types[obj_type]['used'] += 1 + elif '**lock-state:lockobj**' in state: + object_types[obj_type]['lockobj'] += 1 + elif '**lock-state:none**' in state: + object_types[obj_type]['none'] += 1 + + for (allocator, info) in object_types.items(): + stats = []; + if info['used'] > 0: + if not options.used: + continue + stats.append("%d used" % info['used']) + if info['unused'] > 0: + stats.append("%d unused" % info['unused']) + if info['lockobj'] > 0 and options.lockobj: + stats.append("%d lockobj" % info['lockobj']) + if info['none'] > 0 and options.none: + stats.append("%d none" % info['none']) + if len(stats) == 0: + continue + print("%s: %s" % (allocator, ', '.join(stats))) + + +def main(argv=None): + """Main entry point for the script""" + + ret_code = 0 + + if argv is None: + argv = sys.argv + + parser = OptionParser() + + parser.add_option("-f", "--file", action="store", type="string", + dest="filepath", default="/var/log/asterisk/refs", + help="The full path to the refs file to process") + parser.add_option("-u", "--suppress-used", action="store_false", + dest="used", default=True, + help="Don't output types that have used locks.") + parser.add_option("-n", "--show-none", action="store_true", + dest="none", default=False, + help="Show counts of objects with no locking.") + parser.add_option("-o", "--show-lockobj", action="store_true", + dest="lockobj", default=False, + help="Show counts of objects with a lockobj.") + + (options, args) = parser.parse_args(argv) + + if not os.path.isfile(options.filepath): + print("File not found: %s" % options.filepath, file=sys.stderr) + return -1 + + try: + process_file(options) + except (KeyboardInterrupt, SystemExit, IOError): + print("File processing cancelled", file=sys.stderr) + return -1 + + return ret_code + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/main/astobj2.c b/main/astobj2.c index 23109a6f89a0039dbaa525d7857b1191a2355a83..d6a8a4eb457cd12837e28a5d4f693d8c1daa0345 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -473,6 +473,9 @@ int __ao2_ref(void *user_data, int delta, int32_t current_value; int32_t ret; struct ao2_weakproxy *weakproxy = NULL; +#ifdef DEBUG_THREADS + const char *lock_state; +#endif if (obj == NULL) { if (ref_log && user_data) { @@ -592,21 +595,33 @@ int __ao2_ref(void *user_data, int delta, switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) { case AO2_ALLOC_OPT_LOCK_MUTEX: obj_mutex = INTERNAL_OBJ_MUTEX(user_data); +#ifdef DEBUG_THREADS + lock_state = obj_mutex->mutex.lock.flags.setup ? "used" : "unused"; +#endif ast_mutex_destroy(&obj_mutex->mutex.lock); ast_free(obj_mutex); break; case AO2_ALLOC_OPT_LOCK_RWLOCK: obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data); +#ifdef DEBUG_THREADS + lock_state = obj_rwlock->rwlock.lock.flags.setup ? "used" : "unused"; +#endif ast_rwlock_destroy(&obj_rwlock->rwlock.lock); ast_free(obj_rwlock); break; case AO2_ALLOC_OPT_LOCK_NOLOCK: +#ifdef DEBUG_THREADS + lock_state = "none"; +#endif ast_free(obj); break; case AO2_ALLOC_OPT_LOCK_OBJ: obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data); +#ifdef DEBUG_THREADS + lock_state = "lockobj"; +#endif ao2_t_ref(obj_lockobj->lockobj.lock, -1, "release lockobj"); ast_free(obj_lockobj); @@ -614,12 +629,22 @@ int __ao2_ref(void *user_data, int delta, default: ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n", user_data); +#ifdef DEBUG_THREADS + lock_state = "invalid"; +#endif break; } if (ref_log && tag) { - fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**destructor**,%s\n", - user_data, delta, ast_get_tid(), file, line, func, tag); + fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**destructor%s%s**,%s\n", + user_data, delta, ast_get_tid(), file, line, func, +#ifdef DEBUG_THREADS + "**lock-state:", + lock_state, +#else + "", "", +#endif + tag); fflush(ref_log); }