mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	main/astobj2: Make REF_DEBUG a menuselect item; improve REF_DEBUG output
This patch does the following:
(1) It makes REF_DEBUG a meneselect item. Enabling REF_DEBUG now enables
    REF_DEBUG globally throughout Asterisk.
(2) The ref debug log file is now created in the AST_LOG_DIR directory.
    Every run will now blow away the previous run (as large ref files
    sometimes caused issues). We now also no longer open/close the file
    on each write, instead relying on fflush to make sure data gets written
    to the file (in case the ao2 call being performed is about to cause a
    crash)
(3) It goes with a comma delineated format for the ref debug file. This
    makes parsing much easier. This also now includes the thread ID of the
    thread that caused ref change.
(4) A new python script instead for refcounting has been added in the
    contrib/scripts folder.
(5) The old refcounter implementation in utils/ has been removed.
Review: https://reviewboard.asterisk.org/r/3377/
........
Merged revisions 412114 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 412115 from http://svn.asterisk.org/svn/asterisk/branches/11
........
Merged revisions 412153 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@412154 65c4cc65-6c06-0410-ace0-fbb531ad65f3
			
			
This commit is contained in:
		| @@ -174,5 +174,9 @@ Realtime Configuration: | ||||
|  - A new set of Alembic scripts has been added for CDR tables. This will create | ||||
|    a 'cdr' table with the default schema that Asterisk expects. | ||||
|  | ||||
| Utilities: | ||||
|  - The refcounter program has been removed in favor of the refcounter.py script | ||||
|    in contrib/scripts. | ||||
|  | ||||
| =========================================================== | ||||
| =========================================================== | ||||
|   | ||||
| @@ -5,6 +5,9 @@ | ||||
| 		<member name="DEBUG_THREADS" displayname="Enable Thread Debugging"> | ||||
| 			<support_level>core</support_level> | ||||
| 		</member> | ||||
| 		<member name="REF_DEBUG" displayname="Enable reference count debugging"> | ||||
| 			<support_level>extended</support_level> | ||||
| 		</member> | ||||
| 		<member name="STATIC_BUILD" displayname="Build static binaries"> | ||||
| 			<support_level>extended</support_level> | ||||
| 		</member> | ||||
|   | ||||
| @@ -230,21 +230,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
| 
 | ||||
| #include "asterisk/network.h" | ||||
| #include "asterisk/paths.h"	/* need ast_config_AST_SYSTEM_NAME */ | ||||
| /*
 | ||||
|    Uncomment the define below,  if you are having refcount related memory leaks. | ||||
|    With this uncommented, this module will generate a file, /tmp/refs, which contains | ||||
|    a history of the ao2_ref() calls. To be useful, all calls to ao2_* functions should | ||||
|    be modified to ao2_t_* calls, and include a tag describing what is happening with | ||||
|    enough detail, to make pairing up a reference count increment with its corresponding decrement. | ||||
|    The refcounter program in utils/ can be invaluable in highlighting objects that are not | ||||
|    balanced, along with the complete history for that object. | ||||
|    In normal operation, the macros defined will throw away the tags, so they do not | ||||
|    affect the speed of the program at all. They can be considered to be documentation. | ||||
| 
 | ||||
|    Note: This must also be enabled in channels/sip/security_events.c | ||||
| */ | ||||
| /* #define  REF_DEBUG 1 */ | ||||
| 
 | ||||
| #include "asterisk/lock.h" | ||||
| #include "asterisk/config.h" | ||||
| #include "asterisk/module.h" | ||||
|   | ||||
| @@ -32,8 +32,6 @@ | ||||
|  | ||||
| ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
|  | ||||
| /* #define  REF_DEBUG 1 */ | ||||
|  | ||||
| #include "include/sip.h" | ||||
| #include "include/security_events.h" | ||||
|  | ||||
|   | ||||
							
								
								
									
										171
									
								
								contrib/scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										171
									
								
								contrib/scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| #!/usr/bin/env python | ||||
| """Process a ref debug log | ||||
|  | ||||
|  This file will process a log file created by the REF_DEBUG | ||||
|  build option in Asterisk. | ||||
|  | ||||
|  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) 2014, Digium, Inc. | ||||
|  Matt Jordan <mjordan@digium.com> | ||||
| """ | ||||
|  | ||||
| import sys | ||||
| import os | ||||
|  | ||||
| from optparse import OptionParser | ||||
|  | ||||
| def parse_line(line): | ||||
|     """Parse out a line into its constituent parts. | ||||
|  | ||||
|     Keyword Arguments: | ||||
|     line The line from a ref debug log to parse out | ||||
|  | ||||
|     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 | ||||
|  | ||||
|     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(filename): | ||||
|     """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 | ||||
|         - A list of objects whose lifetimes were not completed | ||||
|         - A list of objects whose lifetimes are skewed | ||||
|     """ | ||||
|  | ||||
|     finished_objects = [] | ||||
|     leaked_objects = [] | ||||
|     skewed_objects = [] | ||||
|     current_objects = {} | ||||
|  | ||||
|     with open(filename, 'r') as ref_file: | ||||
|         for line in ref_file: | ||||
|             parsed_line = parse_line(line) | ||||
|             if not parsed_line: | ||||
|                 continue | ||||
|  | ||||
|             obj = parsed_line['addr'] | ||||
|  | ||||
|             if obj not in current_objects: | ||||
|                 current_objects[obj] = [] | ||||
|                 if 'constructor' not in parsed_line['state']: | ||||
|                     skewed_objects.append(current_objects[obj]) | ||||
|             current_objects[obj].append(parsed_line) | ||||
|  | ||||
|             if 'destructor' in parsed_line['state']: | ||||
|                 lifetime = current_objects.get(obj) | ||||
|                 finished_objects.append(lifetime) | ||||
|                 del current_objects[obj] | ||||
|  | ||||
|     leaked_objects = current_objects.values() | ||||
|     return (finished_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]['addr']) | ||||
|         for entry in obj: | ||||
|             print "[%s] %s:%s %s: %s %s - [%s]" % (entry['thread_id'], | ||||
|                                               entry['file'], entry['line'], | ||||
|                                               entry['function'], | ||||
|                                               entry['delta'], entry['tag'], | ||||
|                                               entry['state']) | ||||
|         print "\n" | ||||
|  | ||||
|  | ||||
| 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("-l", "--suppress-leaks", action="store_false", | ||||
|                       dest="leaks", default=True, | ||||
|                       help="If specified, don't output leaked objects") | ||||
|     parser.add_option("-n", "--suppress-normal", action="store_false", | ||||
|                       dest="normal", default=True, | ||||
|                       help="If specified, don't output objects with a " \ | ||||
|                            "complete lifetime") | ||||
|     parser.add_option("-s", "--suppress-skewed", action="store_false", | ||||
|                       dest="skewed", default=True, | ||||
|                       help="If specified, don't output objects with a " \ | ||||
|                            "skewed lifetime") | ||||
|  | ||||
|     (options, args) = parser.parse_args(argv) | ||||
|  | ||||
|     if not os.path.isfile(options.filepath): | ||||
|         print >>sys.stderr, "File not found: %s" % options.filepath | ||||
|         return -1 | ||||
|  | ||||
|     try: | ||||
|         (finished_objects, | ||||
|          leaked_objects, | ||||
|          skewed_objects) = process_file(options.filepath) | ||||
|  | ||||
|         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") | ||||
|  | ||||
|     except (KeyboardInterrupt, SystemExit, IOError): | ||||
|         print >>sys.stderr, "File processing cancelled" | ||||
|         return -1 | ||||
|  | ||||
|     return ret_code | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     sys.exit(main(sys.argv)) | ||||
| @@ -141,11 +141,10 @@ list. However there is no ordering among elements. | ||||
| \note DEBUGGING REF COUNTS BIBLE: | ||||
| An interface to help debug refcounting is provided | ||||
| in this package. It is dependent on the REF_DEBUG macro being | ||||
| defined in a source file, before the #include of astobj2.h, | ||||
| and in using variants of the normal ao2_xxx functions | ||||
| that are named ao2_t_xxx instead, with an extra argument, a string, | ||||
| that will be printed out into /tmp/refs when the refcount for an | ||||
| object is changed. | ||||
| defined via menuselect and in using variants of the normal ao2_xxxx | ||||
| function that are named ao2_t_xxxx instead, with an extra argument, | ||||
| a string that will be printed out into the refs log file when the | ||||
| refcount for an object is changed. | ||||
|  | ||||
|   these ao2_t_xxx variants are provided: | ||||
|  | ||||
| @@ -164,45 +163,56 @@ each case is supposed to be a string pointer, a "tag", that should contain | ||||
| enough of an explanation, that you can pair operations that increment the | ||||
| ref count, with operations that are meant to decrement the refcount. | ||||
|  | ||||
| Each of these calls will generate at least one line of output in /tmp/refs. | ||||
| These lines look like this: | ||||
| Each of these calls will generate at least one line of output in in the refs | ||||
| log files. These lines look like this: | ||||
| ... | ||||
| 0x8756f00 =1   chan_sip.c:22240:load_module (allocate users) | ||||
| 0x86e3408 =1   chan_sip.c:22241:load_module (allocate peers) | ||||
| 0x86dd380 =1   chan_sip.c:22242:load_module (allocate peers_by_ip) | ||||
| 0x822d020 =1   chan_sip.c:22243:load_module (allocate dialogs) | ||||
| 0x8930fd8 =1   chan_sip.c:20025:build_peer (allocate a peer struct) | ||||
| 0x8930fd8 +1   chan_sip.c:21467:reload_config (link peer into peer table) [@1] | ||||
| 0x8930fd8 -1   chan_sip.c:2370:unref_peer (unref_peer: from reload_config) [@2] | ||||
| 0x89318b0 =1   chan_sip.c:20025:build_peer (allocate a peer struct) | ||||
| 0x89318b0 +1   chan_sip.c:21467:reload_config (link peer into peer table) [@1] | ||||
| 0x89318b0 -1   chan_sip.c:2370:unref_peer (unref_peer: from reload_config) [@2] | ||||
| 0x8930218 =1   chan_sip.c:20025:build_peer (allocate a peer struct) | ||||
| 0x8930218 +1   chan_sip.c:21539:reload_config (link peer into peers table) [@1] | ||||
| 0x868c040 -1   chan_sip.c:2424:dialog_unlink_all (unset the relatedpeer->call field in tandem with relatedpeer field itself) [@2] | ||||
| 0x868c040 -1   chan_sip.c:2443:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time) [@1] | ||||
| 0x868c040 **call destructor** chan_sip.c:2443:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time) | ||||
| 0x8cc07e8 -1   chan_sip.c:2370:unref_peer (unsetting a dialog relatedpeer field in sip_destroy) [@3] | ||||
| 0x8cc07e8 +1   chan_sip.c:3876:find_peer (ao2_find in peers table) [@2] | ||||
| 0x8cc07e8 -1   chan_sip.c:2370:unref_peer (unref_peer, from sip_devicestate, release ref from find_peer) [@3] | ||||
| 0x8756f00,+1,1234,chan_sip.c,22240,load_module,**constructor**,allocate users | ||||
| 0x86e3408,+1,1234,chan_sip.c,22241,load_module,**constructor**,allocate peers | ||||
| 0x86dd380,+1,1234,chan_sip.c,22242,load_module,**constructor**,allocate peers_by_ip | ||||
| 0x822d020,+1,1234,chan_sip.c,22243,load_module,**constructor**,allocate dialogs | ||||
| 0x8930fd8,+1,1234,chan_sip.c,20025,build_peer,**constructor**,allocate a peer struct | ||||
| 0x8930fd8,+1,1234,chan_sip.c,21467,reload_config,1,link peer into peer table | ||||
| 0x8930fd8,-1,1234,chan_sip.c,2370,unref_peer,2,unref_peer: from reload_config | ||||
| 0x89318b0,1,5678,chan_sip.c,20025,build_peer,**constructor**,allocate a peer struct | ||||
| 0x89318b0,+1,5678,chan_sip.c,21467,reload_config,1,link peer into peer table | ||||
| 0x89318b0,-1,1234,chan_sip.c,2370,unref_peer,2,unref_peer: from reload_config | ||||
| 0x8930218,+1,1234,chan_sip.c,20025,build_peer,**constructor**,allocate a peer struct | ||||
| 0x8930218,+1,1234,chan_sip.c,21539,reload_config,1,link peer into peers table | ||||
| 0x868c040,-1,1234,chan_sip.c,2424,dialog_unlink_all,2,unset the relatedpeer->call field in tandem with relatedpeer field itself | ||||
| 0x868c040,-1,1234,chan_sip.c,2443,dialog_unlink_all,1,Let's unbump the count in the unlink so the poor pvt can disappear if it is time | ||||
| 0x868c040,-1,1234,chan_sip.c,2443,dialog_unlink_all,**destructor**,Let's unbump the count in the unlink so the poor pvt can disappear if it is time | ||||
| 0x8cc07e8,-1,1234,chan_sip.c,2370,unref_peer,3,unsetting a dialog relatedpeer field in sip_destroy | ||||
| 0x8cc07e8,+1,1234,chan_sip.c,3876,find_peer,2,ao2_find in peers table | ||||
| 0x8cc07e8,-1,1234,chan_sip.c,2370,unref_peer,3,unref_peer, from sip_devicestate, release ref from find_peer | ||||
| ... | ||||
|  | ||||
| The first column is the object address. | ||||
| The second column reflects how the operation affected the ref count | ||||
|     for that object. Creation sets the ref count to 1 (=1). | ||||
|     increment or decrement and amount are specified (-1/+1). | ||||
| The remainder of the line specifies where in the file the call was made, | ||||
|     and the function name, and the tag supplied in the function call. | ||||
|  | ||||
| The **call destructor** is specified when the destroy routine is | ||||
| run for an object. It does not affect the ref count, but is important | ||||
| in debugging, because it is possible to have the astobj2 system run it | ||||
| multiple times on the same object, commonly fatal to asterisk. | ||||
| This uses a comma delineated format. The columns in the format are as | ||||
| follows: | ||||
| - The first column is the object address. | ||||
| - The second column reflects how the operation affected the ref count | ||||
|     for that object. A change in the ref count is reflected either as | ||||
|     an increment (+) or decrement (-), as well as the amount it changed | ||||
|     by. | ||||
| - The third column is the ID of the thread that modified the reference | ||||
|   count. | ||||
| - The fourth column is the source file that the change in reference was | ||||
|   issued from. | ||||
| - The fifth column is the line number of the source file that the ref | ||||
|   change was issued from. | ||||
| - The sixth column is the name of the function that the ref change was | ||||
|   issued from. | ||||
| - The seventh column indicates either (a) construction of the object via | ||||
|   the special tag **constructor**; (b) destruction of the object via | ||||
|   the special tag **destructor**; (c) the previous reference count | ||||
|   prior to this reference change. | ||||
| - The eighth column is a special tag added by the developer to provide | ||||
|   context for the ref change. Note that any subsequent columns are | ||||
|   considered to be part of this tag. | ||||
|  | ||||
| Sometimes you have some helper functions to do object ref/unref | ||||
| operations. Using these normally hides the place where these | ||||
| functions were called. To get the location where these functions | ||||
| were called to appear in /tmp/refs, you can do this sort of thing: | ||||
| were called to appear in /refs, you can do this sort of thing: | ||||
|  | ||||
| #ifdef REF_DEBUG | ||||
| #define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__) | ||||
| @@ -250,13 +260,14 @@ file, function, and line number info provided. You might find this | ||||
| well worth the effort to help track these function calls in the code. | ||||
|  | ||||
| To find out why objects are not destroyed (a common bug), you can | ||||
| edit the source file to use the ao2_t_* variants, add the #define REF_DEBUG 1 | ||||
| before the #include "asterisk/astobj2.h" line, and add a descriptive | ||||
| tag to each call. Recompile, and run Asterisk, exit asterisk with | ||||
| "stop gracefully", which should result in every object being destroyed. | ||||
| Then, you can "sort -k 1 /tmp/refs > x1" to get a sorted list of | ||||
| all the objects, or you can use "util/refcounter" to scan the file | ||||
| for you and output any problems it finds. | ||||
| edit the source file to use the ao2_t_* variants, enable REF_DEBUG | ||||
| in menuselect, and add a descriptive tag to each call. Recompile, | ||||
| and run Asterisk, exit asterisk with "core stop gracefully", which should | ||||
| result in every object being destroyed. | ||||
|  | ||||
| Then, you can "sort -k 1 {AST_LOG_DIR}/refs > x1" to get a sorted list of | ||||
| all the objects, or you can use "contrib/script/refcounter.py" to scan | ||||
| the file for you and output any problems it finds. | ||||
|  | ||||
| The above may seem astronomically more work than it is worth to debug | ||||
| reference counts, which may be true in "simple" situations, but for | ||||
| @@ -272,29 +283,29 @@ For instance, here is an example of this for a dialog object in | ||||
| chan_sip, that was not getting destroyed, after I moved the lines around | ||||
| to pair operations: | ||||
|  | ||||
|    0x83787a0 =1   chan_sip.c:5733:sip_alloc (allocate a dialog(pvt) struct) | ||||
|    0x83787a0 -1   chan_sip.c:19173:sip_poke_peer (unref dialog at end of sip_poke_peer, obtained from sip_alloc, just before it goes out of scope) [@4] | ||||
|    0x83787a0,+1,1234,chan_sip.c,5733,sip_alloc,**constructor**,(allocate a dialog(pvt) struct) | ||||
|    0x83787a0,-1,1234,chan_sip.c,19173,sip_poke_peer,4,(unref dialog at end of sip_poke_peer, obtained from sip_alloc, just before it goes out of scope) | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:5854:sip_alloc (link pvt into dialogs table) [@1] | ||||
|    0x83787a0 -1   chan_sip.c:19150:sip_poke_peer (About to change the callid -- remove the old name) [@3] | ||||
|    0x83787a0 +1   chan_sip.c:19152:sip_poke_peer (Linking in under new name) [@2] | ||||
|    0x83787a0 -1   chan_sip.c:2399:dialog_unlink_all (unlinking dialog via ao2_unlink) [@5] | ||||
|    0x83787a0,+1,1234,chan_sip.c,5854,sip_alloc,1,(link pvt into dialogs table) | ||||
|    0x83787a0,-1,1234,chan_sip.c,19150,sip_poke_peer,3,(About to change the callid -- remove the old name) | ||||
|    0x83787a0,+1,1234,chan_sip.c,19152,sip_poke_peer,2,(Linking in under new name) | ||||
|    0x83787a0,-1,1234,chan_sip.c,2399,dialog_unlink_all,5,(unlinking dialog via ao2_unlink) | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:19130:sip_poke_peer (copy sip alloc from p to peer->call) [@2] | ||||
|    0x83787a0,+1,1234,chan_sip.c,19130,sip_poke_peer,2,(copy sip alloc from p to peer->call) | ||||
|  | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:2996:__sip_reliable_xmit (__sip_reliable_xmit: setting pkt->owner) [@3] | ||||
|    0x83787a0 -1   chan_sip.c:2425:dialog_unlink_all (remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy) [@4] | ||||
|    0x83787a0,+1,1234,chan_sip.c,2996,__sip_reliable_xmit,3,(__sip_reliable_xmit: setting pkt->owner) | ||||
|    0x83787a0,-1,1234,chan_sip.c,2425,dialog_unlink_all,4,(remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy) | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:22356:unload_module (iterate thru dialogs) [@4] | ||||
|    0x83787a0 -1   chan_sip.c:22359:unload_module (toss dialog ptr from iterator_next) [@5] | ||||
|    0x83787a0,+1,1234,chan_sip.c,22356,unload_module,4,(iterate thru dialogs) | ||||
|    0x83787a0,-1,1234,chan_sip.c,22359,unload_module,5,(toss dialog ptr from iterator_next) | ||||
|  | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:22373:unload_module (iterate thru dialogs) [@3] | ||||
|    0x83787a0 -1   chan_sip.c:22375:unload_module (throw away iterator result) [@2] | ||||
|    0x83787a0,+1,1234,chan_sip.c,22373,unload_module,3,(iterate thru dialogs) | ||||
|    0x83787a0,-1,1234,chan_sip.c,22375,unload_module,2,(throw away iterator result) | ||||
|  | ||||
|    0x83787a0 +1   chan_sip.c:2397:dialog_unlink_all (Let's bump the count in the unlink so it doesn't accidentally become dead before we are done) [@4] | ||||
|    0x83787a0 -1   chan_sip.c:2436:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time) [@3] | ||||
|    0x83787a0,+1,1234,chan_sip.c,2397,dialog_unlink_all,4,(Let's bump the count in the unlink so it doesn't accidentally become dead before we are done) | ||||
|    0x83787a0,-1,1234,chan_sip.c,2436,dialog_unlink_all,3,(Let's unbump the count in the unlink so the poor pvt can disappear if it is time) | ||||
|  | ||||
| As you can see, only one unbalanced operation is in the list, a ref count increment when | ||||
| the peer->call was set, but no corresponding decrement was made... | ||||
|   | ||||
| @@ -4267,6 +4267,8 @@ int main(int argc, char *argv[]) | ||||
| 	register_config_cli(); | ||||
| 	read_config_maps(); | ||||
|  | ||||
| 	astobj2_init(); | ||||
|  | ||||
| 	if (ast_opt_console) { | ||||
| 		if (el_hist == NULL || el == NULL) | ||||
| 			ast_el_initialize(); | ||||
| @@ -4376,8 +4378,6 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 	threadstorage_init(); | ||||
|  | ||||
| 	astobj2_init(); | ||||
|  | ||||
| 	ast_format_attr_init(); | ||||
| 	ast_format_list_init(); | ||||
| 	if (ast_rtp_engine_init()) { | ||||
|   | ||||
| @@ -34,13 +34,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
| #include "asterisk/dlinkedlists.h" | ||||
| #include "asterisk/utils.h" | ||||
| #include "asterisk/cli.h" | ||||
| #define REF_FILE "/tmp/refs" | ||||
| #include "asterisk/paths.h" | ||||
|  | ||||
| #if defined(TEST_FRAMEWORK) | ||||
| /* We are building with the test framework enabled so enable AO2 debug tests as well. */ | ||||
| #define AO2_DEBUG 1 | ||||
| #endif	/* defined(TEST_FRAMEWORK) */ | ||||
|  | ||||
| static FILE *ref_log; | ||||
|  | ||||
| /*! | ||||
|  * astobj2 objects are always preceded by this data structure, | ||||
|  * which contains a reference counter, | ||||
| @@ -517,19 +519,14 @@ int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *fil | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (delta != 0) { | ||||
| 		FILE *refo = fopen(REF_FILE, "a"); | ||||
| 		if (refo) { | ||||
| 			fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"), | ||||
| 				delta, file, line, func, tag, obj->priv_data.ref_counter); | ||||
| 			fclose(refo); | ||||
| 		} | ||||
| 	} | ||||
| 	if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */ | ||||
| 		FILE *refo = fopen(REF_FILE, "a"); | ||||
| 		if (refo) { | ||||
| 			fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, func, tag); | ||||
| 			fclose(refo); | ||||
| 	if (ref_log) { | ||||
| 		if (obj->priv_data.ref_counter + delta == 0) { | ||||
| 			fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**destructor**,%s\n", user_data, delta, ast_get_tid(), file, line, func, tag); | ||||
| 			fflush(ref_log); | ||||
| 		} else if (delta != 0) { | ||||
| 			fprintf(ref_log, "%p,%s%d,%d,%s,%d,%s,%d,%s\n", user_data, (delta < 0 ? "" : "+"), | ||||
| 				delta, ast_get_tid(), file, line, func, obj ? obj->priv_data.ref_counter : -1, tag); | ||||
| 			fflush(ref_log); | ||||
| 		} | ||||
| 	} | ||||
| 	return internal_ao2_ref(user_data, delta, file, line, func); | ||||
| @@ -626,15 +623,14 @@ void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsig | ||||
| { | ||||
| 	/* allocation */ | ||||
| 	void *obj; | ||||
| 	FILE *refo; | ||||
|  | ||||
| 	if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, func)) == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (ref_debug && (refo = fopen(REF_FILE, "a"))) { | ||||
| 		fprintf(refo, "%p =1   %s:%d:%s (%s)\n", obj, file, line, func, tag); | ||||
| 		fclose(refo); | ||||
| 	if (ref_log) { | ||||
| 		fprintf(ref_log, "%p,+1,%d,%s,%d,%s,**constructor**,%s\n", obj, ast_get_tid(), file, line, func, tag); | ||||
| 		fflush(ref_log); | ||||
| 	} | ||||
|  | ||||
| 	/* return a pointer to the user data */ | ||||
| @@ -5582,19 +5578,29 @@ static struct ast_cli_entry cli_astobj2[] = { | ||||
| }; | ||||
| #endif	/* defined(AO2_DEBUG) || defined(AST_DEVMODE) */ | ||||
|  | ||||
| #if defined(AO2_DEBUG) || defined(AST_DEVMODE) | ||||
| static void astobj2_cleanup(void) | ||||
| { | ||||
|  | ||||
| #if defined(AST_DEVMODE) | ||||
| 	ao2_t_ref(reg_containers, -1, "Releasing container registration container"); | ||||
| 	reg_containers = NULL; | ||||
| #endif | ||||
| #if defined(AO2_DEBUG) || defined(AST_DEVMODE) | ||||
| 	ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2)); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef REF_DEBUG | ||||
| 	fclose(ref_log); | ||||
| 	ref_log = NULL; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int astobj2_init(void) | ||||
| { | ||||
| #ifdef REF_DEBUG | ||||
| 	char ref_filename[1024]; | ||||
| #endif | ||||
|  | ||||
| #if defined(AST_DEVMODE) | ||||
| 	reg_containers = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK, | ||||
| 		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_reg_sort_cb, NULL, | ||||
| @@ -5603,10 +5609,20 @@ int astobj2_init(void) | ||||
| 		return -1; | ||||
| 	} | ||||
| #endif	/* defined(AST_DEVMODE) */ | ||||
| #if defined(AO2_DEBUG) || defined(AST_DEVMODE) | ||||
|  | ||||
| #ifdef REF_DEBUG | ||||
| 	snprintf(ref_filename, sizeof(ref_filename), "%s/refs", ast_config_AST_LOG_DIR); | ||||
| 	ref_log = fopen(ref_filename, "w"); | ||||
| 	if (!ref_log) { | ||||
| 		ast_log(LOG_ERROR, "Could not open ref debug log file: %s\n", ref_filename); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| #if defined(AST_DEVMODE) || defined(AO2_DEBUG) | ||||
| 	ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2)); | ||||
| #endif	/* defined(AO2_DEBUG) */ | ||||
|  | ||||
| 	ast_register_atexit(astobj2_cleanup); | ||||
| #endif	/* defined(AO2_DEBUG) || defined(AST_DEVMODE) */ | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -1,317 +0,0 @@ | ||||
| /* | ||||
|  * Asterisk -- An open source telephony toolkit. | ||||
|  * | ||||
|  * Copyright (C) 2008, Steve Murphy | ||||
|  * | ||||
|  * Steve Murphy <murf@digium.com> | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /*! \file | ||||
|  * | ||||
|  * \brief A program to read in the /tmp/refs file generated | ||||
|  *         by astobj2 code when the REF_DEBUG macro is defined. | ||||
|  *         It will read in the file line by line, and | ||||
|  *         sort the data out by object, and check to see | ||||
|  *         if the refcounts balance to zero, and the object | ||||
|  *         was destroyed just once. Any problems that are  | ||||
|  *         found are reported to stdout and the objects | ||||
|  *         ref count history is printed out. If all is well, | ||||
|  *         this program reads in the /tmp/refs file and  | ||||
|  *         generates no output. No news is good news. | ||||
|  *  The contents of the /tmp/refs file looks like this: | ||||
|  * \verbatim | ||||
|  * 0x84fd718 -1   astobj2.c:926:cd_cb_debug (deref object via container destroy) [@1] | ||||
|  * 0x84fd718 =1   chan_sip.c:19760:build_user (allocate a user struct) | ||||
|  * 0x84fd718 +1   chan_sip.c:21558:reload_config (link user into users table) [@1] | ||||
|  * 0x84fd718 -1   chan_sip.c:2376:unref_user (Unref the result of build_user. Now, the table link is the only one left.) [@2] | ||||
|  * 0x84fd718 **call destructor** astobj2.c:926:cd_cb_debug (deref object via container destroy) | ||||
|  * \endverbatim | ||||
|  * | ||||
|  * \author Steve Murphy <murf@digium.com> | ||||
|  */ | ||||
|  | ||||
| /*** MODULEINFO | ||||
| 	<support_level>extended</support_level> | ||||
|  ***/ | ||||
|  | ||||
| #include "asterisk.h" | ||||
| ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <sys/stat.h> | ||||
| #include <signal.h> | ||||
| #include <errno.h> | ||||
| #include "asterisk/lock.h" | ||||
| #include "asterisk/hashtab.h" | ||||
| #include "asterisk/channel.h" | ||||
| #include "asterisk/utils.h" | ||||
| #include "asterisk/module.h" | ||||
|  | ||||
| struct rc_hist | ||||
| { | ||||
| 	char *desc; | ||||
| 	struct rc_hist *next; | ||||
| }; | ||||
|  | ||||
| struct rc_obj /* short for refcounted object */ | ||||
| { | ||||
| 	unsigned int addr; | ||||
| 	unsigned int count;  /* this plus addr makes each entry unique, starts at 1 */ | ||||
| 	int last_count; /* count 1 objects will record how many other objects had the same addr */ | ||||
| 	int destroy_count; | ||||
| 	int total_refcount; | ||||
| 	struct rc_hist *hist; | ||||
| 	struct rc_hist *last; | ||||
| }; | ||||
|  | ||||
| void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used); | ||||
| void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used) | ||||
| { | ||||
| } | ||||
|  | ||||
| static unsigned int hashtab_hash_rc(const void *obj) | ||||
| { | ||||
| 	const struct rc_obj *rc = obj; | ||||
| 	return rc->addr + rc->count; /* it's addr will make a FINE hash */ | ||||
| } | ||||
|  | ||||
| static int hashtab_compare_rc(const void *a, const void *b) | ||||
| { | ||||
| 	const struct rc_obj *rca = a; | ||||
| 	const struct rc_obj *rcb = b; | ||||
| 	if (rca->addr == rcb->addr && rca->count == rcb->count) | ||||
| 		return 0; | ||||
| 	else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
|  | ||||
| static struct rc_obj *alloc_obj(unsigned int addr, unsigned int count) | ||||
| { | ||||
| 	struct rc_obj *x = calloc(1,sizeof(struct rc_obj)); | ||||
| 	x->addr = addr; | ||||
| 	x->count = count; | ||||
| 	x->last_count = 1; | ||||
| 	x->total_refcount = 1; | ||||
| 	return x; | ||||
| } | ||||
|  | ||||
| static void add_to_hist(char *buffer, struct rc_obj *obj) | ||||
| { | ||||
| 	struct rc_hist *y = calloc(1,sizeof(struct rc_hist)); | ||||
| 	y->desc = strdup(buffer); | ||||
| 	if (obj->last) { | ||||
| 		obj->last->next = y; | ||||
| 		obj->last = y; | ||||
| 	} else { | ||||
| 		obj->hist = obj->last = y; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| int main(int argc,char **argv) | ||||
| { | ||||
| 	char linebuffer[300]; | ||||
| 	FILE *ifile = fopen("/tmp/refs", "r"); | ||||
| 	char *t; | ||||
| 	unsigned int un; | ||||
| 	struct rc_obj *curr_obj, *count1_obj; | ||||
| 	struct rc_obj lookup; | ||||
| 	struct ast_hashtab_iter *it; | ||||
| 	struct ast_hashtab *objhash; | ||||
| 	 | ||||
| 	if (!ifile) { | ||||
| 		printf("Sorry, Cannot open /tmp/refs!\n"); | ||||
| 		exit(10); | ||||
| 	} | ||||
| 	 | ||||
| 	objhash = ast_hashtab_create(9000, hashtab_compare_rc, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_rc, 1); | ||||
| 	 | ||||
| 	while (fgets(linebuffer, sizeof(linebuffer), ifile)) { | ||||
| 		/* collect data about the entry */ | ||||
| 		un = strtoul(linebuffer, &t, 16); | ||||
| 		lookup.addr = un; | ||||
| 		lookup.count = 1; | ||||
|  | ||||
| 		count1_obj = ast_hashtab_lookup(objhash, &lookup); | ||||
| 		 | ||||
| 		if (count1_obj) { | ||||
| 			/* there IS a count1 obj, so let's see which one we REALLY want */ | ||||
| 			if (*(t+1) == '=') { | ||||
| 				/* start a new object! */ | ||||
| 				curr_obj = alloc_obj(un, ++count1_obj->last_count); | ||||
| 				/* put it in the hashtable */ | ||||
| 				ast_hashtab_insert_safe(objhash, curr_obj); | ||||
| 			} else { | ||||
| 				if (count1_obj->last_count > 1) { | ||||
| 					lookup.count = count1_obj->last_count; | ||||
| 					curr_obj = ast_hashtab_lookup(objhash, &lookup); | ||||
| 				} else { | ||||
| 					curr_obj = count1_obj; | ||||
| 				} | ||||
| 				 | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
| 			/* NO obj at ALL? -- better make one! */ | ||||
| 			if (*(t+1) != '=') { | ||||
| 				printf("BAD: object %x appears without previous allocation marker!\n", un); | ||||
| 			} | ||||
| 			curr_obj = count1_obj = alloc_obj(un, 1); | ||||
| 			/* put it in the hashtable */ | ||||
| 			ast_hashtab_insert_safe(objhash, curr_obj); | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		if (*(t+1) == '+' || *(t+1) == '-' ) { | ||||
| 			curr_obj->total_refcount += strtol(t+1, NULL, 10); | ||||
| 		} else if (*(t+1) == '*') { | ||||
| 			curr_obj->destroy_count++; | ||||
| 		} | ||||
| 		 | ||||
| 		add_to_hist(linebuffer, curr_obj); | ||||
| 	} | ||||
| 	fclose(ifile); | ||||
| 	 | ||||
| 	/* traverse the objects and check for problems */ | ||||
| 	it = ast_hashtab_start_traversal(objhash); | ||||
| 	while ((curr_obj = ast_hashtab_next(it))) { | ||||
| 		if (curr_obj->total_refcount != 0 || curr_obj->destroy_count != 1) { | ||||
| 			struct rc_hist *h; | ||||
| 			if (curr_obj->total_refcount != 0) | ||||
| 				printf("Problem: net Refcount not zero for object %x\n", curr_obj->addr); | ||||
| 			if (curr_obj->destroy_count > 1 ) | ||||
| 				printf("Problem: Object %x destroyed more than once!\n", curr_obj->addr); | ||||
| 			printf("Object %x history:\n", curr_obj->addr); | ||||
| 			for(h=curr_obj->hist;h;h=h->next) { | ||||
| 				printf("   %s", h->desc); | ||||
| 			} | ||||
| 			printf("==============\n"); | ||||
| 		} | ||||
| 	} | ||||
| 	ast_hashtab_end_traversal(it); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* stub routines to satisfy linking with asterisk subcomponents */ | ||||
|  | ||||
| #ifndef LOW_MEMORY | ||||
| int  ast_add_profile(const char *x, uint64_t scale) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int ast_loader_register(int (*updater)(void)) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int ast_loader_unregister(int (*updater)(void)) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| void ast_module_register(const struct ast_module_info *x) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ast_module_unregister(const struct ast_module_info *x) | ||||
| { | ||||
| } | ||||
|  | ||||
| #ifndef LOW_MEMORY | ||||
| void ast_register_file_version(const char *file, const char *version) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ast_unregister_file_version(const char *file) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| #undef ast_mark | ||||
|  | ||||
| int64_t ast_mark(int x, int start1_stop0) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) | ||||
| { | ||||
| 	va_list vars; | ||||
| 	va_start(vars,fmt); | ||||
| 	printf("LOG: lev:%d file:%s  line:%d func: %s  ", | ||||
| 		   level, file, line, function); | ||||
| 	vprintf(fmt, vars); | ||||
| 	fflush(stdout); | ||||
| 	va_end(vars); | ||||
| } | ||||
|  | ||||
| void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...) | ||||
| { | ||||
|         va_list vars; | ||||
|         va_start(vars,fmt); | ||||
|  | ||||
|         printf("VERBOSE: "); | ||||
|         vprintf(fmt, vars); | ||||
|         fflush(stdout); | ||||
|         va_end(vars); | ||||
| } | ||||
|  | ||||
| void ast_register_thread(char *name) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void ast_unregister_thread(void *id) | ||||
| { | ||||
| } | ||||
| #ifdef HAVE_BKTR | ||||
| struct ast_bt *__ast_bt_create(void); | ||||
| struct ast_bt *__ast_bt_create(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| int __ast_bt_get_addresses(struct ast_bt *bt); | ||||
| int __ast_bt_get_addresses(struct ast_bt *bt) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| char **__ast_bt_get_symbols(void **addresses, size_t num_frames); | ||||
| char **__ast_bt_get_symbols(void **addresses, size_t num_frames) | ||||
| { | ||||
| 	char **foo = calloc(num_frames, sizeof(char *) + 1); | ||||
| 	if (foo) { | ||||
| 		int i; | ||||
| 		for (i = 0; i < num_frames; i++) { | ||||
| 			foo[i] = (char *) foo + sizeof(char *) * num_frames; | ||||
| 		} | ||||
| 	} | ||||
| 	return foo; | ||||
| } | ||||
|  | ||||
| void *__ast_bt_destroy(struct ast_bt *bt); | ||||
| void *__ast_bt_destroy(struct ast_bt *bt) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| void ast_log_backtrace(void) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| @@ -36,10 +36,6 @@ | ||||
| 	<defaultenabled>no</defaultenabled> | ||||
| 	<support_level>extended</support_level> | ||||
|   </member> | ||||
|   <member name="refcounter"> | ||||
| 	<defaultenabled>no</defaultenabled> | ||||
| 	<support_level>extended</support_level> | ||||
|   </member> | ||||
|   <member name="smsq"> | ||||
| 	<defaultenabled>no</defaultenabled> | ||||
| 	<depend>popt</depend> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user