tests/krb5/raw_testcase.py: Allow prettyPrint of more MS-KILE-defined values
[Samba.git] / script / attr_count_read
blobeecdf57707b455de62b83af37497d67c1426766e
1 #!/usr/bin/env python3
3 # Copyright (C) Catalyst IT Ltd. 2019
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 import sys
20 import argparse
21 import struct
22 import os
23 from collections import OrderedDict, Counter
24 from pprint import pprint
26 sys.path.insert(0, "bin/python")
27 import tdb
30 def unpack_uint(filename, casefold=True):
31     db = tdb.Tdb(filename)
32     d = {}
33     for k in db:
34         v = struct.unpack("I", db[k])[0]
35         k2 = k.decode('utf-8')
36         if casefold:
37             k2 = k2.lower()
38         if k2 in d: # because casefold
39             d[k2] += v
40         else:
41             d[k2] = v
42     return d
45 def unpack_ssize_t_pair(filename, casefold):
46     db = tdb.Tdb(filename)
47     pairs = []
48     for k in db:
49         key = struct.unpack("nn", k)
50         v = struct.unpack("I", db[k])[0]
51         pairs.append((v, key))
53     pairs.sort(reverse=True)
54     #print(pairs)
55     return [(k, v) for (v, k) in pairs]
58 DATABASES = [
59     ('requested', "debug/attr_counts_requested.tdb", unpack_uint,
60      "The attribute was specifically requested."),
61     ('duplicates', "debug/attr_counts_duplicates.tdb", unpack_uint,
62      "Requested more than once in the same request."),
63     ('empty request', "debug/attr_counts_empty_req.tdb", unpack_uint,
64      "No attributes were requested, but these were returned"),
65     ('null request', "debug/attr_counts_null_req.tdb", unpack_uint,
66      "The attribute list was NULL and these were returned."),
67     ('found', "debug/attr_counts_found.tdb", unpack_uint,
68      "The attribute was specifically requested and it was found."),
69     ('not found', "debug/attr_counts_not_found.tdb", unpack_uint,
70      "The attribute was specifically requested but was not found."),
71     ('unwanted', "debug/attr_counts_unwanted.tdb", unpack_uint,
72      "The attribute was not requested and it was found."),
73     ('star match', "debug/attr_counts_star_match.tdb", unpack_uint,
74      'The attribute was not specifically requested but "*" was.'),
75     ('req vs found', "debug/attr_counts_req_vs_found.tdb", unpack_ssize_t_pair,
76      "How many attributes were requested versus how many were returned."),
80 def plot_pair_data(name, data, doc, lim=90):
81     # Note we keep the matplotlib import internal to this function for
82     # two reasons:
83     # 1. Some people won't have matplotlib, but might want to run the
84     #    script.
85     # 2. The import takes hundreds of milliseconds, which is a
86     #    nuisance if you don't wat graphs.
87     #
88     # This plot could be improved!
89     import matplotlib.pylab as plt
90     fig, ax = plt.subplots()
91     if lim:
92         data2 = []
93         for p, c in data:
94             if p[0] > lim or p[1] > lim:
95                 print("not plotting %s: %s" % (p, c))
96                 continue
97             data2.append((p, c))
98         skipped = len(data) - len(data2)
99         if skipped:
100             name += " (excluding %d out of range values)" % skipped
101             data = data2
102     xy, counts = zip(*data)
103     x, y = zip(*xy)
104     bins_x = max(x) + 4
105     bins_y = max(y)
106     ax.set_title(name)
107     ax.scatter(x, y, c=counts)
108     plt.show()
111 def print_pair_data(name, data, doc):
112     print(name)
113     print(doc)
114     t = "%14s | %14s | %14s"
115     print(t % ("requested", "returned", "count"))
116     print(t % (('-' * 14,) * 3))
118     for xy, count in data:
119         x, y = xy
120         if x == -2:
121             x = 'NULL'
122         elif x == -4:
123             x = '*'
124         print(t % (x, y, count))
127 def print_counts(count_data):
128     all_attrs = Counter()
129     for c in count_data:
130         all_attrs.update(c[1])
132     print("found %d attrs" % len(all_attrs))
133     longest = max(len(x) for x in all_attrs)
135     #pprint(all_attrs)
136     rows = OrderedDict()
137     for a, _ in all_attrs.most_common():
138         rows[a] = [a]
140     for col_name, counts, doc in count_data:
141         for attr, row in rows.items():
142             d = counts.get(attr, '')
143             row.append(d)
145         print("%15s: %s" % (col_name, doc))
146     print()
148     t = "%{}s".format(longest)
149     for c in count_data:
150         t += " | %{}s".format(max(len(c[0]), 7))
152     h = t % (("attribute",) + tuple(c[0] for c in count_data))
153     print(h)
154     print("-" * len(h))
156     for attr, row in rows.items():
157         print(t % tuple(row))
158         pass
161 def main():
162     parser = argparse.ArgumentParser()
163     parser.add_argument('LDB_PRIVATE_DIR',
164                         help="read attr counts in this directory")
165     parser.add_argument('--plot', action="store_true",
166                         help='attempt to draw graphs')
167     parser.add_argument('--no-casefold', action="store_false",
168                         default=True, dest="casefold",
169                         help='See all the encountered case varients')
170     args = parser.parse_args()
172     if not os.path.isdir(args.LDB_PRIVATE_DIR):
173         parser.print_usage()
174         sys.exit(1)
176     count_data = []
177     pair_data = []
178     for k, filename, unpacker, doc in DATABASES:
179         filename = os.path.join(args.LDB_PRIVATE_DIR, filename)
180         try:
181             d = unpacker(filename, casefold=args.casefold)
182         except (RuntimeError, IOError) as e:
183             print("could not parse %s: %s" % (filename, e))
184             continue
185         if unpacker is unpack_ssize_t_pair:
186             pair_data.append((k, d, doc))
187         else:
188             count_data.append((k, d, doc))
190     for k, v, doc in pair_data:
191         if args.plot:
192             plot_pair_data(k, v, doc)
193         print_pair_data(k, v, doc)
195     print()
196     print_counts(count_data)
198 main()