1# mem-phys-addr.py: Resolve physical address samples 2# SPDX-License-Identifier: GPL-2.0 3# 4# Copyright (c) 2018, Intel Corporation. 5 6import os 7import sys 8import re 9import bisect 10import collections 11from dataclasses import dataclass 12from typing import (Dict, Optional) 13 14sys.path.append(os.environ['PERF_EXEC_PATH'] + \ 15 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') 16 17@dataclass(frozen=True) 18class IomemEntry: 19 """Read from a line in /proc/iomem""" 20 begin: int 21 end: int 22 indent: int 23 label: str 24 25# Physical memory layout from /proc/iomem. Key is the indent and then 26# a list of ranges. 27iomem: Dict[int, list[IomemEntry]] = collections.defaultdict(list) 28# Child nodes from the iomem parent. 29children: Dict[IomemEntry, set[IomemEntry]] = collections.defaultdict(set) 30# Maximum indent seen before an entry in the iomem file. 31max_indent: int = 0 32# Count for each range of memory. 33load_mem_type_cnt: Dict[IomemEntry, int] = collections.Counter() 34# Perf event name set from the first sample in the data. 35event_name: Optional[str] = None 36 37def parse_iomem(): 38 """Populate iomem from /proc/iomem file""" 39 global iomem 40 global max_indent 41 global children 42 with open('/proc/iomem', 'r', encoding='ascii') as f: 43 for line in f: 44 indent = 0 45 while line[indent] == ' ': 46 indent += 1 47 if indent > max_indent: 48 max_indent = indent 49 m = re.split('-|:', line, 2) 50 begin = int(m[0], 16) 51 end = int(m[1], 16) 52 label = m[2].strip() 53 entry = IomemEntry(begin, end, indent, label) 54 # Before adding entry, search for a parent node using its begin. 55 if indent > 0: 56 parent = find_memory_type(begin) 57 assert parent, f"Given indent expected a parent for {label}" 58 children[parent].add(entry) 59 iomem[indent].append(entry) 60 61def find_memory_type(phys_addr) -> Optional[IomemEntry]: 62 """Search iomem for the range containing phys_addr with the maximum indent""" 63 for i in range(max_indent, -1, -1): 64 if i not in iomem: 65 continue 66 position = bisect.bisect_right(iomem[i], phys_addr, 67 key=lambda entry: entry.begin) 68 if position is None: 69 continue 70 iomem_entry = iomem[i][position-1] 71 if iomem_entry.begin <= phys_addr <= iomem_entry.end: 72 return iomem_entry 73 print(f"Didn't find {phys_addr}") 74 return None 75 76def print_memory_type(): 77 print(f"Event: {event_name}") 78 print(f"{'Memory type':<40} {'count':>10} {'percentage':>10}") 79 print(f"{'-' * 40:<40} {'-' * 10:>10} {'-' * 10:>10}") 80 total = sum(load_mem_type_cnt.values()) 81 # Add count from children into the parent. 82 for i in range(max_indent, -1, -1): 83 if i not in iomem: 84 continue 85 for entry in iomem[i]: 86 global children 87 for child in children[entry]: 88 if load_mem_type_cnt[child] > 0: 89 load_mem_type_cnt[entry] += load_mem_type_cnt[child] 90 91 def print_entries(entries): 92 """Print counts from parents down to their children""" 93 global children 94 for entry in sorted(entries, 95 key = lambda entry: load_mem_type_cnt[entry], 96 reverse = True): 97 count = load_mem_type_cnt[entry] 98 if count > 0: 99 mem_type = ' ' * entry.indent + f"{entry.begin:x}-{entry.end:x} : {entry.label}" 100 percent = 100 * count / total 101 print(f"{mem_type:<40} {count:>10} {percent:>10.1f}") 102 print_entries(children[entry]) 103 104 print_entries(iomem[0]) 105 106def trace_begin(): 107 parse_iomem() 108 109def trace_end(): 110 print_memory_type() 111 112def process_event(param_dict): 113 if "sample" not in param_dict: 114 return 115 116 sample = param_dict["sample"] 117 if "phys_addr" not in sample: 118 return 119 120 phys_addr = sample["phys_addr"] 121 entry = find_memory_type(phys_addr) 122 if entry: 123 load_mem_type_cnt[entry] += 1 124 125 global event_name 126 if event_name is None: 127 event_name = param_dict["ev_name"] 128