powerpc/selftests: Add selftest to test concurrent perf/ptrace events
ptrace and perf watchpoints can't co-exists if their address range
overlaps. See commit 29da4f91c0
("powerpc/watchpoint: Don't allow
concurrent perf and ptrace events") for more detail. Add selftest
for the same.
Sample o/p:
# ./ptrace-perf-hwbreak
test: ptrace-perf-hwbreak
tags: git_version:powerpc-5.8-7-118-g937fa174a15d-dirty
perf cpu event -> ptrace thread event (Overlapping): Ok
perf cpu event -> ptrace thread event (Non-overlapping): Ok
perf thread event -> ptrace same thread event (Overlapping): Ok
perf thread event -> ptrace same thread event (Non-overlapping): Ok
perf thread event -> ptrace other thread event: Ok
ptrace thread event -> perf kernel event: Ok
ptrace thread event -> perf same thread event (Overlapping): Ok
ptrace thread event -> perf same thread event (Non-overlapping): Ok
ptrace thread event -> perf other thread event: Ok
ptrace thread event -> perf cpu event (Overlapping): Ok
ptrace thread event -> perf cpu event (Non-overlapping): Ok
ptrace thread event -> perf same thread & cpu event (Overlapping): Ok
ptrace thread event -> perf same thread & cpu event (Non-overlapping): Ok
ptrace thread event -> perf other thread & cpu event: Ok
success: ptrace-perf-hwbreak
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210412112218.128183-5-ravi.bangoria@linux.ibm.com
This commit is contained in:
parent
c65c64cc7b
commit
290f7d8ce2
@ -14,3 +14,4 @@ perf-hwbreak
|
||||
core-pkey
|
||||
ptrace-pkey
|
||||
ptrace-syscall
|
||||
ptrace-perf-hwbreak
|
||||
|
@ -2,7 +2,7 @@
|
||||
TEST_GEN_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
|
||||
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
|
||||
ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \
|
||||
perf-hwbreak ptrace-syscall
|
||||
perf-hwbreak ptrace-syscall ptrace-perf-hwbreak
|
||||
|
||||
top_srcdir = ../../../../..
|
||||
include ../../lib.mk
|
||||
|
659
tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c
Normal file
659
tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c
Normal file
@ -0,0 +1,659 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include "ptrace.h"
|
||||
|
||||
char data[16];
|
||||
|
||||
/* Overlapping address range */
|
||||
volatile __u64 *ptrace_data1 = (__u64 *)&data[0];
|
||||
volatile __u64 *perf_data1 = (__u64 *)&data[4];
|
||||
|
||||
/* Non-overlapping address range */
|
||||
volatile __u64 *ptrace_data2 = (__u64 *)&data[0];
|
||||
volatile __u64 *perf_data2 = (__u64 *)&data[8];
|
||||
|
||||
static unsigned long pid_max_addr(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char *line, *c;
|
||||
char addr[100];
|
||||
size_t len = 0;
|
||||
|
||||
fp = fopen("/proc/kallsyms", "r");
|
||||
if (!fp) {
|
||||
printf("Failed to read /proc/kallsyms. Exiting..\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (getline(&line, &len, fp) != -1) {
|
||||
if (!strstr(line, "pid_max") || strstr(line, "pid_max_max") ||
|
||||
strstr(line, "pid_max_min"))
|
||||
continue;
|
||||
|
||||
strncpy(addr, line, len < 100 ? len : 100);
|
||||
c = strchr(addr, ' ');
|
||||
*c = '\0';
|
||||
return strtoul(addr, &c, 16);
|
||||
}
|
||||
fclose(fp);
|
||||
printf("Could not find pix_max. Exiting..\n");
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void perf_user_event_attr_set(struct perf_event_attr *attr, __u64 addr, __u64 len)
|
||||
{
|
||||
memset(attr, 0, sizeof(struct perf_event_attr));
|
||||
attr->type = PERF_TYPE_BREAKPOINT;
|
||||
attr->size = sizeof(struct perf_event_attr);
|
||||
attr->bp_type = HW_BREAKPOINT_R;
|
||||
attr->bp_addr = addr;
|
||||
attr->bp_len = len;
|
||||
attr->exclude_kernel = 1;
|
||||
attr->exclude_hv = 1;
|
||||
}
|
||||
|
||||
static void perf_kernel_event_attr_set(struct perf_event_attr *attr)
|
||||
{
|
||||
memset(attr, 0, sizeof(struct perf_event_attr));
|
||||
attr->type = PERF_TYPE_BREAKPOINT;
|
||||
attr->size = sizeof(struct perf_event_attr);
|
||||
attr->bp_type = HW_BREAKPOINT_R;
|
||||
attr->bp_addr = pid_max_addr();
|
||||
attr->bp_len = sizeof(unsigned long);
|
||||
attr->exclude_user = 1;
|
||||
attr->exclude_hv = 1;
|
||||
}
|
||||
|
||||
static int perf_cpu_event_open(int cpu, __u64 addr, __u64 len)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
perf_user_event_attr_set(&attr, addr, len);
|
||||
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
|
||||
}
|
||||
|
||||
static int perf_thread_event_open(pid_t child_pid, __u64 addr, __u64 len)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
perf_user_event_attr_set(&attr, addr, len);
|
||||
return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
|
||||
}
|
||||
|
||||
static int perf_thread_cpu_event_open(pid_t child_pid, int cpu, __u64 addr, __u64 len)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
perf_user_event_attr_set(&attr, addr, len);
|
||||
return syscall(__NR_perf_event_open, &attr, child_pid, cpu, -1, 0);
|
||||
}
|
||||
|
||||
static int perf_thread_kernel_event_open(pid_t child_pid)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
perf_kernel_event_attr_set(&attr);
|
||||
return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
|
||||
}
|
||||
|
||||
static int perf_cpu_kernel_event_open(int cpu)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
perf_kernel_event_attr_set(&attr);
|
||||
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
|
||||
}
|
||||
|
||||
static int child(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
|
||||
if (ret) {
|
||||
printf("Error: PTRACE_TRACEME failed\n");
|
||||
return 0;
|
||||
}
|
||||
kill(getpid(), SIGUSR1); /* --> parent (SIGUSR1) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ptrace_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
|
||||
__u64 addr, int len)
|
||||
{
|
||||
info->version = 1;
|
||||
info->trigger_type = type;
|
||||
info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
|
||||
info->addr = addr;
|
||||
info->addr2 = addr + len;
|
||||
info->condition_value = 0;
|
||||
if (!len)
|
||||
info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
|
||||
else
|
||||
info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
|
||||
}
|
||||
|
||||
static int ptrace_open(pid_t child_pid, __u64 wp_addr, int len)
|
||||
{
|
||||
struct ppc_hw_breakpoint info;
|
||||
|
||||
ptrace_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
|
||||
return ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
|
||||
}
|
||||
|
||||
static int test1(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by ptrace)
|
||||
* if (existing cpu event by perf)
|
||||
* if (addr range overlaps)
|
||||
* fail;
|
||||
*/
|
||||
|
||||
perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
if (perf_fd < 0)
|
||||
return -1;
|
||||
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd > 0 || errno != ENOSPC)
|
||||
ret = -1;
|
||||
|
||||
close(perf_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test2(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by ptrace)
|
||||
* if (existing cpu event by perf)
|
||||
* if (addr range does not overlaps)
|
||||
* allow;
|
||||
*/
|
||||
|
||||
perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
|
||||
if (perf_fd < 0)
|
||||
return -1;
|
||||
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
|
||||
if (ptrace_fd < 0) {
|
||||
ret = -1;
|
||||
goto perf_close;
|
||||
}
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
|
||||
perf_close:
|
||||
close(perf_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test3(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by ptrace)
|
||||
* if (existing thread event by perf on the same thread)
|
||||
* if (addr range overlaps)
|
||||
* fail;
|
||||
*/
|
||||
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
|
||||
sizeof(*perf_data1));
|
||||
if (perf_fd < 0)
|
||||
return -1;
|
||||
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd > 0 || errno != ENOSPC)
|
||||
ret = -1;
|
||||
|
||||
close(perf_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test4(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by ptrace)
|
||||
* if (existing thread event by perf on the same thread)
|
||||
* if (addr range does not overlaps)
|
||||
* fail;
|
||||
*/
|
||||
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
|
||||
sizeof(*perf_data2));
|
||||
if (perf_fd < 0)
|
||||
return -1;
|
||||
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
|
||||
if (ptrace_fd < 0) {
|
||||
ret = -1;
|
||||
goto perf_close;
|
||||
}
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
|
||||
perf_close:
|
||||
close(perf_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test5(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int cpid;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by ptrace)
|
||||
* if (existing thread event by perf on the different thread)
|
||||
* allow;
|
||||
*/
|
||||
cpid = fork();
|
||||
if (!cpid) {
|
||||
/* Temporary Child */
|
||||
pause();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto kill_child;
|
||||
}
|
||||
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0) {
|
||||
ret = -1;
|
||||
goto perf_close;
|
||||
}
|
||||
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
perf_close:
|
||||
close(perf_fd);
|
||||
kill_child:
|
||||
kill(cpid, SIGINT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test6(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread kernel event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* allow;
|
||||
* -- OR --
|
||||
* if (new per cpu kernel event by perf)
|
||||
* if (existing thread event by ptrace)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_thread_kernel_event_open(child_pid);
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto ptrace_close;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
perf_fd = perf_cpu_kernel_event_open(0);
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto ptrace_close;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
ptrace_close:
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test7(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range overlaps)
|
||||
* fail;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
|
||||
sizeof(*perf_data1));
|
||||
if (perf_fd > 0 || errno != ENOSPC)
|
||||
ret = -1;
|
||||
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test8(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range does not overlaps)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
|
||||
sizeof(*perf_data2));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto ptrace_close;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
ptrace_close:
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test9(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int cpid;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread event by perf)
|
||||
* if (existing thread event by ptrace on the other thread)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
cpid = fork();
|
||||
if (!cpid) {
|
||||
/* Temporary Child */
|
||||
pause();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto kill_child;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
kill_child:
|
||||
kill(cpid, SIGINT);
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test10(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per cpu event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range overlaps)
|
||||
* fail;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
if (perf_fd > 0 || errno != ENOSPC)
|
||||
ret = -1;
|
||||
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test11(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per cpu event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range does not overlap)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto ptrace_close;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
ptrace_close:
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test12(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread and per cpu event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range overlaps)
|
||||
* fail;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
if (perf_fd > 0 || errno != ENOSPC)
|
||||
ret = -1;
|
||||
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test13(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread and per cpu event by perf)
|
||||
* if (existing thread event by ptrace on the same thread)
|
||||
* if (addr range does not overlap)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data2, sizeof(*perf_data2));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto ptrace_close;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
ptrace_close:
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test14(pid_t child_pid)
|
||||
{
|
||||
int perf_fd;
|
||||
int ptrace_fd;
|
||||
int cpid;
|
||||
int ret = 0;
|
||||
|
||||
/* Test:
|
||||
* if (new per thread and per cpu event by perf)
|
||||
* if (existing thread event by ptrace on the other thread)
|
||||
* allow;
|
||||
*/
|
||||
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
|
||||
if (ptrace_fd < 0)
|
||||
return -1;
|
||||
|
||||
cpid = fork();
|
||||
if (!cpid) {
|
||||
/* Temporary Child */
|
||||
pause();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
perf_fd = perf_thread_cpu_event_open(cpid, 0, (__u64)perf_data1,
|
||||
sizeof(*perf_data1));
|
||||
if (perf_fd < 0) {
|
||||
ret = -1;
|
||||
goto kill_child;
|
||||
}
|
||||
close(perf_fd);
|
||||
|
||||
kill_child:
|
||||
kill(cpid, SIGINT);
|
||||
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_test(const char *msg, int (*fun)(pid_t arg), pid_t arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fun(arg);
|
||||
if (ret)
|
||||
printf("%s: Error\n", msg);
|
||||
else
|
||||
printf("%s: Ok\n", msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *desc[14] = {
|
||||
"perf cpu event -> ptrace thread event (Overlapping)",
|
||||
"perf cpu event -> ptrace thread event (Non-overlapping)",
|
||||
"perf thread event -> ptrace same thread event (Overlapping)",
|
||||
"perf thread event -> ptrace same thread event (Non-overlapping)",
|
||||
"perf thread event -> ptrace other thread event",
|
||||
"ptrace thread event -> perf kernel event",
|
||||
"ptrace thread event -> perf same thread event (Overlapping)",
|
||||
"ptrace thread event -> perf same thread event (Non-overlapping)",
|
||||
"ptrace thread event -> perf other thread event",
|
||||
"ptrace thread event -> perf cpu event (Overlapping)",
|
||||
"ptrace thread event -> perf cpu event (Non-overlapping)",
|
||||
"ptrace thread event -> perf same thread & cpu event (Overlapping)",
|
||||
"ptrace thread event -> perf same thread & cpu event (Non-overlapping)",
|
||||
"ptrace thread event -> perf other thread & cpu event",
|
||||
};
|
||||
|
||||
static int test(pid_t child_pid)
|
||||
{
|
||||
int ret = TEST_PASS;
|
||||
|
||||
ret |= do_test(desc[0], test1, child_pid);
|
||||
ret |= do_test(desc[1], test2, child_pid);
|
||||
ret |= do_test(desc[2], test3, child_pid);
|
||||
ret |= do_test(desc[3], test4, child_pid);
|
||||
ret |= do_test(desc[4], test5, child_pid);
|
||||
ret |= do_test(desc[5], test6, child_pid);
|
||||
ret |= do_test(desc[6], test7, child_pid);
|
||||
ret |= do_test(desc[7], test8, child_pid);
|
||||
ret |= do_test(desc[8], test9, child_pid);
|
||||
ret |= do_test(desc[9], test10, child_pid);
|
||||
ret |= do_test(desc[10], test11, child_pid);
|
||||
ret |= do_test(desc[11], test12, child_pid);
|
||||
ret |= do_test(desc[12], test13, child_pid);
|
||||
ret |= do_test(desc[13], test14, child_pid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
|
||||
{
|
||||
if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
|
||||
perror("Can't get breakpoint info");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int ptrace_perf_hwbreak(void)
|
||||
{
|
||||
int ret;
|
||||
pid_t child_pid;
|
||||
struct ppc_debug_info dbginfo;
|
||||
|
||||
child_pid = fork();
|
||||
if (!child_pid)
|
||||
return child();
|
||||
|
||||
/* parent */
|
||||
wait(NULL); /* <-- child (SIGUSR1) */
|
||||
|
||||
get_dbginfo(child_pid, &dbginfo);
|
||||
SKIP_IF(dbginfo.num_data_bps <= 1);
|
||||
|
||||
ret = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
|
||||
SKIP_IF(ret < 0);
|
||||
close(ret);
|
||||
|
||||
ret = test(child_pid);
|
||||
|
||||
ptrace(PTRACE_CONT, child_pid, NULL, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return test_harness(ptrace_perf_hwbreak, "ptrace-perf-hwbreak");
|
||||
}
|
Loading…
Reference in New Issue
Block a user