History

perf script python: Improve physical mem type resolution

Previously system RAM and persistent memory were hard code matched, change so that the label of the memory region is just read from /proc/iomem. This avoids frequent N/A samples. Change the /proc/iomem reading, event processing and output so that nested entries appear and their counts count toward their parent. As labels may be repeated, include the memory ranges in the output to make it clear why, for example, "System RAM" appears twice. Before: Event: mem_inst_retired.all_loads:P Memory type count percentage ---------------------------------------- ---------- ---------- System RAM 9460 96.5% N/A 998 3.5% After: Event: mem_inst_retired.all_loads:P Memory type count percentage ---------------------------------------- ---------- ---------- 100000000-105f7fffff : System RAM 36741 96.5 841400000-8416599ff : Kernel data 89 0.2 840800000-8412a6fff : Kernel rodata 60 0.2 841ebe000-8423fffff : Kernel bss 34 0.1 0-fff : Reserved 1345 3.5 100000-89dd9fff : System RAM 2 0.0 Before: Event: mem_inst_retired.any:P Memory type count percentage ---------------------------------------- ----------- ----------- System RAM 9460 90.5% N/A 998 9.5% After: Event: mem_inst_retired.any:P Memory type count percentage ---------------------------------------- ---------- ---------- 100000000-105f7fffff : System RAM 9460 90.5 841400000-8416599ff : Kernel data 45 0.4 840800000-8412a6fff : Kernel rodata 19 0.2 841ebe000-8423fffff : Kernel bss 12 0.1 0-fff : Reserved 998 9.5 The code has been updated to python 3 with type hints and resolving issues reported by mypy and pylint. Tabs are swapped to spaces as preferred in PEP8, because most lines of code were modified (of this small file) and this makes pylint significantly less noisy. Committer testing: root@number:/tmp# grep -m1 "model name" /proc/cpuinfo model name : Intel(R) Core(TM) i7-14700K root@number:/tmp# root@number:/tmp# perf script mem-phys-addr -a find / /bin /lib /lib64 /sbin Warning: 744 out of order events recorded. Event: cpu_core/mem_inst_retired.all_loads/P Memory type count percentage ---------------------------------------- ---------- ---------- 100000000-8bfbfffff : System RAM 364561 76.5 621400000-6223a6fff : Kernel rodata 10474 2.2 622400000-62283d4bf : Kernel data 4828 1.0 623304000-6237fffff : Kernel bss 1063 0.2 620000000-6213fffff : Kernel code 98 0.0 0-fff : Reserved 111480 23.4 100000-2b0ca017 : System RAM 337 0.1 2fbad000-30d92fff : System RAM 44 0.0 2c79d000-2fbabfff : System RAM 30 0.0 30d94000-316d5fff : System RAM 16 0.0 2b131a58-2c71dfff : System RAM 7 0.0 root@number:/tmp#

Ian Rogers committed 1 year ago

Commit d78e20c1 parent b2b95a21 file · +102 −75Browse files

Showing the changes to tools/perf/scripts/python (1 of 1 file). Show all

Unified · Split

tools/perf/scripts/python/mem-phys-addr.py+102−75
@@ -3,98 +3,125 @@ · 2 unmodified lines
33 #
44 # Copyright (c) 2018, Intel Corporation.
55
6−from __future__ import division
7−from __future__ import print_function
8−
96 import os
107 import sys
11−import struct
128 import re
139 import bisect
1410 import collections
11+from dataclasses import dataclass
12+from typing import (Dict, Optional)
1513
1614 sys.path.append(os.environ['PERF_EXEC_PATH'] + \
17− '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
15+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
16+
17+@dataclass(frozen=True)
18+class IomemEntry:
19+ """Read from a line in /proc/iomem"""
20+ begin: int
21+ end: int
22+ indent: int
23+ label: str
1824
19−#physical address ranges for System RAM
20−system_ram = []
21−#physical address ranges for Persistent Memory
22−pmem = []
23−#file object for proc iomem
24−f = None
25−#Count for each type of memory
26−load_mem_type_cnt = collections.Counter()
27−#perf event name
28−event_name = None
25+# Physical memory layout from /proc/iomem. Key is the indent and then
26+# a list of ranges.
27+iomem: Dict[int, list[IomemEntry]] = collections.defaultdict(list)
28+# Child nodes from the iomem parent.
29+children: Dict[IomemEntry, set[IomemEntry]] = collections.defaultdict(set)
30+# Maximum indent seen before an entry in the iomem file.
31+max_indent: int = 0
32+# Count for each range of memory.
33+load_mem_type_cnt: Dict[IomemEntry, int] = collections.Counter()
34+# Perf event name set from the first sample in the data.
35+event_name: Optional[str] = None
2936
3037 def parse_iomem():
31− global f
32− f = open('/proc/iomem', 'r')
33− for i, j in enumerate(f):
34− m = re.split('-|:',j,2)
35− if m[2].strip() == 'System RAM':
36− system_ram.append(int(m[0], 16))
37− system_ram.append(int(m[1], 16))
38− if m[2].strip() == 'Persistent Memory':
39− pmem.append(int(m[0], 16))
40− pmem.append(int(m[1], 16))
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)
4160
42−def print_memory_type():
43− print("Event: %s" % (event_name))
44− print("%-40s %10s %10s\n" % ("Memory type", "count", "percentage"), end='')
45− print("%-40s %10s %10s\n" % ("----------------------------------------",
46− "-----------", "-----------"),
47− end='');
48− total = sum(load_mem_type_cnt.values())
49− for mem_type, count in sorted(load_mem_type_cnt.most_common(), \
50− key = lambda kv: (kv[1], kv[0]), reverse = True):
51− print("%-40s %10d %10.1f%%\n" %
52− (mem_type, count, 100 * count / total),
53− end='')
61+def 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
5475
55−def trace_begin():
56− parse_iomem()
76+def 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]
5790
58−def trace_end():
59− print_memory_type()
60− f.close()
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])
61103
62−def is_system_ram(phys_addr):
63− #/proc/iomem is sorted
64− position = bisect.bisect(system_ram, phys_addr)
65− if position % 2 == 0:
66− return False
67− return True
104+ print_entries(iomem[0])
68105
69−def is_persistent_mem(phys_addr):
70− position = bisect.bisect(pmem, phys_addr)
71− if position % 2 == 0:
72− return False
73− return True
106+def trace_begin():
107+ parse_iomem()
74108
75−def find_memory_type(phys_addr):
76− if phys_addr == 0:
77− return "N/A"
78− if is_system_ram(phys_addr):
79− return "System RAM"
109+def trace_end():
110+ print_memory_type()
80111
81− if is_persistent_mem(phys_addr):
82− return "Persistent Memory"
112+def process_event(param_dict):
113+ if "sample" not in param_dict:
114+ return
83115
84− #slow path, search all
85− f.seek(0, 0)
86− for j in f:
87− m = re.split('-|:',j,2)
88− if int(m[0], 16) <= phys_addr <= int(m[1], 16):
89− return m[2]
90− return "N/A"
116+ sample = param_dict["sample"]
117+ if "phys_addr" not in sample:
118+ return
91119
92−def process_event(param_dict):
93− name = param_dict["ev_name"]
94− sample = param_dict["sample"]
95− phys_addr = sample["phys_addr"]
120+ phys_addr = sample["phys_addr"]
121+ entry = find_memory_type(phys_addr)
122+ if entry:
123+ load_mem_type_cnt[entry] += 1
96124
97− global event_name
98− if event_name == None:
99− event_name = name
100− load_mem_type_cnt[find_memory_type(phys_addr)] += 1
125+ global event_name
126+ if event_name is None:
127+ event_name = param_dict["ev_name"]