From 535da540fc8f347cd3810b51d03ca00e4a9971a8 Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Thu, 13 Sep 2018 14:30:52 -0400 Subject: [PATCH 1/2] RFC: ao2 reference storage location logging. This change enables recording of additional information to the REF_DEBUG log. References can be associated with pointers and object sizes are recorded. This allows refcounter.py to filter out any references with matching unrefs. Leaked references can be associated with a memory offset within another leaked object, marking the object as indirectly leaked. Logging storage locations is especially useful for certain object types like formats that are long lived and widely used. No attempt is made to report an error if the refs log says that a pointer has been overwritten. It is assumed this is the result of an object being stored in an ao2_container where the object is associated with offset 0 of the container. The same can be done for linked lists and vectors, though it is not automatic. This is also needed to support ao2_s_replace, and we add the new reference before removing the old reference. The refcounter.py script has been rewritten to use classes. I attempted to rework the procedural script to perform the new features but I felt the code was becoming unreadable. The updated refcounter.py takes longer to run, but this is due to associating refs and unrefs together. I feel that having an automated process take longer is worth the savings in time spent manually reviewing the output. ASTERISK-26171 Change-Id: Iacb0a51cefaa98c83eab18aa2b7d18850bb33951 --- channels/chan_sip.c | 4 +- contrib/scripts/refcounter.py | 468 ++++++++++++++++++++----------- include/asterisk/astobj2.h | 224 +++++++++++---- include/asterisk/utils.h | 10 + main/astobj2.c | 56 ++-- main/astobj2_container.c | 67 +++-- main/astobj2_container_private.h | 2 +- main/astobj2_hash.c | 16 +- main/astobj2_rbtree.c | 16 +- main/channel_internal_api.c | 3 +- main/format_cap.c | 4 +- main/named_locks.c | 2 +- main/sorcery.c | 2 +- res/res_musiconhold.c | 2 +- 14 files changed, 590 insertions(+), 286 deletions(-) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e375bfe82b..bd519c2da7 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -8901,9 +8901,9 @@ struct sip_pvt *__sip_alloc(ast_string_field callid, struct ast_sockaddr *addr, { struct sip_pvt *p; - p = __ao2_alloc(sizeof(*p), sip_pvt_dtor, + p = __ao2_alloc_full(sizeof(*p), sip_pvt_dtor, AO2_ALLOC_OPT_LOCK_MUTEX, "allocate a dialog(pvt) struct", - file, line, func); + file, line, func, NULL); if (!p) { return NULL; } diff --git a/contrib/scripts/refcounter.py b/contrib/scripts/refcounter.py index de3cda051d..43fddc1cb8 100755 --- a/contrib/scripts/refcounter.py +++ b/contrib/scripts/refcounter.py @@ -18,152 +18,304 @@ Matt Jordan """ -from __future__ import print_function import sys import os +import subprocess from optparse import OptionParser -def parse_line(line): - """Parse out a line into its constituent parts. +class AO2Ref: + def __init__(self, tokens, manager): + (addr, delta, thread_id, + fname, line, func, + state, self.ptr, self.tag) = tokens - Keyword Arguments: - line The line from a ref debug log to parse out + self.is_indirect = False + self.is_ptr = self.ptr != '(nil)' + self.is_bad_ptr = False + self.delta = int(delta) - Returns: - A dictionary containing the options, or None - """ - tokens = line.strip().split(',', 7) - if len(tokens) < 8: - print("ERROR: ref debug line '%s' contains fewer tokens than " - "expected: %d" % (line.strip(), len(tokens))) - return None + if not self.is_ptr: + self.ptr = '' + + self.ao2object = manager.ao2_find_or_create(addr, self.delta, state) - processed_line = {'addr': tokens[0], - 'delta': tokens[1], - 'thread_id': tokens[2], - 'file': tokens[3], - 'line': tokens[4], - 'function': tokens[5], - 'state': tokens[6], - 'tag': tokens[7], - } - return processed_line - - -def process_file(options): - """The routine that kicks off processing a ref file - - Keyword Arguments: - filename The full path to the file to process - - Returns: - A tuple containing: - - A list of objects whose lifetimes were completed - (i.e., finished objects) - - A list of objects referenced after destruction - (i.e., invalid objects) - - A list of objects whose lifetimes were not completed - (i.e., leaked objects) - - A list of objects whose lifetimes are skewed - (i.e., Object history starting with an unusual ref count) - """ - - finished_objects = [] - invalid_objects = [] - leaked_objects = [] - skewed_objects = [] - current_objects = {} - filename = options.filepath - - with open(filename, 'r') as ref_file: - for line in ref_file: - parsed_line = parse_line(line) - if not parsed_line: - continue - - invalid = False - obj = parsed_line['addr'] - - if obj not in current_objects: - current_objects[obj] = {'log': [], 'curcount': 1} - if 'constructor' in parsed_line['state']: - # This is the normal expected case - pass - elif 'invalid' in parsed_line['state']: - invalid = True - current_objects[obj]['curcount'] = 0 - if options.invalid: - invalid_objects.append((obj, current_objects[obj])) - elif 'destructor' in parsed_line['state']: - current_objects[obj]['curcount'] = 0 - if options.skewed: - skewed_objects.append((obj, current_objects[obj])) - else: - current_objects[obj]['curcount'] = int( - parsed_line['state']) - if options.skewed: - skewed_objects.append((obj, current_objects[obj])) + if self.is_ptr and manager.options.processpointers and self.delta < 0: + orig = self.ao2object.unsetRefByPtr(self, self.ptr) + if not orig: + self.is_bad_ptr = True + print "Error: Pointer %s did not contain object %s" % ( + self.ptr, addr) + self.ao2object.log(self) + else: + self.ao2object.filterLog(orig) + else: + if self.is_ptr and manager.options.processpointers: + self.ao2object.setRefByPtr(self, self.ptr) + self.ao2object.log(self) + + # text contains everything before ptr, tag and current refcount + self.text = "[%s] %s:%s %s: %s" % (thread_id, fname, line, func, delta) + self.ao2object.addDelta(self.delta) + + def finalize(self, manager): + ptr_txt = '' + if self.is_ptr: + (offsetTo, offset) = manager.ao2_find_offset(self.ptr) + + if offsetTo is None: + ptr_txt = ' ptr:%s' % self.ptr else: - current_objects[obj]['curcount'] += int(parsed_line['delta']) - - current_objects[obj]['log'].append( - "[%s] %s:%s %s: %s %s - [%s]" % ( - parsed_line['thread_id'], - parsed_line['file'], - parsed_line['line'], - parsed_line['function'], - parsed_line['delta'], - parsed_line['tag'], - parsed_line['state'])) - - # It is possible for curcount to go below zero if someone - # unrefs an object by two or more when there aren't that - # many refs remaining. This condition abnormally finishes - # the object. - if current_objects[obj]['curcount'] <= 0: - if current_objects[obj]['curcount'] < 0: - current_objects[obj]['log'].append( - "[%s] %s:%s %s: %s %s - [%s]" % ( - parsed_line['thread_id'], - parsed_line['file'], - parsed_line['line'], - parsed_line['function'], - "+0", - "Object abnormally finalized", - "**implied destructor**")) - # Highlight the abnormally finished object in the - # invalid section as well as reporting it in the normal - # finished section. - if options.invalid: - invalid_objects.append((obj, current_objects[obj])) - if not invalid and options.normal: - finished_objects.append((obj, current_objects[obj])) - del current_objects[obj] - - if options.leaks: - for (key, lines) in current_objects.items(): - leaked_objects.append((key, lines)) - return (finished_objects, invalid_objects, leaked_objects, skewed_objects) - - -def print_objects(objects, prefix=""): - """Prints out the objects that were processed - - Keyword Arguments: - objects A list of objects to print - prefix A prefix to print that specifies something about - this object - """ - - print("======== %s Objects ========" % prefix) - print("\n") - for obj in objects: - print("==== %s Object %s history ====" % (prefix, obj[0])) - for line in obj[1]['log']: - print(line) - print("\n") + ptr_txt = ' offset:%s[%d]' % (offsetTo.addr, offset) + self.is_indirect = True + + if self.is_bad_ptr: + ptr_txt = " bad%s" % ptr_txt + + self.text = "%s%s %s" % (self.text, ptr_txt, self.tag) + + def isSameObject(self, ref): + return self.ao2object == ref.ao2object + + def directDelta(self): + if self.is_indirect: + return 0 + + return self.delta + + def printRef(self, refs): + print "%s - [%d]" % (self.text, refs) + return self.delta + + +class AO2Object: + def __init__(self, manager, addr, delta, state): + self.manager = manager + self._log = [] + self.addr = addr + self.addrNum = int(addr, 16) + self.startrefcount = 0 + self.invalid = False + self.skewed = False + self.size = 'unknown' + self.refbyptr = {} + if state.startswith('**constructor'): + self.size = int(state.split("**")[2]) + self.manager.setCurrent(addr, self) + elif state.startswith('**invalid'): + self.setInvalid() + elif state.startswith('**destructor'): + # the object is skewed but not leaked + self.startrefcount = -delta + self.setSkewed() + else: + self.startrefcount = int(state) + self.manager.setCurrent(addr, self) + self.setSkewed() + + self.refcount = self.startrefcount + + def log(self, ref): + self._log.append(ref) + + def filterLog(self, ref): + self._log.remove(ref) + + def addDelta(self, delta): + self.refcount += delta + if self.refcount <= 0: + if self.refcount < 0: + self.setInvalid() + self.manager.setFinished(self.addr, self.invalid) + + def getOffset(self, addrNum): + if self.size == 'unknown' or self.size <= 0: + return -1 + + offset = addrNum - self.addrNum + if offset < 0 or offset >= self.size: + return -1 + + return offset + + def directRefs(self): + count = self.startrefcount + for obj in self._log: + count += obj.directDelta() + return count + + def finalize(self): + for ref in self._log: + ref.finalize(self.manager) + + def setInvalid(self): + if not self.invalid: + self.invalid = True + self.manager.setInvalid(self) + + def setSkewed(self): + if not self.skewed: + self.skewed = True + self.manager.setSkewed(self) + + def setRefByPtr(self, ref, ptr): + if ptr not in self.refbyptr: + self.refbyptr[ptr] = [ref] + else: + self.refbyptr[ptr].append(ref) + + def unsetRefByPtr(self, unref, ptr): + if ptr in self.refbyptr: + ptrobj = self.refbyptr[ptr] + for ref in ptrobj: + if ref.isSameObject(unref): + ptrobj.remove(ref) + if len(ptrobj) == 0: + del self.refbyptr[ptr] + return ref + + return None + + def printObj(self, prefix): + print "==== %s Object %s (%s bytes) history ====" % ( + prefix, self.addr, self.size) + refs = 0 + for ref in self._log: + refs += ref.printRef(refs) + print "\n" + + +class ObjectManager: + def __init__(self, options): + self.options = options + self.finished = [] + self.invalid = [] + self.skewed = [] + self.leaked = [] + self.indirect = [] + self.current = {} + + def setFinished(self, addr, invalid): + if not invalid and self.options.normal: + self.finished.append(self.current[addr]) + del self.current[addr] + + def setCurrent(self, addr, obj): + self.current[addr] = obj + + def setInvalid(self, obj): + if self.options.invalid: + self.invalid.append(obj) + + def setSkewed(self, obj): + if self.options.skewed: + self.skewed.append(obj) + + def ao2_find_or_create(self, addr, delta, state): + if addr in self.current: + obj = self.current[addr] + return obj + + return AO2Object(self, addr, delta, state) + + def ao2_find_offset(self, addr): + addrNum = int(addr, 16) + + for obj in self.current: + offset = self.current[obj].getOffset(addrNum) + if offset >= 0: + return (self.current[obj], offset) + + return (None, None) + + def _lineToTokens(self, txtline): + tokens = txtline.strip().split(',', 8) + + if len(tokens) < 9: + print >>sys.stderr, "ERROR: ref debug line '%s' contains fewer " \ + "tokens than expected: %d" % (txtline.strip(), len(tokens)) + sys.exit(-1) + + return tokens + + def process(self): + self.obj_filter = {} + if self.options.preprocess and not self.options.normal: + # Get list of objects that leak. This has the potential to miss certain + # skewed or invalid objects so it's disabled by default. + with open(self.options.filepath, 'r') as ref_file: + for txtline in ref_file: + (addr, delta) = self._lineToTokens(txtline)[:2] + + if addr not in self.obj_filter: + self.obj_filter[addr] = 0; + + self.obj_filter[addr] += int(delta) + + if self.obj_filter[addr] == 0: + del self.obj_filter[addr] + + if self.options.preprocess and not self.obj_filter: + # Preprocess is enabled and determined no objects leaked. + return + + with open(self.options.filepath, 'r') as ref_file: + for txtline in ref_file: + tokens = self._lineToTokens(txtline) + addr = tokens[0] + if not self.options.preprocess or addr in self.obj_filter: + AO2Ref(tokens, self) + + for key, obj in self.current.iteritems(): + obj.finalize() + + for key, obj in self.current.iteritems(): + if obj.directRefs() == 0: + if self.options.indirect: + self.indirect.append(obj) + elif self.options.leaks: + self.leaked.append(obj) + + self.current = {} + + def __print_objects(self, objects, prefix=""): + """Prints out the objects that were processed + + Keyword Arguments: + objects A list of objects to print + prefix A prefix to print that specifies something about + this object + """ + + print "======== %s Objects ========" % prefix + print "\n" + for obj in objects: + obj.printObj(prefix) + + def printReport(self): + ret_code = 0 + + if self.options.invalid and len(self.invalid): + self.__print_objects(self.invalid, "Invalid Referenced") + ret_code |= 4 + + if self.options.leaks and len(self.leaked): + self.__print_objects(self.leaked, "Leaked") + ret_code |= 1 + + if self.options.indirect and len(self.indirect): + self.__print_objects(self.indirect, "Indirectly Leaked") + ret_code |= 1 + + if self.options.skewed and len(self.skewed): + self.__print_objects(self.skewed, "Skewed") + ret_code |= 2 + + if self.options.normal: + self.__print_objects(self.finished, "Finalized") + + return ret_code def main(argv=None): @@ -194,41 +346,39 @@ def main(argv=None): dest="skewed", default=True, help="If specified, don't output objects with a " "skewed lifetime") + parser.add_option("", "--suppress-indirect", action="store_false", + dest="indirect", default=True, + help="If specified don't output objects without " + "direct leaks.") + parser.add_option("--ignorepointers", action="store_false", + dest="processpointers", default=True, + help="If specified, don't check for matching " + "pointers in unrefs") + parser.add_option("--preprocess", action="store_true", + dest="preprocess", default=False, + help="Perform two-pass processing. Ignored " + "unless normal objects are suppressed. " + "This option may cause invalid or skewed " + "objects to be missed.") (options, args) = parser.parse_args(argv) if not options.invalid and not options.leaks and not options.normal \ - and not options.skewed: - print("All options disabled", file=sys.stderr) + and not options.skewed and not options.indirect: + print >>sys.stderr, "All options disabled" return -1 if not os.path.isfile(options.filepath): - print("File not found: %s" % options.filepath, file=sys.stderr) + print >>sys.stderr, "File not found: %s" % options.filepath return -1 try: - (finished_objects, - invalid_objects, - leaked_objects, - skewed_objects) = process_file(options) - - if options.invalid and len(invalid_objects): - print_objects(invalid_objects, "Invalid Referenced") - ret_code |= 4 - - if options.leaks and len(leaked_objects): - print_objects(leaked_objects, "Leaked") - ret_code |= 1 - - if options.skewed and len(skewed_objects): - print_objects(skewed_objects, "Skewed") - ret_code |= 2 - - if options.normal: - print_objects(finished_objects, "Finalized") + proc = ObjectManager(options) + proc.process() + ret_code = proc.printReport() except (KeyboardInterrupt, SystemExit, IOError): - print("File processing cancelled", file=sys.stderr) + print >>sys.stderr, "File processing cancelled" return -1 return ret_code diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h index 0e442dbe07..672a5423d0 100644 --- a/include/asterisk/astobj2.h +++ b/include/asterisk/astobj2.h @@ -204,7 +204,7 @@ static struct mydata *my_alloc_debug(void *data, { struct mydata *p; - p = __ao2_alloc(sizeof(*p), NULL, AO2_ALLOC_OPT_LOCK_MUTEX, tag, file, line, func); + p = __ao2_t_alloc(sizeof(*p), NULL, tag, file, line, func); if (p) { p->data = data; } @@ -401,17 +401,40 @@ enum ao2_alloc_opts { */ #define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \ - __ao2_alloc((data_size), (destructor_fn), (options), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_alloc_full((data_size), (destructor_fn), (options), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_alloc_options(data_size, destructor_fn, options) \ - __ao2_alloc((data_size), (destructor_fn), (options), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_alloc_full((data_size), (destructor_fn), (options), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_t_alloc(data_size, destructor_fn, debug_msg) \ - __ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_alloc_full((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_alloc(data_size, destructor_fn) \ - __ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_alloc_full((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) -void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; +void *__ao2_alloc_full(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, + const char *tag, const char *file, int line, const char *func, void *debugstorage) + attribute_warn_unused_result; + +#define __ao2_t_alloc(data_size, destructor_fn, tag, file, line, func) \ + __ao2_alloc_full((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (tag), (file), (line), (func), NULL) + +#define __ao2_alloc(data_size, destructor_fn, file, line, func) \ + __ao2_alloc_full((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "", (file), (line), (func), NULL) + +#define ao2_alloc_full(data_size, destructor_fn, options, tag, debugstorage) \ + __ao2_alloc_full((data_size), (destructor_fn), (options), \ + (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, (debugstorage)) + +#define ao2_s_alloc(storage, data_size, destructor_fn, options, tag) \ + ao2_s_getter(storage, __ao2_alloc_full, (data_size), (destructor_fn), (options), (tag)) + +#define __ao2_s_getter(storage, method, ...) \ + ({ \ + typeof(storage) __dst ## __LINE__ = (storage); \ + *__dst ## __LINE__ = method(__VA_ARGS__, __dst ## __LINE__); \ + ((*__dst ## __LINE__) ? 1 : 0); \ + }) +#define ao2_s_getter(storage, method, ...) \ + __ao2_s_getter(storage, method, __VA_ARGS__, __FILE__, __LINE__, __PRETTY_FUNCTION__) /*! @} */ @@ -430,10 +453,10 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned in * \note lockobj must be a valid AO2 object. */ #define ao2_alloc_with_lockobj(data_size, destructor_fn, lockobj, tag) \ - __ao2_alloc_with_lockobj((data_size), (destructor_fn), (lockobj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_alloc_with_lockobj((data_size), (destructor_fn), (lockobj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) attribute_warn_unused_result; /*! \brief * Reference/unreference an object and return the old refcount. @@ -480,18 +503,71 @@ unsigned int ao2_options_get(void *obj); * \param obj AO2 object to bump the refcount on. * \retval The given \a obj pointer. */ -#define ao2_t_bump(obj, tag) \ +#define ao2_bump_full(obj, tag, debugstorage) \ ({ \ typeof(obj) __obj_ ## __LINE__ = (obj); \ if (__obj_ ## __LINE__) { \ - ao2_t_ref(__obj_ ## __LINE__, +1, (tag)); \ + ao2_ref_full(__obj_ ## __LINE__, +1, (tag), (debugstorage)); \ } \ __obj_ ## __LINE__; \ }) +#define ao2_t_bump(obj, tag) \ + ao2_bump_full((obj), (tag), NULL) #define ao2_bump(obj) \ - ao2_t_bump((obj), "") + ao2_bump_full((obj), "", NULL) + +#define RAII_AO2_S_BUMP(vartype, varname, src) \ + RAII_AO2_S(vartype, varname, ao2_bump_full((src), #varname, &varname)) + +int __ao2_ref_full(void *o, int delta, + const char *tag, const char *file, int line, const char *func, void *debugstorage); + +#define __ao2_ref(o, delta, tag, file, line, func) \ + __ao2_ref_full((o), (delta), (tag), (file), (line), (func), NULL) + +#define ao2_ref_full(o, delta, tag, debugstorage) \ + __ao2_ref_full((o), (delta), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, (debugstorage)) + +void __ao2_ref_move(void *o, const char *tag, void *debugto, void *debugfrom, + const char *file, int line, const char *func); + +#define ao2_ref_move(o, tag, to, from) \ + __ao2_ref_move(o, tag, to, from, __FILE__, __LINE__, __PRETTY_FUNCTION__) + +#define ao2_s_ref(storage, delta, tag) \ + { \ + typeof(storage) __ref_ ## __LINE__ = (storage); \ + if (*__ref_ ## __LINE__) { \ + ao2_ref_full(*__ref_ ## __LINE__, (delta), (tag), __ref_ ## __LINE__); \ + } \ + } + +#define ao2_s_init(storage) \ + ao2_s_ref(storage, +1, "") + +#define ao2_s_cleanup(storage) \ + ao2_s_ref(storage, -1, "") + +#define ao2_s_set(storage, obj) \ + { \ + typeof(storage) __set_ ## __LINE__ = (storage); \ + *__set_ ## __LINE__ = obj; \ + ao2_s_init(__set_ ## __LINE__); \ + } + +#define ao2_s_replace(storage, newval) \ + { \ + typeof(storage) __dst_ ## __LINE__ = (storage); \ + typeof(newval) __src_ ## __LINE__ = (newval); \ + if (__src_ ## __LINE__ != *__dst_ ## __LINE__) { \ + if (__src_ ## __LINE__) {\ + ao2_ref_full(__src_ ## __LINE__, +1, "", __dst_ ## __LINE__); \ + } \ + ao2_s_cleanup(__dst_ ## __LINE__); \ + *__dst_ ## __LINE__ = __src_ ## __LINE__; \ + } \ + } -int __ao2_ref(void *o, int delta, const char *tag, const char *file, int line, const char *func); /*! * \since 12.4.0 @@ -549,13 +625,14 @@ struct ao2_weakproxy { * This can be done by using AO2_WEAKPROXY to declare your structure. */ void *__ao2_weakproxy_alloc(size_t data_size, ao2_destructor_fn destructor_fn, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) + attribute_warn_unused_result; #define ao2_weakproxy_alloc(data_size, destructor_fn) \ - __ao2_weakproxy_alloc(data_size, destructor_fn, "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_weakproxy_alloc(data_size, destructor_fn, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_t_weakproxy_alloc(data_size, destructor_fn, tag) \ - __ao2_weakproxy_alloc(data_size, destructor_fn, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_weakproxy_alloc(data_size, destructor_fn, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) /*! * \since 14.0.0 @@ -918,12 +995,15 @@ int __ao2_global_obj_replace_unref(struct ao2_global_obj *holder, void *obj, con * \retval NULL if no object available. */ #define ao2_t_global_obj_ref(holder, tag) \ - __ao2_global_obj_ref(&holder, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder) + __ao2_global_obj_ref(&holder, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder, NULL) #define ao2_global_obj_ref(holder) \ - __ao2_global_obj_ref(&holder, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder) + __ao2_global_obj_ref(&holder, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder, NULL) -void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name) attribute_warn_unused_result; +void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name, void *debugstorage) attribute_warn_unused_result; +#define RAII_AO2_S_GLOBAL(vartype, varname, holder) \ + RAII_AO2_S(vartype, varname, \ + __ao2_global_obj_ref((&holder), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder, &varname)) /*! \page AstObj2_Containers AstObj2 Containers @@ -1344,14 +1424,17 @@ struct ao2_container; */ #define ao2_t_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag) \ - __ao2_container_alloc_hash((ao2_options), (container_options), (n_buckets), (hash_fn), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_hash((ao2_options), (container_options), (n_buckets), (hash_fn), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn) \ - __ao2_container_alloc_hash((ao2_options), (container_options), (n_buckets), (hash_fn), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_hash((ao2_options), (container_options), (n_buckets), (hash_fn), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options, unsigned int container_options, unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) attribute_warn_unused_result; + +#define ao2_s_container_alloc_hash(storage, ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn, tag) \ + ao2_s_getter(storage, __ao2_container_alloc_hash, (ao2_options), (container_options), (n_buckets), (hash_fn), (sort_fn), (cmp_fn), (tag)) /*! * \brief Allocate and initialize a list container. @@ -1369,13 +1452,16 @@ struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options, */ #define ao2_t_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn, tag) \ - __ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn) \ - __ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) struct ao2_container *__ao2_container_alloc_list(unsigned int ao2_options, unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) attribute_warn_unused_result; + +#define ao2_s_container_alloc_list(storage, ao2_options, container_options, sort_fn, cmp_fn, tag) \ + ao2_s_getter(storage, __ao2_container_alloc_list, (ao2_options), (container_options), (sort_fn), (cmp_fn), (tag)) /*! * \brief Allocate and initialize a red-black tree container. @@ -1392,13 +1478,13 @@ struct ao2_container *__ao2_container_alloc_list(unsigned int ao2_options, */ #define ao2_t_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn, tag) \ - __ao2_container_alloc_rbtree((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_rbtree((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn) \ - __ao2_container_alloc_rbtree((ao2_options), (container_options), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_alloc_rbtree((ao2_options), (container_options), (sort_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) struct ao2_container *__ao2_container_alloc_rbtree(unsigned int ao2_options, unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) attribute_warn_unused_result; /*! \brief * Returns the number of elements in a container. @@ -1439,12 +1525,14 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu * \retval NULL on error. */ struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) + attribute_warn_unused_result; #define ao2_t_container_clone(orig, flags, tag) \ - __ao2_container_clone(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_clone(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_container_clone(orig, flags) \ - __ao2_container_clone(orig, flags, "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_container_clone(orig, flags, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) + /*! * \brief Print output. @@ -1728,13 +1816,19 @@ void *__ao2_unlink(struct ao2_container *c, void *obj, int flags, */ #define ao2_t_callback(c, flags, cb_fn, arg, tag) \ - __ao2_callback((c), (flags), (cb_fn), (arg), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_callback_full((c), (flags), (cb_fn), (arg), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_callback(c, flags, cb_fn, arg) \ - __ao2_callback((c), (flags), (cb_fn), (arg), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) - -void *__ao2_callback(struct ao2_container *c, enum search_flags flags, + __ao2_callback_full((c), (flags), (cb_fn), (arg), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) +#define __ao2_callback(c, flags, cb_fn, arg, tag, file, line, func) \ + __ao2_callback_full((c), (flags), (cb_fn), (arg), (tag), (file), (line), (func), NULL) +#define ao2_callback_full(c, flags, cb_fn, arg, tag, debugstorage) \ + __ao2_callback_full((c), (flags), (cb_fn), (arg), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, (debugstorage)) +#define ao2_s_callback(storage, c, flags, cb_fn, arg, tag) \ + ao2_s_getter(storage, __ao2_callback_full, (c), (flags), (cb_fn), (arg), (tag)) + +void *__ao2_callback_full(struct ao2_container *c, enum search_flags flags, ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line, - const char *func); + const char *func, void *debugstorage); /*! @} */ @@ -1755,25 +1849,38 @@ void *__ao2_callback(struct ao2_container *c, enum search_flags flags, */ #define ao2_t_callback_data(container, flags, cb_fn, arg, data, tag) \ - __ao2_callback_data((container), (flags), (cb_fn), (arg), (data), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_callback_data_full((container), (flags), (cb_fn), (arg), (data), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_callback_data(container, flags, cb_fn, arg, data) \ - __ao2_callback_data((container), (flags), (cb_fn), (arg), (data), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) - -void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags, + __ao2_callback_data_full((container), (flags), (cb_fn), (arg), (data), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) +#define __ao2_callback_data(c, flags, cb_fn, arg, data, tag, file, line, func) \ + __ao2_callback_data_full((c), (flags), (cb_fn), (arg), (data), (tag), (file), (line), (func), NULL) +#define ao2_callback_data_full(debugstorage, c, flags, cb_fn, arg, data, tag) \ + __ao2_callback_data_full((c), (flags), (cb_fn), (arg), (data), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, (debugstorage)) +#define ao2_s_callback_data(storage, c, flags, cb_fn, arg, data, tag) \ + ao2_s_getter(storage, __ao2_callback_data_full, (c), (flags), (cb_fn), (arg), (data), (tag)) + +void *__ao2_callback_data_full(struct ao2_container *c, enum search_flags flags, ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file, - int line, const char *func); + int line, const char *func, void *debugstorage); /*! ao2_find() is a short hand for ao2_callback(c, flags, c->cmp_fn, arg) * XXX possibly change order of arguments ? */ #define ao2_t_find(container, arg, flags, tag) \ - __ao2_find((container), (arg), (flags), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_find_full((container), (arg), (flags), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_find(container, arg, flags) \ - __ao2_find((container), (arg), (flags), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_find_full((container), (arg), (flags), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) +#define __ao2_find(container, arg, flags, tag, file, line, func) \ + __ao2_find_full((container), (arg), (flags), (tag), (file), (line), (func), NULL) +#define ao2_find_full(container, arg, flags, tag, debugstorage) \ + __ao2_find_full((container), (arg), (flags), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, (debugstorage)) -void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, - const char *tag, const char *file, int line, const char *func); +#define ao2_s_find(storage, container, arg, flags, tag) \ + ao2_s_getter(storage, __ao2_find_full, (container), (arg), (flags), (tag)) + +void *__ao2_find_full(struct ao2_container *c, const void *arg, enum search_flags flags, + const char *tag, const char *file, int line, const char *func, void *debugstorage); /*! * \brief Perform an ao2_find on a container with ao2_weakproxy objects, returning the real object. @@ -1909,8 +2016,25 @@ enum ao2_iterator_flags { * iteration. */ AO2_ITERATOR_DESCENDING = (1 << 3), + /*! + * Reference to the container is stored in the iterator. + * + * \note This flag is set within __ao2_iterator_init if debugstorage != NULL. + */ + AO2_ITERATOR_STOREDCONTAINER = (1 << 4), }; +/*! + * \brief Initialize an iterator for a container + * + * \param iter Iterator to initialize. Must point to valid memory. + * \param c the container + * \param flags one or more flags from ao2_iterator_flags. + * + * This function will take a reference on the container being iterated. + */ +void ao2_s_iterator_init(struct ao2_iterator *iter, struct ao2_container *c, int flags); + /*! * \brief Create an iterator for a container * @@ -1945,12 +2069,16 @@ void ao2_iterator_destroy(struct ao2_iterator *iter); #endif /* defined(TEST_FRAMEWORK) */ #define ao2_t_iterator_next(iter, tag) \ - __ao2_iterator_next((iter), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_iterator_next((iter), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) #define ao2_iterator_next(iter) \ - __ao2_iterator_next((iter), "", __FILE__, __LINE__, __PRETTY_FUNCTION__) + __ao2_iterator_next((iter), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, NULL) + +#define ao2_s_iterator_next(storage, iter) \ + ao2_s_getter(storage, __ao2_iterator_next, (iter), "") void *__ao2_iterator_next(struct ao2_iterator *iter, - const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result; + const char *tag, const char *file, int line, const char *func, void *debugstorage) + attribute_warn_unused_result; /*! * \brief Restart an iteration. diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index f459b319db..99bd160bbc 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -791,6 +791,11 @@ static inline void _raii_cleanup_block(_raii_cleanup_block_t *b) { (*b)(); } __block vartype varname = initval; \ _raii_cleanup_ ## varname = ^{ {(void)dtor(varname);} } +#define RAII_AO2_S(vartype, varname, initval) \ + _raii_cleanup_block_t _raii_cleanup_ ## varname __attribute__((cleanup(_raii_cleanup_block),unused)) = NULL; \ + __block vartype varname = initval; \ + _raii_cleanup_ ## varname = ^{ {(void)ao2_s_cleanup(&(varname));} } + #elif defined(__GNUC__) #define RAII_VAR(vartype, varname, initval, dtor) \ @@ -798,6 +803,11 @@ static inline void _raii_cleanup_block(_raii_cleanup_block_t *b) { (*b)(); } void _dtor_ ## varname (vartype * v) { dtor(*v); } \ vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval) +#define RAII_AO2_S(vartype, varname, initval) \ + auto void _dtor_ ## varname (vartype * v); \ + void _dtor_ ## varname (vartype * v) { ao2_s_cleanup(v); } \ + vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval) + #else #error "Cannot compile Asterisk: unknown and unsupported compiler." #endif /* #if __GNUC__ */ diff --git a/main/astobj2.c b/main/astobj2.c index 63058e19e3..c74f2d7e94 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -189,6 +189,15 @@ void log_bad_ao2(void *user_data, const char *file, int line, const char *func) __ast_assert_failed(0, bad_magic, file, line, func); } +void __ao2_ref_move(void *o, const char *tag, void *debugto, void *debugfrom, + const char *file, int line, const char *func) +{ + if (ast_opt_ref_debug) { + __ao2_ref_full(o, +1, tag, file, line, func, debugto); + __ao2_ref_full(o, -1, tag, file, line, func, debugfrom); + } +} + int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var) { struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func); @@ -458,8 +467,8 @@ void *ao2_object_get_lockaddr(void *user_data) return NULL; } -int __ao2_ref(void *user_data, int delta, - const char *tag, const char *file, int line, const char *func) +int __ao2_ref_full(void *user_data, int delta, + const char *tag, const char *file, int line, const char *func, void *debugstorage) { struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func); struct astobj2_lock *obj_mutex; @@ -471,8 +480,8 @@ int __ao2_ref(void *user_data, int delta, if (obj == NULL) { if (ref_log && user_data) { - fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%s\n", - user_data, delta, ast_get_tid(), file, line, func, tag ?: ""); + fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%p,%s\n", + user_data, delta, ast_get_tid(), file, line, func, debugstorage, tag ?: ""); fflush(ref_log); } return -1; @@ -548,9 +557,9 @@ int __ao2_ref(void *user_data, int delta, } if (ref_log && tag) { - fprintf(ref_log, "%p,%s%d,%d,%s,%d,%s,%d,%s\n", user_data, + fprintf(ref_log, "%p,%s%d,%d,%s,%d,%s,%d,%p,%s\n", user_data, (delta < 0 ? "" : "+"), delta, ast_get_tid(), - file, line, func, ret, tag); + file, line, func, ret, debugstorage, tag); fflush(ref_log); } return ret; @@ -561,9 +570,8 @@ int __ao2_ref(void *user_data, int delta, ast_log(__LOG_ERROR, file, line, func, "Invalid refcount %d on ao2 object %p\n", current_value, user_data); if (ref_log) { - /* Log to ref_log invalid even if (tag == NULL) */ - fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%s\n", - user_data, delta, ast_get_tid(), file, line, func, tag ?: ""); + fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%p,%s\n", + user_data, delta, ast_get_tid(), file, line, func, debugstorage, tag ?: ""); fflush(ref_log); } ast_assert(0); @@ -613,8 +621,8 @@ int __ao2_ref(void *user_data, int delta, } 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**,%p,%s\n", + user_data, delta, ast_get_tid(), file, line, func, debugstorage, ""); fflush(ref_log); } @@ -636,7 +644,7 @@ void __ao2_cleanup(void *obj) } static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, - void *lockobj, const char *tag, const char *file, int line, const char *func) + void *lockobj, const char *tag, const char *file, int line, const char *func, void *debugstorage) { /* allocation */ struct astobj2 *obj; @@ -705,8 +713,8 @@ static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_f #endif if (ref_log && tag) { - fprintf(ref_log, "%p,+1,%d,%s,%d,%s,**constructor**,%s\n", - EXTERNAL_OBJ(obj), ast_get_tid(), file, line, func, tag); + fprintf(ref_log, "%p,+1,%d,%s,%d,%s,**constructor**%zu**,%p,%s\n", + EXTERNAL_OBJ(obj), ast_get_tid(), file, line, func, data_size, debugstorage, tag); fflush(ref_log); } @@ -714,17 +722,17 @@ static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_f return EXTERNAL_OBJ(obj); } -void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, - const char *tag, const char *file, int line, const char *func) +void *__ao2_alloc_full(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, + const char *tag, const char *file, int line, const char *func, void *debugstorage) { - return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func); + return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func, debugstorage); } void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { return internal_ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_OBJ, lockobj, - tag, file, line, func); + tag, file, line, func, debugstorage); } unsigned int ao2_options_get(void *obj) @@ -777,7 +785,7 @@ int __ao2_global_obj_replace_unref(struct ao2_global_obj *holder, void *obj, con return 0; } -void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name) +void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name, void *debugstorage) { void *obj; @@ -796,7 +804,7 @@ void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const obj = holder->obj; if (obj) { - __ao2_ref(obj, +1, tag, file, line, func); + __ao2_ref_full(obj, +1, tag, file, line, func, debugstorage); } __ast_rwlock_unlock(file, line, func, &holder->lock, name); @@ -806,7 +814,7 @@ void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const void *__ao2_weakproxy_alloc(size_t data_size, ao2_destructor_fn destructor_fn, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { struct ao2_weakproxy *weakproxy; @@ -816,8 +824,8 @@ void *__ao2_weakproxy_alloc(size_t data_size, ao2_destructor_fn destructor_fn, return NULL; } - weakproxy = __ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_MUTEX, - tag, file, line, func); + weakproxy = __ao2_alloc_full(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_MUTEX, + tag, file, line, func, debugstorage); if (weakproxy) { struct astobj2 *weakproxy_internal; diff --git a/main/astobj2_container.c b/main/astobj2_container.c index 9bea58f74d..b740184934 100644 --- a/main/astobj2_container.c +++ b/main/astobj2_container.c @@ -47,7 +47,7 @@ int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flag if ((flags & AO2_UNLINK_NODE_UNLINK_OBJECT) && !(flags & AO2_UNLINK_NODE_NOUNREF_OBJECT)) { - __ao2_ref(node->obj, -1, tag ?: "Remove obj from container", file, line, func); + __ao2_ref_full(node->obj, -1, tag ?: "Remove obj from container", file, line, func, container); } node->obj = NULL; @@ -229,7 +229,7 @@ static int cb_true_data(void *user_data, void *arg, void *data, int flags) */ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { void *ret; ao2_callback_fn *cb_default = NULL; @@ -265,13 +265,13 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags * is destroyed, the container will be automatically * destroyed as well. */ - multi_container = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, - NULL, "OBJ_MULTIPLE return container creation"); + ao2_s_container_alloc_list(&multi_container, AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL, + "OBJ_MULTIPLE return container"); if (!multi_container) { return NULL; } if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) { - ao2_t_ref(multi_container, -1, "OBJ_MULTIPLE interator creation failed."); + ao2_s_cleanup(&multi_container); return NULL; } } @@ -362,7 +362,10 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags * Bump the ref count since we are not going to unlink and * transfer the container's object ref to the returned object. */ - __ao2_ref(ret, 1, tag ?: "Traversal found object", file, line, func); + __ao2_ref_full(ret, +1, tag ?: "Traversal found object", file, line, func, debugstorage); + } else if (ast_opt_ref_debug) { + /* move the reference to debugstorage from self */ + __ao2_ref_move(ret, "Traversal found object for OBJ_UNLINK", debugstorage, self, file, line, func); } } } @@ -397,35 +400,35 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags /* if multi_container was created, we are returning multiple objects */ if (multi_container) { - *multi_iterator = ao2_iterator_init(multi_container, + ao2_s_iterator_init(multi_iterator, multi_container, AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD); - ao2_t_ref(multi_container, -1, - "OBJ_MULTIPLE for multiple objects traversal complete."); + + ao2_s_cleanup(&multi_container); return multi_iterator; } else { return ret; } } -void *__ao2_callback(struct ao2_container *c, enum search_flags flags, +void *__ao2_callback_full(struct ao2_container *c, enum search_flags flags, ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line, - const char *func) + const char *func, void *debugstorage) { - return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, tag, file, line, func); + return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, tag, file, line, func, debugstorage); } -void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags, +void *__ao2_callback_data_full(struct ao2_container *c, enum search_flags flags, ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file, - int line, const char *func) + int line, const char *func, void *debugstorage) { - return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, tag, file, line, func); + return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, tag, file, line, func, debugstorage); } /*! * the find function just invokes the default callback with some reasonable flags. */ -void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, - const char *tag, const char *file, int line, const char *func) +void *__ao2_find_full(struct ao2_container *c, const void *arg, enum search_flags flags, + const char *tag, const char *file, int line, const char *func, void *debugstorage) { void *arged = (void *) arg;/* Done to avoid compiler const warning */ @@ -434,7 +437,7 @@ void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags fla ast_assert(0); return NULL; } - return __ao2_callback(c, flags, c->cmp_fn, arged, tag, file, line, func); + return __ao2_callback_full(c, flags, c->cmp_fn, arged, tag, file, line, func, debugstorage); } void *__ao2_weakproxy_find(struct ao2_container *c, const void *arg, enum search_flags flags, @@ -486,7 +489,7 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) { struct ao2_iterator a = { .c = c, - .flags = flags + .flags = flags & ~AO2_ITERATOR_STOREDCONTAINER }; ao2_t_ref(c, +1, "Init iterator with container."); @@ -494,6 +497,13 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) return a; } +void ao2_s_iterator_init(struct ao2_iterator *iter, struct ao2_container *c, int flags) +{ + memset(iter, 0, sizeof(*iter)); + ao2_s_set(&iter->c, c); + iter->flags = flags | AO2_ITERATOR_STOREDCONTAINER; +} + void ao2_iterator_restart(struct ao2_iterator *iter) { if (!is_ao2_object(iter->c)) { @@ -537,7 +547,8 @@ void ao2_iterator_destroy(struct ao2_iterator *iter) ao2_iterator_restart(iter); /* Release the iterated container reference. */ - ao2_t_ref(iter->c, -1, "Unref iterator in ao2_iterator_destroy"); + ao2_ref_full(iter->c, -1, "Unref iterator in ao2_iterator_destroy", + iter->flags & AO2_ITERATOR_STOREDCONTAINER ? &iter->c : NULL); iter->c = NULL; /* Free the malloced iterator. */ @@ -554,7 +565,7 @@ void ao2_iterator_cleanup(struct ao2_iterator *iter) } void *__ao2_iterator_next(struct ao2_iterator *iter, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { enum ao2_lock_req orig_lock; struct ao2_container_node *node; @@ -594,15 +605,13 @@ void *__ao2_iterator_next(struct ao2_iterator *iter, if (node) { ret = node->obj; - if (iter->flags & AO2_ITERATOR_UNLINK) { - /* Transfer the object ref from the container to the returned object. */ - __container_unlink_node_debug(node, AO2_UNLINK_NODE_DEC_COUNT, tag, file, line, func); + /* Bump ref of returned object */ + __ao2_ref_full(ret, +1, tag ?: "Next iterator object.", file, line, func, debugstorage); + if (iter->flags & AO2_ITERATOR_UNLINK) { /* Transfer the container's node ref to the iterator. */ + __container_unlink_node_debug(node, AO2_UNLINK_NODE_DEC_COUNT | AO2_UNLINK_NODE_UNLINK_OBJECT, tag, file, line, func); } else { - /* Bump ref of returned object */ - __ao2_ref(ret, +1, tag ?: "Next iterator object.", file, line, func); - /* Bump the container's node ref for the iterator. */ ao2_t_ref(node, +1, NULL); } @@ -697,7 +706,7 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu return res; } -struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func) +struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func, void *debugstorage) { struct ao2_container *clone; int failed; @@ -713,7 +722,7 @@ struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum sea return NULL; } - clone = orig->v_table->alloc_empty_clone(orig, tag, file, line, func); + clone = orig->v_table->alloc_empty_clone(orig, tag, file, line, func, debugstorage); if (!clone) { return NULL; } diff --git a/main/astobj2_container_private.h b/main/astobj2_container_private.h index 3651ca41f3..5c28becba6 100644 --- a/main/astobj2_container_private.h +++ b/main/astobj2_container_private.h @@ -95,7 +95,7 @@ typedef void (*ao2_container_destroy_fn)(struct ao2_container *self); * \retval empty-container on success. * \retval NULL on error. */ -typedef struct ao2_container *(*ao2_container_alloc_empty_clone_fn)(struct ao2_container *self, const char *tag, const char *file, int line, const char *func); +typedef struct ao2_container *(*ao2_container_alloc_empty_clone_fn)(struct ao2_container *self, const char *tag, const char *file, int line, const char *func, void *debugstorage); /*! * \brief Create a new container node. diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c index 55077c5d34..d9ab7ed31c 100644 --- a/main/astobj2_hash.c +++ b/main/astobj2_hash.c @@ -114,7 +114,7 @@ struct hash_traversal_state_check { * \retval NULL on error. */ static struct ao2_container *hash_ao2_alloc_empty_clone(struct ao2_container_hash *self, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { if (!__is_ao2_object(self, file, line, func)) { return NULL; @@ -122,7 +122,7 @@ static struct ao2_container *hash_ao2_alloc_empty_clone(struct ao2_container_has return __ao2_container_alloc_hash(ao2_options_get(self), self->common.options, self->n_buckets, self->hash_fn, self->common.sort_fn, self->common.cmp_fn, - tag, file, line, func); + tag, file, line, func, debugstorage); } /*! @@ -217,7 +217,7 @@ static struct hash_bucket_node *hash_ao2_new_node(struct ao2_container_hash *sel i = abs(self->hash_fn(obj_new, OBJ_SEARCH_OBJECT) % self->n_buckets); - __ao2_ref(obj_new, +1, tag ?: "Container node creation", file, line, func); + __ao2_ref_full(obj_new, +1, tag ?: "Container node creation", file, line, func, self); node->common.obj = obj_new; node->common.my_container = (struct ao2_container *) self; node->my_bucket = i; @@ -1078,7 +1078,7 @@ static struct ao2_container *hash_ao2_container_init( struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options, unsigned int container_options, unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { unsigned int num_buckets; size_t container_size; @@ -1087,16 +1087,16 @@ struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options, num_buckets = hash_fn ? n_buckets : 1; container_size = sizeof(struct ao2_container_hash) + num_buckets * sizeof(struct hash_bucket); - self = __ao2_alloc(container_size, container_destruct, ao2_options, - tag ?: __PRETTY_FUNCTION__, file, line, func); + self = __ao2_alloc_full(container_size, container_destruct, ao2_options, + tag ?: __PRETTY_FUNCTION__, file, line, func, debugstorage); return hash_ao2_container_init(self, container_options, num_buckets, hash_fn, sort_fn, cmp_fn); } struct ao2_container *__ao2_container_alloc_list(unsigned int ao2_options, unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { return __ao2_container_alloc_hash(ao2_options, container_options, 1, NULL, - sort_fn, cmp_fn, tag, file, line, func); + sort_fn, cmp_fn, tag, file, line, func, debugstorage); } diff --git a/main/astobj2_rbtree.c b/main/astobj2_rbtree.c index 73d689664e..6e870dbe4d 100644 --- a/main/astobj2_rbtree.c +++ b/main/astobj2_rbtree.c @@ -556,14 +556,14 @@ static void rb_rotate_right(struct ao2_container_rbtree *self, struct rbtree_nod * \retval NULL on error. */ static struct ao2_container *rb_ao2_alloc_empty_clone(struct ao2_container_rbtree *self, - const char *tag, const char *file, int line, const char *func) + const char *tag, const char *file, int line, const char *func, void *debugstorage) { if (!__is_ao2_object(self, file, line, func)) { return NULL; } return __ao2_container_alloc_rbtree(ao2_options_get(self), self->common.options, - self->common.sort_fn, self->common.cmp_fn, tag, file, line, func); + self->common.sort_fn, self->common.cmp_fn, tag, file, line, func, debugstorage); } /*! @@ -910,7 +910,7 @@ static struct rbtree_node *rb_ao2_new_node(struct ao2_container_rbtree *self, vo return NULL; } - __ao2_ref(obj_new, +1, tag ?: "Container node creation", file, line, func); + __ao2_ref_full(obj_new, +1, tag ?: "Container node creation", file, line, func, self); node->common.obj = obj_new; node->common.my_container = (struct ao2_container *) self; @@ -2036,9 +2036,9 @@ static struct ao2_container *rb_ao2_container_init(struct ao2_container_rbtree * return (struct ao2_container *) self; } -struct ao2_container *__ao2_container_alloc_rbtree(unsigned int ao2_options, unsigned int container_options, - ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, - const char *tag, const char *file, int line, const char *func) +struct ao2_container *__ao2_container_alloc_rbtree(unsigned int ao2_options, + unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn, + const char *tag, const char *file, int line, const char *func, void *debugstorage) { struct ao2_container_rbtree *self; @@ -2048,7 +2048,7 @@ struct ao2_container *__ao2_container_alloc_rbtree(unsigned int ao2_options, uns return NULL; } - self = __ao2_alloc(sizeof(*self), container_destruct, ao2_options, - tag ?: __PRETTY_FUNCTION__, file, line, func); + self = __ao2_alloc_full(sizeof(*self), container_destruct, ao2_options, + tag ?: __PRETTY_FUNCTION__, file, line, func, debugstorage); return rb_ao2_container_init(self, container_options, sort_fn, cmp_fn); } diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index b926514ca4..235dd0bf5d 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -1291,8 +1291,7 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), { struct ast_channel *tmp; - tmp = __ao2_alloc(sizeof(*tmp), destructor, - AO2_ALLOC_OPT_LOCK_MUTEX, "", file, line, function); + tmp = __ao2_alloc(sizeof(*tmp), destructor, file, line, function); if (!tmp) { return NULL; diff --git a/main/format_cap.c b/main/format_cap.c index d71ccdbaa5..5630ab33d1 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -119,8 +119,8 @@ struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags, { struct ast_format_cap *cap; - cap = __ao2_alloc(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK, - tag, file, line, func); + cap = __ao2_alloc_full(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK, + tag, file, line, func, NULL); if (!cap) { return NULL; } diff --git a/main/named_locks.c b/main/named_locks.c index 2fb8f440f3..67fbab439e 100644 --- a/main/named_locks.c +++ b/main/named_locks.c @@ -92,7 +92,7 @@ struct ast_named_lock *__ast_named_lock_get(const char *filename, int lineno, co goto failure_cleanup; } - lock = __ao2_alloc(sizeof(*lock) + keylen, NULL, lock_type, concat_key, filename, lineno, func); + lock = __ao2_alloc_full(sizeof(*lock) + keylen, NULL, lock_type, concat_key, filename, lineno, func, NULL); if (!lock) { goto failure_cleanup; } diff --git a/main/sorcery.c b/main/sorcery.c index 9028707930..5f382a4b08 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -605,7 +605,7 @@ struct ast_sorcery *__ast_sorcery_open(const char *module_name, const char *file } strcpy(proxy->module_name, module_name); /* Safe */ - sorcery = __ao2_alloc(sizeof(*sorcery), sorcery_destructor, AO2_ALLOC_OPT_LOCK_MUTEX, module_name, file, line, func); + sorcery = __ao2_alloc_full(sizeof(*sorcery), sorcery_destructor, AO2_ALLOC_OPT_LOCK_MUTEX, module_name, file, line, func, NULL); if (!sorcery) { goto failure_cleanup; } diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 137f83bac7..ed4bfe4f26 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -1382,7 +1382,7 @@ static struct mohclass *_moh_class_malloc(const char *file, int line, const char { struct mohclass *class; - class = __ao2_alloc(sizeof(*class), moh_class_destructor, AO2_ALLOC_OPT_LOCK_MUTEX, + class = __ao2_t_alloc(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname); if (class) { class->format = ao2_bump(ast_format_slin); -- 2.17.1