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
changes/97/197/1
Matthew Jordan 11 years ago
parent af95d8c1d2
commit 4f30c7e91f

@ -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"

@ -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>

Loading…
Cancel
Save