2"""Process a ref debug log
4 This file will process a log file created by enabling
5 the refdebug config option in asterisk.conf.
7 See http://www.asterisk.org for more information about
8 the Asterisk project. Please do not directly contact
9 any of the maintainers of this project for assistance;
10 the project provides a web site, mailing lists and IRC
11 channels for your use.
13 This program is free software, distributed under the terms of
14 the GNU General Public License Version 2. See the LICENSE file
15 at the top of the source tree.
17 Copyright (C) 2014, Digium, Inc.
18 Matt Jordan <mjordan@digium.com>
21from __future__
import print_function
25from optparse
import OptionParser
29 """Parse out a line into its constituent parts.
32 line The line from a ref debug log to parse out
35 A dictionary containing the options,
or None
37 tokens = line.strip().split(',', 7)
39 print(
"ERROR: ref debug line '%s' contains fewer tokens than "
40 "expected: %d" % (line.strip(),
len(tokens)))
43 processed_line = {
'addr': tokens[0],
45 'thread_id': tokens[2],
48 'function': tokens[5],
56 """The routine that kicks off processing a ref file
59 filename The full path to the file to process
63 - A list of objects whose lifetimes were completed
64 (i.e., finished objects)
65 - A list of objects referenced after destruction
66 (i.e., invalid objects)
67 - A list of objects whose lifetimes were not completed
68 (i.e., leaked objects)
69 - A list of objects whose lifetimes are skewed
70 (i.e., Object history starting
with an unusual ref count)
78 filename = options.filepath
80 with open(filename,
'r')
as ref_file:
87 obj = parsed_line[
'addr']
89 if obj
not in current_objects:
90 current_objects[obj] = {
'log': [],
'curcount': 1}
91 if 'constructor' in parsed_line[
'state']:
94 elif 'invalid' in parsed_line[
'state']:
96 current_objects[obj][
'curcount'] = 0
98 invalid_objects.append((obj, current_objects[obj]))
99 elif 'destructor' in parsed_line[
'state']:
100 current_objects[obj][
'curcount'] = 0
102 skewed_objects.append((obj, current_objects[obj]))
104 current_objects[obj][
'curcount'] = int(
105 parsed_line[
'state'])
107 skewed_objects.append((obj, current_objects[obj]))
109 current_objects[obj][
'curcount'] += int(parsed_line[
'delta'])
112 if 'constructor' in parsed_line[
'state']:
113 parsed_line[
'state'] =
'**constructor**'
114 elif 'destructor' in parsed_line[
'state']:
115 parsed_line[
'state'] =
'**destructor**'
117 current_objects[obj][
'log'].append(
118 "[%s] %s:%s %s: %s %s - [%s]" % (
119 parsed_line[
'thread_id'],
122 parsed_line[
'function'],
123 parsed_line[
'delta'],
125 parsed_line[
'state']))
131 if current_objects[obj][
'curcount'] <= 0:
132 if current_objects[obj][
'curcount'] < 0:
133 current_objects[obj][
'log'].append(
134 "[%s] %s:%s %s: %s %s - [%s]" % (
135 parsed_line[
'thread_id'],
138 parsed_line[
'function'],
140 "Object abnormally finalized",
141 "**implied destructor**"))
146 invalid_objects.append((obj, current_objects[obj]))
147 if not invalid
and options.normal:
148 finished_objects.append((obj, current_objects[obj]))
149 del current_objects[obj]
152 for (key, lines)
in current_objects.items():
153 leaked_objects.append((key, lines))
154 return (finished_objects, invalid_objects, leaked_objects, skewed_objects)
158 """Prints out the objects that were processed
161 objects A list of objects to print
162 prefix A prefix to
print that specifies something about
166 print("======== %s Objects ========" % prefix)
169 print(
"==== %s Object %s history ====" % (prefix, obj[0]))
170 for line
in obj[1][
'log']:
176 """Main entry point for the script"""
183 parser = OptionParser()
185 parser.add_option(
"-f",
"--file", action=
"store", type=
"string",
186 dest=
"filepath", default=
"/var/log/asterisk/refs",
187 help=
"The full path to the refs file to process")
188 parser.add_option(
"-i",
"--suppress-invalid", action=
"store_false",
189 dest=
"invalid", default=
True,
190 help=
"If specified, don't output invalid object "
192 parser.add_option(
"-l",
"--suppress-leaks", action=
"store_false",
193 dest=
"leaks", default=
True,
194 help=
"If specified, don't output leaked objects")
195 parser.add_option(
"-n",
"--suppress-normal", action=
"store_false",
196 dest=
"normal", default=
True,
197 help=
"If specified, don't output objects with a "
199 parser.add_option(
"-s",
"--suppress-skewed", action=
"store_false",
200 dest=
"skewed", default=
True,
201 help=
"If specified, don't output objects with a "
204 (options, args) = parser.parse_args(argv)
206 if not options.invalid
and not options.leaks
and not options.normal \
207 and not options.skewed:
208 print(
"All options disabled", file=sys.stderr)
211 if not os.path.isfile(options.filepath):
212 print(
"File not found: %s" % options.filepath, file=sys.stderr)
221 if options.invalid
and len(invalid_objects):
225 if options.leaks
and len(leaked_objects):
229 if options.skewed
and len(skewed_objects):
236 except (KeyboardInterrupt, SystemExit, IOError):
237 print(
"File processing cancelled", file=sys.stderr)
243if __name__ ==
"__main__":
244 sys.exit(
main(sys.argv))
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def process_file(options)
def print_objects(objects, prefix="")