diff options
Diffstat (limited to 'src/spdk/scripts/dpdk_mem_info.py')
-rwxr-xr-x | src/spdk/scripts/dpdk_mem_info.py | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/src/spdk/scripts/dpdk_mem_info.py b/src/spdk/scripts/dpdk_mem_info.py new file mode 100755 index 000000000..2039a5932 --- /dev/null +++ b/src/spdk/scripts/dpdk_mem_info.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 + +import argparse +import os +from enum import Enum + + +class memory: + def __init__(self, size): + self.size = size + self.heaps = [] + self.mempools = [] + self.memzones = [] + + def get_size(self): + return self.size + + def add_mempool(self, pool): + self.mempools.append(pool) + + def add_memzone(self, zone): + self.memzones.append(zone) + + def add_heap(self, heap): + self.heaps.append(heap) + + def get_total_heap_size(self): + size = 0 + for heap in self.heaps: + size = size + heap.size + return size + + def get_total_mempool_size(self): + size = 0 + for pool in self.mempools: + size = size + pool.get_memzone_size_sum() + return size + + def get_total_memzone_size(self): + size = 0 + for zone in self.memzones: + size = size + zone.size + return size + + def print_summary(self): + print("DPDK memory size {} in {} heap(s)" + .format(B_to_MiB(self.size), len(self.heaps))) + print("{} heaps totaling size {}".format(len(self.heaps), B_to_MiB(self.get_total_heap_size()))) + for x in sorted(self.heaps, key=lambda x: x.size, reverse=True): + x.print_summary(' ') + print("end heaps----------") + print("{} mempools totaling size {}".format(len(self.mempools), B_to_MiB(self.get_total_mempool_size()))) + for x in sorted(self.mempools, key=lambda x: x.get_memzone_size_sum(), reverse=True): + x.print_summary(' ') + print("end mempools-------") + print("{} memzones totaling size {}".format(len(self.memzones), B_to_MiB(self.get_total_memzone_size()))) + for x in sorted(self.memzones, key=lambda x: x.size, reverse=True): + x.print_summary(' ') + print("end memzones-------") + + def print_heap_summary(self, heap_id): + for heap in self.heaps: + if heap_id == heap.id: + heap.print_detailed_stats() + break + else: + print("heap id {} is invalid. please see the summary for valid heaps.\n".format(heap_id)) + + def print_mempool_summary(self, name): + for pool in self.mempools: + if name == pool.name: + pool.print_detailed_stats() + break + else: + print("mempool name {} is invalid. please see the summary for valid mempools.\n".format(name)) + + def print_memzone_summary(self, name): + for zone in self.memzones: + if name == zone.name: + zone.print_detailed_stats("") + break + else: + print("memzone name {} is invalid. please see the summary for valid memzone.\n".format(name)) + + def associate_heap_elements_and_memzones(self): + for zone in self.memzones: + for heap_obj in self.heaps: + for element in heap_obj.busy_malloc_elements: + if element.check_memzone_compatibility(zone): + heap_obj.busy_memzone_elements.append(element) + heap_obj.busy_malloc_elements.remove(element) + + def associate_memzones_and_mempools(self): + for pool in self.mempools: + for zone in self.memzones: + if pool.name in zone.name: + pool.add_memzone(zone) + + for pool in self.mempools: + for zone in pool.memzones: + if zone in self.memzones: + self.memzones.remove(zone) + + +class heap_elem_status(Enum): + FREE = 0 + BUSY = 1 + + +class heap_element: + def __init__(self, size, status, addr): + self.status = status + self.size = size + self.addr = addr + self.memzone = None + + def print_summary(self, header): + print("{}element at address: {} with size: {:>15}".format(header, hex(self.addr), B_to_MiB(self.size))) + + def check_memzone_compatibility(self, memzone): + ele_fini_addr = self.addr + self.size + memzone_fini_addr = memzone.address + memzone.size + if (self.addr <= memzone.address and ele_fini_addr >= memzone_fini_addr): + self.memzone = memzone + return True + return False + + +class heap: + def __init__(self, id, size, num_allocations): + self.id = id + self.size = size + self.num_allocations = num_allocations + self.free_elements = [] + self.busy_malloc_elements = [] + self.busy_memzone_elements = [] + + def add_element(self, element): + if element.status == heap_elem_status.FREE: + self.free_elements.append(element) + else: + self.busy_malloc_elements.append(element) + + def print_element_stats(self, list_to_print, list_type, header): + print("{}list of {} elements. size: {}".format(header, list_type, B_to_MiB(self.get_element_size(list_to_print)))) + for x in sorted(list_to_print, key=lambda x: x.size, reverse=True): + x.print_summary("{} ".format(header)) + if x.memzone is not None: + x.memzone.print_summary(" {}associated memzone info: ".format(header)) + + def get_element_size(self, list_to_check): + size = 0 + for element in list_to_check: + size = size + element.size + return size + + def print_summary(self, header): + print("{}size: {:>15} heap id: {}".format(header, B_to_MiB(self.size), self.id)) + + def print_detailed_stats(self): + print("heap id: {} total size: {} number of busy elements: {} number of free elements: {}" + .format(self.id, B_to_MiB(self.size), len(self.busy_malloc_elements), len(self.free_elements))) + self.print_element_stats(self.free_elements, "free", " ") + self.print_element_stats(self.busy_malloc_elements, "standard malloc", " ") + self.print_element_stats(self.busy_memzone_elements, "memzone associated", " ") + + +class mempool: + def __init__(self, name, num_objs, num_populated_objs, obj_size): + self.name = name + self.num_objs = num_objs + self.num_populated_objs = num_populated_objs + self.obj_size = obj_size + self.memzones = [] + + def add_memzone(self, memzone): + self.memzones.append(memzone) + + def get_memzone_size_sum(self): + size = 0 + for zone in self.memzones: + size = size + zone.size + return size + + def print_summary(self, header): + print("{}size: {:>15} name: {}" + .format(header, B_to_MiB(self.get_memzone_size_sum()), self.name)) + + def print_detailed_stats(self): + print("size: {:>15} name: {} comprised of {} memzone(s):" + .format(B_to_MiB(self.get_memzone_size_sum()), self.name, len(self.memzones))) + for x in sorted(self.memzones, key=lambda x: x.size, reverse=True): + x.print_detailed_stats(" ") + + +class memzone: + def __init__(self, name, size, address): + self.name = name + self.size = size + self.address = address + self.segments = [] + + def add_segment(self, segment): + self.segments.append(segment) + + def print_summary(self, header): + print("{}size: {:>15} name: {}".format(header, B_to_MiB(self.size), self.name)) + + def print_detailed_stats(self, header): + self.print_summary(header) + print("{}located at address {}".format(header, hex(self.address))) + print("{}spanning {} segment(s):".format(header, len(self.segments))) + for x in sorted(self.segments, key=lambda x: x.size, reverse=True): + x.print_summary(' ') + + +class segment: + def __init__(self, size, address): + self.size = size + self.address = address + + def print_summary(self, header): + print("{}address: {} length: {:>15}".format(header, hex(self.address), B_to_MiB(self.size))) + + +class parse_state(Enum): + PARSE_MEMORY_SIZE = 0 + PARSE_MEMZONES = 1 + PARSE_MEMZONE_SEGMENTS = 2 + PARSE_MEMPOOLS = 3 + PARSE_MEMPOOL_INFO = 4 + PARSE_HEAPS = 5 + PARSE_HEAP_ELEMENTS = 6 + + +def B_to_MiB(raw_value): + raw_value = raw_value / (1024.0 * 1024.0) + + return "%6f %s" % (raw_value, "MiB") + + +def parse_zone(line): + zone, info = line.split(':', 1) + name, length, addr, trash = info.split(',', 3) + + trash, name = name.split(':', 1) + name = name.replace("<", "") + name = name.replace(">", "") + trash, length = length.split(':', 1) + trash, addr = addr.split(':', 1) + + return memzone(name, int(length, 0), int(addr, 0)) + + +def parse_segment(line): + trash, addr, iova, length, pagesz = line.split(':') + addr, trash = addr.strip().split(' ') + length, trash = length.strip().split(' ') + + return segment(int(length, 0), int(addr, 0)) + + +def parse_mempool_name(line): + trash, info = line.split() + name, addr = line.split('@') + name = name.replace("<", "") + name = name.replace(">", "") + trash, name = name.split() + + return name + + +def parse_mem_stats(stat_path): + state = parse_state.PARSE_MEMORY_SIZE + with open(stat_path, "r") as stats: + + line = stats.readline() + while line != '': + if state == parse_state.PARSE_MEMORY_SIZE: + if "DPDK memory size" in line: + mem_size = int(line.replace("DPDK memory size ", "")) + memory_struct = memory(mem_size) + state = parse_state.PARSE_MEMZONES + line = stats.readline() + + if state == parse_state.PARSE_MEMZONES: + if line.find("Zone") == 0: + zone = parse_zone(line) + state = parse_state.PARSE_MEMZONE_SEGMENTS + line = stats.readline() + + if state == parse_state.PARSE_MEMZONE_SEGMENTS: + if line.find("Zone") == 0: + memory_struct.add_memzone(zone) + state = parse_state.PARSE_MEMZONES + continue + elif line.lstrip().find("addr:") == 0: + segment = parse_segment(line) + zone.add_segment(segment) + elif "DPDK mempools." in line: + state = parse_state.PARSE_MEMPOOLS + continue + line = stats.readline() + + if state == parse_state.PARSE_MEMPOOLS: + mempool_info = {} + if line.find("mempool") == 0: + mempool_info['name'] = parse_mempool_name(line) + state = parse_state.PARSE_MEMPOOL_INFO + line = stats.readline() + + if state == parse_state.PARSE_MEMPOOL_INFO: + if line.find("mempool") == 0: + try: + new_mempool = mempool(mempool_info['name'], int(mempool_info['size'], 0), + int(mempool_info['populated_size'], 0), int(mempool_info['total_obj_size'], 0)) + memory_struct.add_mempool(new_mempool) + except KeyError: + print("proper key values not provided for mempool.") + state = parse_state.PARSE_MEMPOOLS + continue + elif "cache" in line: + pass + elif "DPDK malloc stats." in line: + try: + new_mempool = mempool(mempool_info['name'], int(mempool_info['size'], 0), + int(mempool_info['populated_size'], 0), int(mempool_info['total_obj_size'], 0)) + memory_struct.add_mempool(new_mempool) + except KeyError: + print("proper key values not provided for mempool.") + while "DPDK malloc heaps." not in line: + line = stats.readline() + state = parse_state.PARSE_HEAPS + else: + try: + field, value = line.strip().split('=') + mempool_info[field] = value + except Exception as e: + pass + line = stats.readline() + + if state == parse_state.PARSE_HEAPS: + trash, heap_id = line.strip().split(':') + line = stats.readline() + trash, heap_size = line.split(':') + line = stats.readline() + trash, num_allocations = line.split(':') + if int(heap_size, 0) == 0: + pass + else: + new_heap = heap(heap_id.lstrip(), int(heap_size, 0), int(num_allocations, 0)) + memory_struct.add_heap(new_heap) + state = parse_state.PARSE_HEAP_ELEMENTS + + line = stats.readline() + + if state == parse_state.PARSE_HEAP_ELEMENTS: + if line.find("Heap id") == 0: + state = parse_state.PARSE_HEAPS + continue + elif line.find("Malloc element at") == 0: + trash, address, status = line.rsplit(maxsplit=2) + line = stats.readline() + trash, length, trash = line.split(maxsplit=2) + line = stats.readline() + if "FREE" in status: + element = heap_element(int(length, 0), heap_elem_status.FREE, int(address, 0)) + else: + element = heap_element(int(length, 0), heap_elem_status.BUSY, int(address, 0)) + new_heap.add_element(element) + line = stats.readline() + + memory_struct.associate_heap_elements_and_memzones() + memory_struct.associate_memzones_and_mempools() + return memory_struct + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Dumps memory stats for DPDK. If no arguments are provided, it dumps a general summary.') + parser.add_argument('-f', dest="stats_file", help='path to a dpdk memory stats file.', default='/tmp/spdk_mem_dump.txt') + parser.add_argument('-m', '--heap', dest="heap", help='Print detailed information about the given heap.', default=None) + parser.add_argument('-p', '--mempool', dest="mempool", help='Print detailed information about the given mempool.', default=None) + parser.add_argument('-z', '--memzone', dest="memzone", help='Print detailed information about the given memzone.', default=None) + + args = parser.parse_args() + + if not os.path.exists(args.stats_file): + print("Error, specified stats file does not exist. Please make sure you have run the" + "env_dpdk_get_mem_stats rpc on the spdk app you want to analyze.") + exit(1) + + mem_info = parse_mem_stats(args.stats_file) + + summary = True + if args.heap is not None: + mem_info.print_heap_summary(args.heap) + summary = False + if args.mempool is not None: + mem_info.print_mempool_summary(args.mempool) + summary = False + if args.memzone is not None: + mem_info.print_memzone_summary(args.memzone) + summary = False + + if summary: + mem_info.print_summary() |