Whamcloud - gitweb
LU-12461 contrib: Add epython scripts for crash dump analysis
[fs/lustre-release.git] / contrib / debug_tools / epython_scripts / crashlib / input / flagtools.py
1
2 """
3 Flag handling routines
4 Copyright 2015 Cray Inc.  All Rights Reserved
5 """
6
7
8 ### TBD: The "Simple" in the addSimple* interfaces refers to a flag
9 ### that's a single bit.  It's meant to distinguish from flags that
10 ### have multibit fields, such as the node/zone indices stuck in the
11 ### high end of struct page.flags; or a field that's mostly a pointer
12 ### but with some flags in the low bits.
13 #
14 ### To add cases like that will mean redoing most of the
15 ### implementation, but all the current interfaces should be ok, with
16 ### new interfaces added to let users define the non-simple flags.
17
18 import uflookup
19
20
21 class FlagSet:
22     """A collection of flags and values, with routines for translating
23
24     For decoding a flag int to a string, encoding a flag string to an
25     int, and providing python identifiers for testing by name, e.g.,
26
27     jafs = FlagSet() # job_attach flagset
28     jafs.addSimpleFlag("disable_affinity_apply")
29     if job_attach.flags & jafs.disable_affinity_apply: ...
30
31     The advantages over just using a dict include:
32     * Define the values once, and get value->string, string->value,
33       and python identifiers ns.<name> and ns.<name>_shift as above.
34     * The auto-incrementing _next_bit
35     """
36     def __init__(self, mapping=None):
37         """Create and initialize a FlagSet object
38
39         Arguments:
40             mapping:    if specified, provides a mapping object, e.g. dict,
41                         that supplies the initial key(name)/value pairs.
42         """
43         # Public dict of flag names to flag values (not the bit number)
44         self.str_to_value = {}
45         # Public dict of flag values to flag names
46         self.value_to_str = {}
47
48         self._next_bit = 0
49
50         # sorted_values is so that translating a value to a string
51         # will report the strings in the same order every time.  That
52         # order is by numerically increasing value.
53         self._sorted_values = []
54         self._sorted_strs = []
55
56         if mapping is not None:
57             self.addMap(mapping)
58
59     def addSimpleFlag(self, s, bit=None):
60         """Add a single-bit flag.
61
62         If bit is not specified, uses the bit one greater than the
63         previously defined bit.  If multiple flags are defined to use
64         the same bit, value_to_str will remember only the first."""
65
66         if s in self.str_to_value.keys():
67             raise ValueError("Flag {0} already defined (value {1:x})".format(
68                 s, self.str_to_value[s]))
69         if s + "_shift" in self.str_to_value.keys():
70             raise ValueError("Flag {0} conflicts with another "
71                              "flag ({1})".format(s, s + "_shift"))
72
73         try:
74             getattr(self, s)
75         except AttributeError:
76             pass
77         else:
78             raise ValueError("Value {0} already used by FlagSet object!".
79                              format(s))
80
81         try:
82             getattr(self, s + "_shift")
83         except AttributeError:
84             pass
85         else:
86             raise valueError("{0}_shift already used by FlagSet object!".
87                              format(s))
88
89
90         if bit is None:
91             bit = self._next_bit;
92         self._next_bit = bit + 1
93
94         value = 1 << bit
95         if value not in self.value_to_str:
96             self.value_to_str[value] = s
97         self.str_to_value[s] = value
98
99         self._sorted_values = []
100
101         setattr(self, s, value)
102         setattr(self, s+"_shift", bit)
103
104     def addSimpleFlags(self, *l):
105         """Adds a list of single-bit flags."""
106         map(self.addSimpleFlag, l)
107
108     def addMap(self, mapping):
109         """Add the key/value pairs from a mapping type"""
110         for k, v in mapping.items():
111             self.addSimpleFlag(k, v)
112
113     def _EnsureSorted(self):
114         if self._sorted_values:
115             return
116         self._sorted_values = sorted(self.value_to_str.keys())
117 #        self._sorted_strs = sorted(self.str_to_value.keys())
118
119
120     def flagsToStringList(self, flagint):
121         """Translate a given flag int to a list of flag strings."""
122         self._EnsureSorted()
123         strs = []
124         for v in self._sorted_values:
125             if flagint & v != 0:
126                 strs.append(self.value_to_str[v])
127                 flagint &= ~v
128         if flagint != 0:
129             strs.append("{0:#x}".format(flagint))
130         return strs
131
132     def UFLookup(self, key, **kwargs):
133         return uflookup.UFLookup(self.str_to_value, key, **kwargs)
134
135     # TBD: interface to enable a script --dump-flag-translations argument?
136
137
138
139 def join_flaglist(fl, sep = "|", empty = "0"):
140     """Helper function to join a list of flag strings."""
141     if fl:
142         return sep.join(fl)
143     else:
144         return empty
145
146
147 ### Tests
148
149 # I'm trying to follow the convention of
150
151 #   assertEquals(expectedvalue, function_under_test(args))
152
153 # I didn't discover that (on some unittest page) until I was halfway
154 # through, so I may not have gotten them all the right order.
155
156 if __name__ == '__main__':
157     import unittest
158
159     class Test_join_flaglist(unittest.TestCase):
160         """Test the join_flaglist function"""
161
162         def assertJoinFlaglistEqual(self, expectedstring, flaglist):
163             self.assertEqual(expectedstring, join_flaglist(flaglist))
164
165         def test_single_value(self):
166             """Test join_flaglist() with a single value"""
167             self.assertJoinFlaglistEqual("aflag", ["aflag"])
168
169         def test_two_values(self):
170             """Test join_flaglist() with two values"""
171             self.assertJoinFlaglistEqual("aflag|bflag",["aflag", "bflag"])
172
173         def test_three_values(self):
174             """Test join_flaglist() with three values"""
175             self.assertJoinFlaglistEqual("af|bf|cf", ["af", "bf", "cf"])
176
177         def test_comma_sep(self):
178             """Test join_flaglist() with a non-default sep"""
179             self.assertEqual("af,bf,cf",
180                              join_flaglist(["af", "bf", "cf"], sep=','))
181
182         def test_join_empty(self):
183             """Test join_flaglist() with an empty list"""
184             self.assertEqual("0", join_flaglist([]))
185
186         def test_join_empty_nondefault(self):
187             """Test join_flaglist() with a non-default value of empty"""
188             self.assertEqual(" ", join_flaglist([], empty=" "))
189
190
191     class Test_FlagSet(unittest.TestCase):
192         """Test the FlagSet class"""
193
194         def setUp(self):
195             self.fs = FlagSet()
196
197         def VerifyFlag(self, string, value):
198             """Test string->value and value->string"""
199             self.assertEqual(value, self.fs.str_to_value[string])
200             self.assertEqual(string, self.fs.value_to_str[value])
201             self.assertEqual(value, getattr(self.fs, string))
202             self.assertEqual(value, 1<<getattr(self.fs, string+"_shift"))
203
204     class Test_FlagSet_Constructor(Test_FlagSet):
205         def test_constructor(self):
206             """Too much?"""
207             self.assertEqual(self.fs._next_bit, 0)
208             self.assertFalse(self.fs.value_to_str)
209             # etc.
210
211     class Test_Add_Simple_Flag(Test_FlagSet):
212         def test_add_simple_flag(self):
213             """Test that adding a simple flag to an empty FlagSet works"""
214             self.fs.addSimpleFlag("FOO")
215             self.VerifyFlag("FOO", 1)
216
217         def test_3_add_simple_flag(self):
218             """Test multiple addSimpleFlag calls"""
219             self.fs.addSimpleFlag("FOO")
220             self.fs.addSimpleFlag("BAR")
221             self.fs.addSimpleFlag("BAZ")
222
223             self.VerifyFlag("FOO", 1)
224             self.VerifyFlag("BAR", 2)
225             self.VerifyFlag("BAZ", 4)
226
227             self.assertEqual(1, self.fs.FOO)
228             self.assertEqual(2, self.fs.BAR)
229             self.assertEqual(4, self.fs.BAZ)
230
231             self.assertEqual(0, self.fs.FOO_shift)
232             self.assertEqual(1, self.fs.BAR_shift)
233             self.assertEqual(2, self.fs.BAZ_shift)
234
235             self.fs._EnsureSorted()
236 #            self.assertEqual(self.fs._sorted_strs, ["BAR", "BAZ", "FOO"])
237             self.assertEqual(self.fs._sorted_values, [1, 2, 4])
238
239         def test_add_simple_flag_with_value(self):
240             """Test addSimpleFlag calls with explicit bit="""
241             self.fs.addSimpleFlag("FOO")
242             self.fs.addSimpleFlag("BAR", bit=1)
243             self.fs.addSimpleFlag("BAZ")
244             self.fs.addSimpleFlag("BLAT", bit=17)
245             self.fs.addSimpleFlag("FROB")
246             self.fs.addSimpleFlag("SNARF", bit=5)
247
248             self.VerifyFlag("FOO", 1)
249             self.VerifyFlag("BAR", 2)
250             self.VerifyFlag("BAZ", 4)
251             self.VerifyFlag("SNARF", 32)
252             self.VerifyFlag("BLAT", 1<<17)
253             self.VerifyFlag("FROB", 1<<18)
254
255             self.fs._EnsureSorted()
256 #            self.assertEqual(self.fs._sorted_strs,
257 #                             ["BAR", "BAZ", "BLAT", "FOO", "FROB"])
258             self.assertEqual(self.fs._sorted_values,
259                              [1, 2, 4, 32, 1<<17, 1<<18])
260
261
262         def test_add_simple_flag_dup_name(self):
263             """Test exception on duplicate flag name"""
264             self.fs.addSimpleFlag("FOO")
265             self.assertRaises(ValueError, self.fs.addSimpleFlag, "FOO")
266
267         def test_add_simple_flag_dup_value(self):
268             """Test exception on duplicate flag value"""
269             self.fs.addSimpleFlag("FOO")
270             self.fs.addSimpleFlag("BAR", bit=0)
271
272             self.VerifyFlag("FOO", 1)
273             self.assertEqual(1, self.fs.str_to_value["BAR"])
274
275         def test_add_shift_duplicated_name(self):
276             """Test that name and name_shift can't both be added"""
277             self.fs.addSimpleFlag("FOO_shift")
278             self.assertRaises(ValueError, self.fs.addSimpleFlag, "FOO")
279             self.assertRaises(ValueError,
280                               self.fs.addSimpleFlag, "FOO_shift_shift")
281
282         def test_attr_name_conflict(self):
283             """Test that adding a flag won't clobber an object attribute"""
284             self.assertRaises(ValueError,
285                               self.fs.addSimpleFlag, "addSimpleFlag")
286
287     class Test_Add_Simple_Flags(Test_FlagSet):
288         def test_add_simple_flags(self):
289             """Test that addSimpleFlags() can add several flags"""
290
291             self.fs.addSimpleFlags("FOO", "BAR", "BAZ")
292             self.VerifyFlag("FOO", 1)
293             self.VerifyFlag("BAR", 2)
294             self.VerifyFlag("BAZ", 4)
295
296     class Test_FlagSet_mapping(Test_FlagSet):
297         def setUp(self):
298             self.fs = FlagSet(mapping={"FOO": 9, "BAR": 1})
299
300         def test_constructor(self):
301             self.VerifyFlag("FOO", 1<<9)
302             self.VerifyFlag("BAR", 1<<1)
303
304         def test_addMap(self):
305             self.fs.addMap({"BAZ": 3, "ZING": 7})
306
307             self.VerifyFlag("FOO", 1<<9)
308             self.VerifyFlag("BAR", 1<<1)
309             self.VerifyFlag("BAZ", 1<<3)
310             self.VerifyFlag("ZING", 1<<7)
311
312     class Test_FlagSet_FBBZZ(Test_FlagSet):
313         """FlagSet with certain set of flags"""
314         def setUp(self):
315             self.fs = FlagSet()
316             self.fs.addSimpleFlags("FOO", "BAR", "BAZ")
317             self.fs.addSimpleFlag("ZING", bit=13)
318             self.fs.addSimpleFlag("ZOING", bit=42)
319
320         def Verify_F2SL(self, expectedstrlist, flags):
321             self.assertEqual(expectedstrlist, self.fs.flagsToStringList(flags))
322
323     class Test_FlagSet_FBBZZ_flagsToStringList(Test_FlagSet_FBBZZ):
324         def test_F(self):
325             self.Verify_F2SL(["FOO"], 1)
326         def test_B(self):
327             self.Verify_F2SL(["BAR"], 2)
328         def test_B2(self):
329             self.Verify_F2SL(["BAZ"], 4)
330         def test_Z(self):
331             self.Verify_F2SL(["ZING"], 1<<13)
332         def test_Z2(self):
333             self.Verify_F2SL(["ZOING"], 1<<42)
334
335         def test_FB(self):
336             self.Verify_F2SL(["FOO", "BAR"], 3)
337         def test_FBB(self):
338             self.Verify_F2SL(["FOO", "BAR", "BAZ"], 7)
339         def test_FB2(self):
340             self.Verify_F2SL(["BAR", "BAZ"], 6)
341
342         def test_FBBZZ(self):
343             self.Verify_F2SL(["FOO", "BAR", "BAZ", "ZING", "ZOING"],
344                              7|1<<13|1<<42)
345
346         def test_unknownflag(self):
347             self.Verify_F2SL(["0x10"], 0x10)
348         def test_unknownflags(self):
349             self.Verify_F2SL(["0x30"], 0x30)
350         def test_knownandunknownflags(self):
351             self.Verify_F2SL(["FOO", "0x30"], 0x31)
352
353
354     # Run all unit tests
355     unittest.main()