Viewing: util.py
#!/usr/bin/env python
# Copyright (c) 2017-2018 Cray Inc. All Rights Reserved.
from collections import namedtuple
from math import ceil
from crash import addr2sym, PAGESIZE
from pykdump.API import (Addr, exec_crash_command, getSizeOf, member_offset,
readmem, readU8, readULong, sys_info)
from crashlib.cl import (cl_err, cl_warn, cl_info, cl_trace)
BYTES_1K = 1024
BYTES_1M = BYTES_1K * 1024
BYTES_1G = BYTES_1M * 1024
BYTES_1T = BYTES_1G * 1024
def bytes2size(bytes):
'''Return a string representation of bytes, including order,
ie '15.0M' or '2.1G'.'''
suffix = ""
if bytes >= BYTES_1T:
suffix = "T"
size = BYTES_1T
elif bytes >= BYTES_1G:
suffix = "G"
size = BYTES_1G
elif bytes >= BYTES_1M:
suffix = "M"
size = BYTES_1M
elif bytes >= BYTES_1K:
suffix = "K"
size = BYTES_1K
else:
size = 1
full = bytes / size
rem = ((bytes % size) * 10) / size
return "%d.%d%s" % (full, rem, suffix)
def pages2size(npages):
'''Return a string representation of the number of bytes contained
in npages.'''
return bytes2size(npages * PAGESIZE)
def page_to_virt(page):
# run kmem -p to get the pys addr for the page
cmd = "kmem -p %#x" % page
kmemp = exec_crash_command(cmd)
paddr = kmemp.splitlines()[1].split()[1]
cl_trace("*>>> page_to_virt #### phys_addr = %s" % paddr)
# find vaddr from crash ptov command
res = exec_crash_command("ptov " + paddr)
vaddr = res.splitlines()[1].split()[0]
cl_trace("*>>> page_to_virt #### vaddr = %s" % vaddr)
return long(vaddr, 16)
def get_config(name):
cl_trace(">>> get_config: searching system config for %s" % name)
res = exec_crash_command("sys config")
for line in res.splitlines():
if not "=" in line:
continue
(key, value) = line.split("=", 1)
if key == name:
cl_trace(">>> get_config: %s has a value of '%s'" % (name, value))
return value
raise ValueError("Name %s not found in system config" % name)
def atoi(arg):
# See if the user specified the format.
try:
val = int(arg, 0)
except:
# Nope, be generous and try again as hex.
try:
val = int(arg, 16)
except:
# No luck. Return an error.
print("Invalid number: %s" % arg)
val = None
return val
def is_kernel_address(addr):
# The top 17 bits should all be ones.
val = (1 << 17) - 1
if (addr >> 47) != val:
return False
return True
def is_kernel_text_address(addr):
# The top 33 bits should all be ones.
val = (1 << 33) - 1
if (addr >> 31) != val:
return False
return True
def is_valid_address(addr):
if addr < 0x10000:
return False
if addr & 7:
return False
return True
def readString(addr):
res = readmem(addr, 64)
return res.split('\0')[0]
def symbol_name(addr):
if not is_kernel_text_address(addr):
return ""
(name, offset) = addr2sym(addr, True)
if name == None:
return ""
if offset != 0:
name += "+" + hex(offset)
return name
def read_bool(addr):
'''pykdump can't read bools on its own.'''
return bool(readU8(addr))
def read_bool_member(struct, member_name):
'''struct must be a pykdump object, member name is the string name
of the bool member in the struct.'''
struct_type = struct.PYT_symbol
return read_bool(Addr(struct) + member_offset(struct_type, member_name))
def read_bitmap(addr, num_bits):
'''Return an integer representation of the 'num_bits' sized bitmap
at 'addr'. Note Python has arbitrary precision ints so the return
value may be very large.'''
bits_per_long = 8 * getSizeOf('long')
num_longs = int(ceil(float(num_bits) / bits_per_long))
total = 0
for i in range(num_longs):
total |= (readULong(addr + i * getSizeOf('long'))
<< ((num_longs - i - 1) * bits_per_long))
# Mask off unused bits when num_bits not a multiple of bits/long.
mask = 2 ** num_bits - 1
return total & mask
def read_cpumask(cpumask_addr):
'''Return an integer representation of the cpumask bitmap.'''
return read_bitmap(cpumask_addr, sys_info.CPUS)
def read_cpumask_var_t(container_struct, member_name):
'''Return an integer representation of the cpumask_var_t bitmap.
'container_struct' is the struct object which has a cpumask_var_t
as a member. 'member_name' is the name of the cpumask_var_t field
within the container struct.
Pykdump crashes when trying to read a cpumask_var_t. This function
provides a workaround which does not read a cpumask_var_t directly.'''
container_type = container_struct.PYT_symbol
offset = member_offset(container_type, member_name)
cpumask_addr = Addr(container_struct) + offset
return read_cpumask(cpumask_addr)
# Bit offsets and masks for read_qspinlock
# Copied from linux/include/asm-generic/qspinlock_types.h.
#
# Bitfields in the atomic value:
#
# When NR_CPUS < 16K
# 0- 7: locked byte
# 8: pending
# 9-15: not used
# 16-17: tail index
# 18-31: tail cpu (+1)
#
# When NR_CPUS >= 16K
# 0- 7: locked byte
# 8: pending
# 9-10: tail index
# 11-31: tail cpu (+1)'''
if sys_info.CPUS < 2 ** 14:
_q_pending_bits = 8
else:
_q_pending_bits = 1
_q_tail_index_offset = 9
_q_tail_index_bits = 2
_q_tail_index_mask = (2 ** _q_tail_index_bits - 1) << _q_tail_index_offset
_q_tail_cpu_offset = _q_tail_index_offset + _q_tail_index_bits
_q_tail_cpu_bits = 32 - _q_tail_cpu_offset
_q_tail_cpu_mask = (2 ** _q_tail_cpu_bits - 1) << _q_tail_cpu_offset
qspinlock_tuple = namedtuple('qspinlock',
['locked', 'pending', 'tail_index', 'tail_cpu'])
def read_qspinlock(qspinlock):
'''Given a struct qspinlock, which consists of a single 32 bit atomic
value, return a namedtuple of ints (locked, pending, tail_index, tail_cpu),
representing the bit fields of the qspinlock.'''
val = qspinlock.val.counter
locked_byte = val & 0xff
pending = (val & 0x100) >> 8
tail_index = (val & _q_tail_index_mask) >> _q_tail_index_offset
_q_tail_cpu_offset = _q_tail_index_offset + _q_tail_index_bits
_q_tail_cpu_bits = 32 - _q_tail_cpu_offset
_q_tail_cpu_mask = (2 ** _q_tail_cpu_bits - 1) << _q_tail_cpu_offset
tail_cpu = ((val & _q_tail_cpu_mask) >> _q_tail_cpu_offset) - 1
return qspinlock_tuple(locked=locked_byte, pending=pending,
tail_index=tail_index, tail_cpu=tail_cpu)