There could be different types of memory in the system. E.g normal System Memory, Persistent Memory. To understand how the workload maps to those memories, it's important to know the I/O statistics of them. Perf can collect physical addresses, but those are raw data. It still needs extra work to resolve the physical addresses. Provide a script to facilitate the physical addresses resolving and I/O statistics. Profile with MEM_INST_RETIRED.ALL_LOADS or MEM_UOPS_RETIRED.ALL_LOADS event if any of them is available. Look up the /proc/iomem and resolve the physical address. Provide memory type summary. Here is an example output: # perf script report mem-phys-addr Event: mem_inst_retired.all_loads:P Memory type count percentage ---------------------------------------- ----------- ----------- System RAM 74 53.2% Persistent Memory 55 39.6% N/A --- Changes since V2: - Apply the new license rules. - Add comments for globals Changes since V1: - Do not mix DLA and Load Latency. Do not compare the loads and stores. Only profile the loads. - Use event name to replace the RAW event
Showing the changes to tools/perf/scripts/python (3 of 4 files). Show all
Unified · Split
| @@ -0,0 +1,19 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# | |
| 4 | +# Profiling physical memory by all retired load instructions/uops event | |
| 5 | +# MEM_INST_RETIRED.ALL_LOADS or MEM_UOPS_RETIRED.ALL_LOADS | |
| 6 | +# | |
| 7 | + | |
| 8 | +load=`perf list | grep mem_inst_retired.all_loads` | |
| 9 | +if [ -z "$load" ]; then | |
| 10 | + load=`perf list | grep mem_uops_retired.all_loads` | |
| 11 | +fi | |
| 12 | +if [ -z "$load" ]; then | |
| 13 | + echo "There is no event to count all retired load instructions/uops." | |
| 14 | + exit 1 | |
| 15 | +fi | |
| 16 | + | |
| 17 | +arg=$(echo $load | tr -d ' ') | |
| 18 | +arg="$arg:P" | |
| 19 | +perf record --phys-data -e $arg $@ | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# description: resolve physical address samples | |
| 3 | +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/mem-phys-addr.py | |
| @@ -0,0 +1,95 @@ | ||
| 1 | +# mem-phys-addr.py: Resolve physical address samples | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# | |
| 4 | +# Copyright (c) 2018, Intel Corporation. | |
| 5 | + | |
| 6 | +from __future__ import division | |
| 7 | +import os | |
| 8 | +import sys | |
| 9 | +import struct | |
| 10 | +import re | |
| 11 | +import bisect | |
| 12 | +import collections | |
| 13 | + | |
| 14 | +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | |
| 15 | + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | |
| 16 | + | |
| 17 | +#physical address ranges for System RAM | |
| 18 | +system_ram = [] | |
| 19 | +#physical address ranges for Persistent Memory | |
| 20 | +pmem = [] | |
| 21 | +#file object for proc iomem | |
| 22 | +f = None | |
| 23 | +#Count for each type of memory | |
| 24 | +load_mem_type_cnt = collections.Counter() | |
| 25 | +#perf event name | |
| 26 | +event_name = None | |
| 27 | + | |
| 28 | +def parse_iomem(): | |
| 29 | + global f | |
| 30 | + f = open('/proc/iomem', 'r') | |
| 31 | + for i, j in enumerate(f): | |
| 32 | + m = re.split('-|:',j,2) | |
| 33 | + if m[2].strip() == 'System RAM': | |
| 34 | + system_ram.append(long(m[0], 16)) | |
| 35 | + system_ram.append(long(m[1], 16)) | |
| 36 | + if m[2].strip() == 'Persistent Memory': | |
| 37 | + pmem.append(long(m[0], 16)) | |
| 38 | + pmem.append(long(m[1], 16)) | |
| 39 | + | |
| 40 | +def print_memory_type(): | |
| 41 | + print "Event: %s" % (event_name) | |
| 42 | + print "%-40s %10s %10s\n" % ("Memory type", "count", "percentage"), | |
| 43 | + print "%-40s %10s %10s\n" % ("----------------------------------------", \ | |
| 44 | + "-----------", "-----------"), | |
| 45 | + total = sum(load_mem_type_cnt.values()) | |
| 46 | + for mem_type, count in sorted(load_mem_type_cnt.most_common(), \ | |
| 47 | + key = lambda(k, v): (v, k), reverse = True): | |
| 48 | + print "%-40s %10d %10.1f%%\n" % (mem_type, count, 100 * count / total), | |
| 49 | + | |
| 50 | +def trace_begin(): | |
| 51 | + parse_iomem() | |
| 52 | + | |
| 53 | +def trace_end(): | |
| 54 | + print_memory_type() | |
| 55 | + f.close() | |
| 56 | + | |
| 57 | +def is_system_ram(phys_addr): | |
| 58 | + #/proc/iomem is sorted | |
| 59 | + position = bisect.bisect(system_ram, phys_addr) | |
| 60 | + if position % 2 == 0: | |
| 61 | + return False | |
| 62 | + return True | |
| 63 | + | |
| 64 | +def is_persistent_mem(phys_addr): | |
| 65 | + position = bisect.bisect(pmem, phys_addr) | |
| 66 | + if position % 2 == 0: | |
| 67 | + return False | |
| 68 | + return True | |
| 69 | + | |
| 70 | +def find_memory_type(phys_addr): | |
| 71 | + if phys_addr == 0: | |
| 72 | + return "N/A" | |
| 73 | + if is_system_ram(phys_addr): | |
| 74 | + return "System RAM" | |
| 75 | + | |
| 76 | + if is_persistent_mem(phys_addr): | |
| 77 | + return "Persistent Memory" | |
| 78 | + | |
| 79 | + #slow path, search all | |
| 80 | + f.seek(0, 0) | |
| 81 | + for j in f: | |
| 82 | + m = re.split('-|:',j,2) | |
| 83 | + if long(m[0], 16) <= phys_addr <= long(m[1], 16): | |
| 84 | + return m[2] | |
| 85 | + return "N/A" | |
| 86 | + | |
| 87 | +def process_event(param_dict): | |
| 88 | + name = param_dict["ev_name"] | |
| 89 | + sample = param_dict["sample"] | |
| 90 | + phys_addr = sample["phys_addr"] | |
| 91 | + | |
| 92 | + global event_name | |
| 93 | + if event_name == None: | |
| 94 | + event_name = name | |
| 95 | + load_mem_type_cnt[find_memory_type(phys_addr)] += 1 | |