Whamcloud - gitweb
LU-12461 contrib: Add epython scripts for crash dump analysis
[fs/lustre-release.git] / contrib / debug_tools / epython_scripts / crashlib / util.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017-2018 Cray Inc. All Rights Reserved.
4
5 from collections import namedtuple
6 from math import ceil
7
8 from crash import addr2sym, PAGESIZE
9 from pykdump.API import (Addr, exec_crash_command, getSizeOf, member_offset,
10                          readmem, readU8, readULong, sys_info)
11
12 from crashlib.cl import (cl_err, cl_warn, cl_info, cl_trace)
13
14 BYTES_1K = 1024
15 BYTES_1M = BYTES_1K * 1024
16 BYTES_1G = BYTES_1M * 1024
17 BYTES_1T = BYTES_1G * 1024
18
19 def bytes2size(bytes):
20     '''Return a string representation of bytes, including order,
21     ie '15.0M' or '2.1G'.'''
22     suffix = ""
23     if bytes >= BYTES_1T:
24         suffix = "T"
25         size = BYTES_1T
26     elif bytes >= BYTES_1G:
27         suffix = "G"
28         size = BYTES_1G
29     elif bytes >= BYTES_1M:
30         suffix = "M"
31         size = BYTES_1M
32     elif bytes >= BYTES_1K:
33         suffix = "K"
34         size = BYTES_1K
35     else:
36         size = 1
37     full = bytes / size
38     rem = ((bytes % size) * 10) / size
39     return "%d.%d%s" % (full, rem, suffix)
40
41 def pages2size(npages):
42     '''Return a string representation of the number of bytes contained
43     in npages.'''
44     return bytes2size(npages * PAGESIZE)
45
46 def page_to_virt(page):
47     # run kmem -p to get the pys addr for the page
48     cmd = "kmem -p %#x" % page
49     kmemp = exec_crash_command(cmd)
50     paddr = kmemp.splitlines()[1].split()[1]
51     cl_trace("*>>> page_to_virt #### phys_addr = %s" % paddr)
52     # find vaddr from crash ptov command
53     res = exec_crash_command("ptov " + paddr)
54     vaddr = res.splitlines()[1].split()[0]
55     cl_trace("*>>> page_to_virt #### vaddr = %s" % vaddr)
56     return long(vaddr, 16)
57
58 def get_config(name):
59     cl_trace(">>> get_config: searching system config for %s" % name)
60     res = exec_crash_command("sys config")
61     for line in res.splitlines():
62         if not "=" in line:
63             continue
64         (key, value) = line.split("=", 1)
65         if key == name:
66             cl_trace(">>> get_config: %s has a value of '%s'" % (name, value))
67             return value
68     raise ValueError("Name %s not found in system config" % name)
69
70 def atoi(arg):
71     # See if the user specified the format.
72     try:
73         val = int(arg, 0)
74     except:
75         # Nope, be generous and try again as hex.
76         try:
77             val = int(arg, 16)
78         except:
79             # No luck. Return an error.
80             print("Invalid number: %s" % arg)
81             val = None
82     return val
83
84 def is_kernel_address(addr):
85     # The top 17 bits should all be ones.
86     val = (1 << 17) - 1
87     if (addr >> 47) != val:
88         return False
89     return True
90
91 def is_kernel_text_address(addr):
92     # The top 33 bits should all be ones.
93     val = (1 << 33) - 1
94     if (addr >> 31) != val:
95         return False
96     return True
97
98 def is_valid_address(addr):
99     if addr < 0x10000:
100         return False
101     if addr & 7:
102         return False
103     return True
104
105 def readString(addr):
106     res = readmem(addr, 64)
107     return res.split('\0')[0]
108
109 def symbol_name(addr):
110     if not is_kernel_text_address(addr):
111         return ""
112     (name, offset) = addr2sym(addr, True)
113     if name == None:
114         return ""
115     if offset != 0:
116         name += "+" + hex(offset)
117     return name
118
119 def read_bool(addr):
120     '''pykdump can't read bools on its own.'''
121     return bool(readU8(addr))
122
123 def read_bool_member(struct, member_name):
124     '''struct must be a pykdump object, member name is the string name
125     of the bool member in the struct.'''
126     struct_type = struct.PYT_symbol
127     return read_bool(Addr(struct) + member_offset(struct_type, member_name))
128
129 def read_bitmap(addr, num_bits):
130     '''Return an integer representation of the 'num_bits' sized bitmap
131     at 'addr'. Note Python has arbitrary precision ints so the return
132     value may be very large.'''
133     bits_per_long = 8 * getSizeOf('long')
134     num_longs = int(ceil(float(num_bits) / bits_per_long))
135     total = 0
136     for i in range(num_longs):
137         total |= (readULong(addr + i * getSizeOf('long'))
138                  << ((num_longs - i - 1) * bits_per_long))
139     # Mask off unused bits when num_bits not a multiple of bits/long.
140     mask = 2 ** num_bits - 1
141     return total & mask
142
143 def read_cpumask(cpumask_addr):
144     '''Return an integer representation of the cpumask bitmap.'''
145     return read_bitmap(cpumask_addr, sys_info.CPUS)
146
147 def read_cpumask_var_t(container_struct, member_name):
148     '''Return an integer representation of the cpumask_var_t bitmap.
149     'container_struct' is the struct object which has a cpumask_var_t
150     as a member. 'member_name' is the name of the cpumask_var_t field
151     within the container struct.
152
153     Pykdump crashes when trying to read a cpumask_var_t. This function
154     provides a workaround which does not read a cpumask_var_t directly.'''
155     container_type = container_struct.PYT_symbol
156     offset = member_offset(container_type, member_name)
157     cpumask_addr = Addr(container_struct) + offset
158     return read_cpumask(cpumask_addr)
159
160
161 # Bit offsets and masks for read_qspinlock
162 # Copied from linux/include/asm-generic/qspinlock_types.h.
163 #
164 # Bitfields in the atomic value:
165 #
166 # When NR_CPUS < 16K
167 # 0- 7: locked byte
168 #    8: pending
169 # 9-15: not used
170 # 16-17: tail index
171 # 18-31: tail cpu (+1)
172 #
173 # When NR_CPUS >= 16K
174 # 0- 7: locked byte
175 #    8: pending
176 # 9-10: tail index
177 # 11-31: tail cpu (+1)'''
178
179 if sys_info.CPUS < 2 ** 14:
180     _q_pending_bits = 8
181 else:
182     _q_pending_bits = 1
183 _q_tail_index_offset = 9
184 _q_tail_index_bits = 2
185 _q_tail_index_mask = (2 ** _q_tail_index_bits - 1) << _q_tail_index_offset
186 _q_tail_cpu_offset = _q_tail_index_offset + _q_tail_index_bits
187 _q_tail_cpu_bits = 32 - _q_tail_cpu_offset
188 _q_tail_cpu_mask = (2 ** _q_tail_cpu_bits - 1) << _q_tail_cpu_offset
189
190 qspinlock_tuple = namedtuple('qspinlock',
191                              ['locked', 'pending', 'tail_index', 'tail_cpu'])
192
193 def read_qspinlock(qspinlock):
194     '''Given a struct qspinlock, which consists of a single 32 bit atomic
195     value, return a namedtuple of ints (locked, pending, tail_index, tail_cpu),
196     representing the bit fields of the qspinlock.'''
197
198     val = qspinlock.val.counter
199     locked_byte = val & 0xff
200     pending = (val & 0x100) >> 8
201
202     tail_index = (val & _q_tail_index_mask) >> _q_tail_index_offset
203
204     _q_tail_cpu_offset = _q_tail_index_offset + _q_tail_index_bits
205     _q_tail_cpu_bits = 32 - _q_tail_cpu_offset
206     _q_tail_cpu_mask = (2 ** _q_tail_cpu_bits - 1) << _q_tail_cpu_offset
207     tail_cpu = ((val & _q_tail_cpu_mask) >> _q_tail_cpu_offset) - 1
208
209     return qspinlock_tuple(locked=locked_byte, pending=pending,
210                            tail_index=tail_index, tail_cpu=tail_cpu)