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
3#3#
4# Copyright (c) 2018, Intel Corporation.4# Copyright (c) 2018, Intel Corporation.
5 5
6from __future__ import division
7from __future__ import print_function
8
9import os6import os
10import sys7import sys
11import struct
12import re8import re
13import bisect9import bisect
14import collections10import collections
11from dataclasses import dataclass
12from typing import (Dict, Optional)
15 13
16sys.path.append(os.environ['PERF_EXEC_PATH'] + \14sys.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)
18class IomemEntry:
19 """Read from a line in /proc/iomem"""
20 begin: int
21 end: int
22 indent: int
23 label: str
18 24
19#physical address ranges for System RAM25# Physical memory layout from /proc/iomem. Key is the indent and then
20system_ram = []26# a list of ranges.
21#physical address ranges for Persistent Memory27iomem: Dict[int, list[IomemEntry]] = collections.defaultdict(list)
22pmem = []28# Child nodes from the iomem parent.
23#file object for proc iomem29children: Dict[IomemEntry, set[IomemEntry]] = collections.defaultdict(set)
24f = None30# Maximum indent seen before an entry in the iomem file.
25#Count for each type of memory31max_indent: int = 0
26load_mem_type_cnt = collections.Counter()32# Count for each range of memory.
27#perf event name33load_mem_type_cnt: Dict[IomemEntry, int] = collections.Counter()
28event_name = None34# Perf event name set from the first sample in the data.
35event_name: Optional[str] = None
29 36
30def parse_iomem():37def parse_iomem():
31 global f38 """Populate iomem from /proc/iomem file"""
32 f = open('/proc/iomem', 'r')39 global iomem
33 for i, j in enumerate(f):40 global max_indent
34 m = re.split('-|:',j,2)41 global children
35 if m[2].strip() == 'System RAM':42 with open('/proc/iomem', 'r', encoding='ascii') as f:
36 system_ram.append(int(m[0], 16))43 for line in f:
37 system_ram.append(int(m[1], 16))44 indent = 0
38 if m[2].strip() == 'Persistent Memory':45 while line[indent] == ' ':
39 pmem.append(int(m[0], 16))46 indent += 1
40 pmem.append(int(m[1], 16))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)
41 60
42def print_memory_type():61def find_memory_type(phys_addr) -> Optional[IomemEntry]:
43 print("Event: %s" % (event_name))62 """Search iomem for the range containing phys_addr with the maximum indent"""
44 print("%-40s %10s %10s\n" % ("Memory type", "count", "percentage"), end='')63 for i in range(max_indent, -1, -1):
45 print("%-40s %10s %10s\n" % ("----------------------------------------",64 if i not in iomem:
46 "-----------", "-----------"),65 continue
47 end='');66 position = bisect.bisect_right(iomem[i], phys_addr,
48 total = sum(load_mem_type_cnt.values())67 key=lambda entry: entry.begin)
49 for mem_type, count in sorted(load_mem_type_cnt.most_common(), \68 if position is None:
50 key = lambda kv: (kv[1], kv[0]), reverse = True):69 continue
51 print("%-40s %10d %10.1f%%\n" %70 iomem_entry = iomem[i][position-1]
52 (mem_type, count, 100 * count / total),71 if iomem_entry.begin <= phys_addr <= iomem_entry.end:
53 end='')72 return iomem_entry
73 print(f"Didn't find {phys_addr}")
74 return None
54 75
55def trace_begin():76def print_memory_type():
56 parse_iomem()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]
57 90
58def trace_end():91 def print_entries(entries):
59 print_memory_type()92 """Print counts from parents down to their children"""
60 f.close()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])
61 103
62def is_system_ram(phys_addr):104 print_entries(iomem[0])
63 #/proc/iomem is sorted
64 position = bisect.bisect(system_ram, phys_addr)
65 if position % 2 == 0:
66 return False
67 return True
68 105
69def is_persistent_mem(phys_addr):106def trace_begin():
70 position = bisect.bisect(pmem, phys_addr)107 parse_iomem()
71 if position % 2 == 0:
72 return False
73 return True
74 108
75def find_memory_type(phys_addr):109def trace_end():
76 if phys_addr == 0:110 print_memory_type()
77 return "N/A"
78 if is_system_ram(phys_addr):
79 return "System RAM"
80 111
81 if is_persistent_mem(phys_addr):112def process_event(param_dict):
82 return "Persistent Memory"113 if "sample" not in param_dict:
114 return
83 115
84 #slow path, search all116 sample = param_dict["sample"]
85 f.seek(0, 0)117 if "phys_addr" not in sample:
86 for j in f:118 return
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"
91 119
92def process_event(param_dict):120 phys_addr = sample["phys_addr"]
93 name = param_dict["ev_name"]121 entry = find_memory_type(phys_addr)
94 sample = param_dict["sample"]122 if entry:
95 phys_addr = sample["phys_addr"]123 load_mem_type_cnt[entry] += 1
96 124
97 global event_name125 global event_name
98 if event_name == None:126 if event_name is None:
99 event_name = name127 event_name = param_dict["ev_name"]
100 load_mem_type_cnt[find_memory_type(phys_addr)] += 1