Skip to content

Commit 28062df

Browse files
author
Sachin P Bappalige
committed
Add DAWR Watchpoint for different data types
Signed-off-by: Sachin P Bappalige <sachinpb@linux.ibm.com>
2 parents ace0611 + a964779 commit 28062df

File tree

4 files changed

+293
-9
lines changed

4 files changed

+293
-9
lines changed

perf/perf_datatype_profiling.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/env python
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation; either version 2 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11+
#
12+
# See LICENSE for more details.
13+
#
14+
# Copyright: 2025 IBM
15+
# Author: Tejas Manhas <Tejas.Manhas1@ibm.com>
16+
import re
17+
from avocado import Test
18+
from avocado.utils import distro, process, genio
19+
from avocado.utils.software_manager.manager import SoftwareManager
20+
21+
22+
class datatype_profiling(Test):
23+
"""
24+
This is a test class for datatype profiling that checks datatype offset etc.
25+
"""
26+
27+
def setUp(self):
28+
'''
29+
Install the basic packages to support perf
30+
'''
31+
detected_distro = distro.detect()
32+
if 'ppc64' not in detected_distro.arch:
33+
self.cancel('This test is not supported on %s architecture'
34+
% detected_distro.arch)
35+
if 'PowerNV' in genio.read_file('/proc/cpuinfo'):
36+
self.cancel('This test is only supported on LPAR')
37+
process.run("dmesg -C")
38+
self.check_mem_event_availability()
39+
self.check_dependencies()
40+
41+
def run_cmd(self, cmd):
42+
"""
43+
run command on SUT as root
44+
"""
45+
46+
result = process.run(cmd, shell=True, ignore_status=True)
47+
output = result.stdout_text + result.stderr_text
48+
return output
49+
50+
def check_mem_event_availability(self):
51+
try:
52+
output = self.run_cmd(
53+
"perf mem record -e list")
54+
except Exception as e:
55+
self.log.info(f"Command failed: {e}")
56+
return False
57+
if not re.search(r"ldlat-loads\s*:\s*available", output):
58+
self.cancel("Required memory event 'ldlat-loads' not available")
59+
60+
if not re.search(r"ldlat-stores\s*:\s*available", output):
61+
self.cancel("Required memory event 'ldlat-stores' not available")
62+
63+
def check_dependencies(self):
64+
"""
65+
Check if required debug packages for current kernel are installed
66+
and perf events are available.
67+
"""
68+
69+
detected_distro = distro.detect()
70+
is_rhel = False
71+
is_sles = False
72+
if "rhel" in detected_distro.name.lower():
73+
is_rhel = True
74+
if "suse" in detected_distro.name.lower():
75+
is_sles = True
76+
# Define base package names (without versions)
77+
base_packages = ["perf"]
78+
79+
# Add debuginfo names based on distro
80+
if is_rhel:
81+
base_packages += [
82+
"kernel-tools-libs",
83+
"kernel-tools",
84+
"kernel-headers",
85+
"kernel-devel",
86+
"kernel",
87+
"kernel-core",
88+
"kernel-modules",
89+
"kernel-modules-extra",
90+
"kernel-modules-core",
91+
"kernel-debuginfo",
92+
"kernel-debug-debuginfo",
93+
"kernel-debuginfo-common-ppc64le"
94+
]
95+
elif is_sles:
96+
base_packages += [
97+
"kernel-default-debuginfo",
98+
"kernel-debug-debuginfo",
99+
"kernel-default-devel",
100+
"kernel-devel",
101+
"kernel-default"
102+
]
103+
else:
104+
self.cancel("Unsupported Linux distribution")
105+
106+
smm = SoftwareManager()
107+
for package in base_packages:
108+
if not smm.check_installed(package) and not smm.install(package):
109+
self.cancel('%s is needed for the test to be run' % package)
110+
111+
def check_perf_report_headers(self, cmd):
112+
"""
113+
Checks for expected headers in perf report output.
114+
"""
115+
output = self.run_cmd(cmd)
116+
expected_headers = ["Symbol", "Data Type", "Data Type Offset"]
117+
for header in expected_headers:
118+
if header not in output:
119+
self.fail(
120+
f"Missing expected header '{header}' in perf report output")
121+
122+
def check_perf_annotate_headers(self, cmd, check_insn_stat=False):
123+
"""
124+
Checks for expected headers in perf annotate output.
125+
"""
126+
self.run_cmd(f"{cmd} > out")
127+
output = self.run_cmd("cat out")
128+
129+
if check_insn_stat:
130+
# First check for Name/opcode, Good, Bad headers
131+
expected_insn_headers = ["Name/opcode", "Good", "Bad"]
132+
for header in expected_insn_headers:
133+
if header not in output:
134+
self.fail(
135+
f"Missing expected header '{header}' in perf annotate insn stat output")
136+
137+
# Now check for 'offset' and 'size' in each Annotate type block
138+
sections = output.split("Annotate type:")
139+
for section in sections[1:]:
140+
if "offset" not in section or "size" not in section:
141+
self.fail(
142+
"Missing 'offset' or 'size' header in perf annotate output")
143+
144+
def test_datatype_profiling(self):
145+
"""
146+
Test to verify perf data type profiling feature.
147+
Steps:
148+
1. Verify perf report headers.
149+
2. Verify perf annotate data type headers.
150+
3. Verify perf annotate instruction stats headers.
151+
Repeat above for:
152+
a. perf mem record -a sleep 10
153+
b. perf record -a -e mem-loads sleep 5
154+
c. perf record -c 1 -e mem-stores sleep 5
155+
"""
156+
157+
record_cmds = [
158+
"perf mem record -a -o /tmp/perf_output.data sleep 10",
159+
"perf record -a -e mem-loads -o /tmp/perf_output.data sleep 5",
160+
"perf record -c 1 -e mem-stores -o /tmp/perf_output.data sleep 5"
161+
]
162+
163+
for cmd in record_cmds:
164+
self.run_cmd(cmd)
165+
166+
self.check_perf_report_headers(
167+
"perf report -i /tmp/perf_output.data -s symbol,type,typeoff")
168+
self.check_perf_annotate_headers(
169+
"perf annotate -i /tmp/perf_output.data --data-type")
170+
self.check_perf_annotate_headers(
171+
"perf annotate -i /tmp/perf_output.data --data-type --insn-stat",
172+
check_insn_stat=True)
173+
174+
def tearDown(self):
175+
"""
176+
tear down function to clear dmesg and data files.
177+
"""
178+
process.run("dmesg -T")
179+
self.run_cmd("rm -rf /tmp/perf_output.data")
180+
self.run_cmd("rm -rf out")

trace/dawr.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,9 @@ def setUp(self):
5252
shutil.copyfile(self.get_data('dawr_v%d.c' % value),
5353
os.path.join(self.teststmpdir,
5454
'dawr_v%d.c' % value))
55-
for name in ['dawr_local.c', 'dawr_pointer.c', 'dawr_struct.c', 'dawr_array.c']:
56-
shutil.copyfile(self.get_data(name),
57-
os.path.join(self.teststmpdir, name))
58-
shutil.copyfile(self.get_data('Makefile'),
59-
os.path.join(self.teststmpdir, 'Makefile'))
55+
for fname in ['boundary_check.c', 'dawr_local.c', 'dawr_pointer.c', 'dawr_struct.c', 'dawr_array.c', 'Makefile']:
56+
shutil.copyfile(self.get_data(fname),
57+
os.path.join(self.teststmpdir, fname))
6058
build.make(self.teststmpdir)
6159
os.chdir(self.teststmpdir)
6260
self.output_file = "perf.data"
@@ -154,8 +152,9 @@ def test_read_dawr_v3_gdb(self):
154152
'watchpoint %s: %s'
155153
% (i, value)]))
156154
child.sendline('r')
157-
return_value.append(child.expect_exact([pexpect.TIMEOUT,
158-
'not enough available hardware']))
155+
return_value.append(
156+
child.expect_exact([pexpect.TIMEOUT,
157+
'not enough available hardware']))
159158
for i in return_value:
160159
if i == 0:
161160
self.fail('Test case failed for 3 variables')
@@ -198,6 +197,22 @@ def test_read_dawr_array_perf(self):
198197
self.output_file, data[0])
199198
self.perf_cmd(perf_record)
200199

200+
def test_dawr_boundary_check(self):
201+
"""
202+
Run dawr_boundary_check to check
203+
unaligned 512-byte DAWR boundary condition
204+
"""
205+
output = self.run_test('./boundary_check')
206+
data = output.stdout.decode("utf-8")
207+
208+
expected_msg = "TEST Boundary check PASSED: unaligned_512bytes"
209+
if expected_msg not in data:
210+
self.fail(
211+
f"TEST Boundary check FAILED: unaligned_512bytes.\n"
212+
f"Output was:\n{data}")
213+
else:
214+
self.log.info(expected_msg)
215+
201216
def tearDown(self):
202217
# Delete the temporary file
203218
if os.path.isfile("perf.data"):

trace/dawr.py.data/Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
all : dawr_v1 dawr_v2 dawr_v3
1+
all : dawr_v1 dawr_v2 dawr_v3 boundary_check
22
dawr_v1 : dawr_v1.c
33
gcc -g -o dawr_v1 dawr_v1.c
44
dawr_v2 : dawr_v2.c
55
gcc -g -o dawr_v2 dawr_v2.c
66
dawr_v3 : dawr_v3.c
77
gcc -g -o dawr_v3 dawr_v3.c
8+
boundary_check : boundary_check.c
9+
gcc -g -o boundary_check boundary_check.c
810
clean :
9-
rm dawr_v1 dawr_v2 dawr_v3
11+
rm dawr_v1 dawr_v2 dawr_v3 boundary_check
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* This program is free software; you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation; either version 2 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10+
*
11+
* See LICENSE for more details.
12+
*
13+
* Copyright: 2025 IBM
14+
* Author: SACHIN P B <sachinpb@linux.ibm.com>
15+
*/
16+
17+
#define _GNU_SOURCE
18+
#include <linux/perf_event.h>
19+
#include <linux/hw_breakpoint.h>
20+
#include <sys/ioctl.h>
21+
#include <assert.h>
22+
#include <stdlib.h>
23+
#include <stdio.h>
24+
#include <string.h>
25+
#include <unistd.h>
26+
#include <errno.h>
27+
#include <sys/syscall.h>
28+
29+
#define HW_BREAKPOINT_LEN_512 512
30+
31+
static char c[1024];
32+
static void multi_dawr_workload(void)
33+
{
34+
volatile char *ptr = c + 8;
35+
ptr[0] = 0xAA;
36+
ptr[511] = 0xBB;
37+
}
38+
39+
static int perf_process_event_open(int bp_type, __u64 addr, int len)
40+
{
41+
struct perf_event_attr attr;
42+
memset(&attr, 0, sizeof(struct perf_event_attr));
43+
attr.type = PERF_TYPE_BREAKPOINT;
44+
attr.size = sizeof(struct perf_event_attr);
45+
attr.config = 0;
46+
attr.bp_type = bp_type;
47+
attr.bp_addr = addr;
48+
attr.bp_len = len;
49+
50+
return syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
51+
}
52+
53+
int main()
54+
{
55+
unsigned long long breaks = 0;
56+
int fd;
57+
__u64 addr = (__u64)&c + 8;
58+
size_t res;
59+
60+
fd = perf_process_event_open(HW_BREAKPOINT_RW, addr, HW_BREAKPOINT_LEN_512);
61+
if (fd < 0) {
62+
perror("perf_process_event_open");
63+
return 1;
64+
}
65+
66+
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
67+
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
68+
multi_dawr_workload();
69+
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
70+
71+
res = read(fd, &breaks, sizeof(breaks));
72+
if (res != sizeof(unsigned long long)) {
73+
perror("read failed");
74+
close(fd);
75+
return 1;
76+
}
77+
78+
close(fd);
79+
80+
if (breaks != 2) {
81+
printf("FAILED: unaligned_512bytes: %llu != 2\n", breaks);
82+
return 1;
83+
}
84+
85+
printf("TEST Boundary check PASSED: unaligned_512bytes\n");
86+
return 0;
87+
}

0 commit comments

Comments
 (0)