Asterisk - The Open Source Telephony Project  18.5.0
refstats.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 """Process a ref debug log for memory usage
3 
4  This will provide information about total and peak
5  allocations.
6 
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.
12 
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.
16 
17  Copyright (C) 2018, CFWare, LLC
18  Corey Farrell <[email protected]>
19 """
20 
21 from __future__ import print_function
22 import sys
23 import os
24 
25 from optparse import OptionParser
26 
27 
29  """Create statistics object"""
30  return {
31  'count': 0,
32  'overhead': 0,
33  'user_data': 0,
34  'totalmem': 0
35  }
36 
37 
38 def update_stats(current, peak, total, key, direction, delta):
39  """Update statistics objects"""
40 
41  if direction == 1:
42  total[key] += delta
43 
44  delta *= direction
45  current[key] += delta
46  if current[key] > peak[key]:
47  peak[key] = current[key]
48 
49 
50 def process_file(options):
51  """The routine that kicks off processing a ref file"""
52 
53  current = create_stats()
54  total = create_stats()
55  peak = create_stats()
56  object_types = {}
57  objects = {}
58  filename = options.filepath
59 
60  with open(filename, 'r') as ref_file:
61  for line in ref_file:
62  if 'constructor' not in line and 'destructor' not in line:
63  continue
64  # The line format is:
65  # addr,delta,thread_id,file,line,function,state,tag
66  # Only addr, file, line, function, state are used by reflocks.py
67  tokens = line.strip().split(',', 7)
68  addr = tokens[0]
69  state = tokens[6]
70  if 'constructor' in state:
71  split_state = state.split("**")
72  if len(split_state) < 4:
73  print("File does not contain object size information", file=sys.stderr)
74  sys.exit(1)
75 
76  obj_type = '%s:%s:%s' % (tokens[3], tokens[4], tokens[5])
77  if obj_type not in object_types:
78  object_types[obj_type] = {
79  'used': 0,
80  'unused': 0,
81  'none': 0
82  }
83  overhead = int(split_state[2])
84  user_data = int(split_state[3])
85  obj = objects[addr] = {
86  'overhead': overhead,
87  'user_data': user_data,
88  'obj_type': obj_type
89  }
90 
91  direction = 1
92  else:
93  if addr not in objects:
94  # This error would be reported by refcounter.py.
95  continue
96  obj = objects[addr]
97  del objects[addr]
98  direction = -1
99  obj_type = obj['obj_type']
100  if '**lock-state:unused**' in state:
101  object_types[obj_type]['unused'] += 1
102  elif '**lock-state:used**' in state:
103  object_types[obj_type]['used'] += 1
104 
105  # Increment current and peak usage
106  update_stats(current, peak, total, 'count', direction, 1)
107  update_stats(current, peak, total, 'overhead', direction, obj['overhead'])
108  update_stats(current, peak, total, 'user_data', direction, obj['user_data'])
109  update_stats(current, peak, total, 'totalmem', direction, obj['overhead'] + obj['user_data'])
110 
111  print("Total usage statistics:")
112  print("%20s: %d" % ("Count", total['count']))
113  print("%20s: %d" % ("Total Memory (k)", total['totalmem'] / 1024))
114  print("%20s: %d (%.2f%%)" % ("Overhead (k)", total['overhead'] / 1024, total['overhead'] * 100.0 / total['totalmem']))
115  print("%20s: %d" % ("User Data (k)", total['user_data'] / 1024))
116  print("")
117  print("Peak usage statistics:")
118  print("%20s: %d" % ("Count", peak['count']))
119  print("%20s: %d" % ("Total Memory (k)", peak['totalmem'] / 1024))
120  print("%20s: %d (%.2f%%)" % ("Overhead (k)", peak['overhead'] / 1024, peak['overhead'] * 100.0 / peak['totalmem']))
121  print("%20s: %d" % ("User Data (k)", peak['user_data'] / 1024))
122  print("")
123 
124  lockbyobj = {'used': 0, 'total': 0}
125  lockbytype = {'used': 0, 'total': 0}
126  for (allocator, info) in object_types.items():
127  lockbyobj['used'] += info['used']
128  lockbyobj['total'] += info['used'] + info['unused']
129 
130  if info['used'] != 0:
131  lockbytype['used'] += 1
132  elif info['unused'] == 0:
133  # This object type doesn't have locking.
134  continue
135  lockbytype['total'] += 1
136 
137  print("Lock usage statistics:")
138  print("%20s: %d of %d used (%.2f%%)" % (
139  "By object",
140  lockbyobj['used'],
141  lockbyobj['total'],
142  lockbyobj['used'] * 100.0 / lockbyobj['total']))
143  print("%20s: %d of %d used (%.2f%%)" % (
144  "By type",
145  lockbytype['used'],
146  lockbytype['total'],
147  lockbytype['used'] * 100.0 / lockbytype['total']))
148 
149 
150 def main(argv=None):
151  """Main entry point for the script"""
152 
153  ret_code = 0
154 
155  if argv is None:
156  argv = sys.argv
157 
158  parser = OptionParser()
159 
160  parser.add_option("-f", "--file", action="store", type="string",
161  dest="filepath", default="/var/log/asterisk/refs",
162  help="The full path to the refs file to process")
163 
164  (options, args) = parser.parse_args(argv)
165 
166  if not os.path.isfile(options.filepath):
167  print("File not found: %s" % options.filepath, file=sys.stderr)
168  return -1
169 
170  try:
171  process_file(options)
172  except (KeyboardInterrupt, SystemExit, IOError):
173  print("File processing cancelled", file=sys.stderr)
174  return -1
175 
176  return ret_code
177 
178 
179 if __name__ == "__main__":
180  sys.exit(main(sys.argv))
def main(argv=None)
Definition: refstats.py:150
def update_stats(current, peak, total, key, direction, delta)
Definition: refstats.py:38
def create_stats()
Definition: refstats.py:28
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def process_file(options)
Definition: refstats.py:50