162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <asm/unistd.h> 462306a36Sopenharmony_ci#include <linux/hw_breakpoint.h> 562306a36Sopenharmony_ci#include <linux/ptrace.h> 662306a36Sopenharmony_ci#include <memory.h> 762306a36Sopenharmony_ci#include <stdlib.h> 862306a36Sopenharmony_ci#include <sys/wait.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "utils.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Child subroutine that performs a load on the address, then traps 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_civoid same_watch_addr_child(unsigned long *addr); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Address of the ld instruction in same_watch_addr_child() */ 1862306a36Sopenharmony_ciextern char same_watch_addr_load[]; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Address of the end trap instruction in same_watch_addr_child() */ 2162306a36Sopenharmony_ciextern char same_watch_addr_trap[]; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Child subroutine that performs a load on the first address, then a load on 2562306a36Sopenharmony_ci * the second address (with no instructions separating this from the first 2662306a36Sopenharmony_ci * load), then traps. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid perf_then_ptrace_child(unsigned long *first_addr, unsigned long *second_addr); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Address of the first ld instruction in perf_then_ptrace_child() */ 3162306a36Sopenharmony_ciextern char perf_then_ptrace_load1[]; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Address of the second ld instruction in perf_then_ptrace_child() */ 3462306a36Sopenharmony_ciextern char perf_then_ptrace_load2[]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Address of the end trap instruction in perf_then_ptrace_child() */ 3762306a36Sopenharmony_ciextern char perf_then_ptrace_trap[]; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic inline long sys_ptrace(long request, pid_t pid, unsigned long addr, unsigned long data) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci return syscall(__NR_ptrace, request, pid, addr, data); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic long ptrace_traceme(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return sys_ptrace(PTRACE_TRACEME, 0, 0, 0); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic long ptrace_getregs(pid_t pid, struct pt_regs *result) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return sys_ptrace(PTRACE_GETREGS, pid, 0, (unsigned long)result); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic long ptrace_setregs(pid_t pid, struct pt_regs *result) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci return sys_ptrace(PTRACE_SETREGS, pid, 0, (unsigned long)result); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic long ptrace_cont(pid_t pid, long signal) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return sys_ptrace(PTRACE_CONT, pid, 0, signal); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic long ptrace_singlestep(pid_t pid, long signal) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return sys_ptrace(PTRACE_SINGLESTEP, pid, 0, signal); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic long ppc_ptrace_gethwdbginfo(pid_t pid, struct ppc_debug_info *dbginfo) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return sys_ptrace(PPC_PTRACE_GETHWDBGINFO, pid, 0, (unsigned long)dbginfo); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic long ppc_ptrace_sethwdbg(pid_t pid, struct ppc_hw_breakpoint *bp_info) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return sys_ptrace(PPC_PTRACE_SETHWDEBUG, pid, 0, (unsigned long)bp_info); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic long ppc_ptrace_delhwdbg(pid_t pid, int bp_id) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci return sys_ptrace(PPC_PTRACE_DELHWDEBUG, pid, 0L, bp_id); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic long ptrace_getreg_pc(pid_t pid, void **pc) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct pt_regs regs; 8762306a36Sopenharmony_ci long err; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci err = ptrace_getregs(pid, ®s); 9062306a36Sopenharmony_ci if (err) 9162306a36Sopenharmony_ci return err; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci *pc = (void *)regs.nip; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic long ptrace_setreg_pc(pid_t pid, void *pc) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct pt_regs regs; 10162306a36Sopenharmony_ci long err; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci err = ptrace_getregs(pid, ®s); 10462306a36Sopenharmony_ci if (err) 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci regs.nip = (unsigned long)pc; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci err = ptrace_setregs(pid, ®s); 11062306a36Sopenharmony_ci if (err) 11162306a36Sopenharmony_ci return err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, 11762306a36Sopenharmony_ci int group_fd, unsigned long flags) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void perf_user_event_attr_set(struct perf_event_attr *attr, void *addr, u64 len) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci memset(attr, 0, sizeof(struct perf_event_attr)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci attr->type = PERF_TYPE_BREAKPOINT; 12762306a36Sopenharmony_ci attr->size = sizeof(struct perf_event_attr); 12862306a36Sopenharmony_ci attr->bp_type = HW_BREAKPOINT_R; 12962306a36Sopenharmony_ci attr->bp_addr = (u64)addr; 13062306a36Sopenharmony_ci attr->bp_len = len; 13162306a36Sopenharmony_ci attr->exclude_kernel = 1; 13262306a36Sopenharmony_ci attr->exclude_hv = 1; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int perf_watchpoint_open(pid_t child_pid, void *addr, u64 len) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct perf_event_attr attr; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci perf_user_event_attr_set(&attr, addr, len); 14062306a36Sopenharmony_ci return perf_event_open(&attr, child_pid, -1, -1, 0); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int perf_read_counter(int perf_fd, u64 *count) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * A perf counter is retrieved by the read() syscall. It contains 14762306a36Sopenharmony_ci * the current count as 8 bytes that are interpreted as a u64 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci ssize_t len = read(perf_fd, count, sizeof(*count)); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (len != sizeof(*count)) 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void ppc_ptrace_init_breakpoint(struct ppc_hw_breakpoint *info, 15862306a36Sopenharmony_ci int type, void *addr, int len) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci info->version = 1; 16162306a36Sopenharmony_ci info->trigger_type = type; 16262306a36Sopenharmony_ci info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; 16362306a36Sopenharmony_ci info->addr = (u64)addr; 16462306a36Sopenharmony_ci info->addr2 = (u64)addr + len; 16562306a36Sopenharmony_ci info->condition_value = 0; 16662306a36Sopenharmony_ci if (!len) 16762306a36Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * Checks if we can place at least 2 watchpoints on the child process 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic int check_watchpoints(pid_t pid) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct ppc_debug_info dbginfo; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci FAIL_IF_MSG(ppc_ptrace_gethwdbginfo(pid, &dbginfo), "PPC_PTRACE_GETHWDBGINFO failed"); 18062306a36Sopenharmony_ci SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)"); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Wrapper around a plain fork() call that sets up the child for 18762306a36Sopenharmony_ci * ptrace-ing. Both the parent and child return from this, though 18862306a36Sopenharmony_ci * the child is stopped until ptrace_cont(pid) is run by the parent. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic int ptrace_fork_child(pid_t *pid) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int status; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci *pid = fork(); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (*pid < 0) 19762306a36Sopenharmony_ci FAIL_IF_MSG(1, "Failed to fork child"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!*pid) { 20062306a36Sopenharmony_ci FAIL_IF_EXIT_MSG(ptrace_traceme(), "PTRACE_TRACEME failed"); 20162306a36Sopenharmony_ci FAIL_IF_EXIT_MSG(raise(SIGSTOP), "Child failed to raise SIGSTOP"); 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci /* Synchronise on child SIGSTOP */ 20462306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(*pid, &status, 0) == -1, "Failed to wait for child"); 20562306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * Tests the interaction between ptrace and perf watching the same data. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * We expect ptrace to take 'priority', as it is has before-execute 21562306a36Sopenharmony_ci * semantics. 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * The perf counter should not be incremented yet because perf has after-execute 21862306a36Sopenharmony_ci * semantics. E.g., if ptrace changes the child PC, we don't even execute the 21962306a36Sopenharmony_ci * instruction at all. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * When the child is stopped for ptrace, we test both continue and single step. 22262306a36Sopenharmony_ci * Both should increment the perf counter. We also test changing the PC somewhere 22362306a36Sopenharmony_ci * different and stepping, which should not increment the perf counter. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ciint same_watch_addr_test(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */ 22862306a36Sopenharmony_ci int bp_id; /* Breakpoint handle of ptrace watchpoint */ 22962306a36Sopenharmony_ci int perf_fd; /* File descriptor of perf performance counter */ 23062306a36Sopenharmony_ci u64 perf_count; /* Most recently fetched perf performance counter value */ 23162306a36Sopenharmony_ci pid_t pid; /* PID of child process */ 23262306a36Sopenharmony_ci void *pc; /* Most recently fetched child PC value */ 23362306a36Sopenharmony_ci int status; /* Stop status of child after waitpid */ 23462306a36Sopenharmony_ci unsigned long value; /* Dummy value to be read/written to by child */ 23562306a36Sopenharmony_ci int err; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci err = ptrace_fork_child(&pid); 23862306a36Sopenharmony_ci if (err) 23962306a36Sopenharmony_ci return err; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!pid) { 24262306a36Sopenharmony_ci same_watch_addr_child(&value); 24362306a36Sopenharmony_ci exit(1); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci err = check_watchpoints(pid); 24762306a36Sopenharmony_ci if (err) 24862306a36Sopenharmony_ci return err; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Place a perf watchpoint counter on value */ 25162306a36Sopenharmony_ci perf_fd = perf_watchpoint_open(pid, &value, sizeof(value)); 25262306a36Sopenharmony_ci FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter"); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Place a ptrace watchpoint on value */ 25562306a36Sopenharmony_ci ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, &value, sizeof(value)); 25662306a36Sopenharmony_ci bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); 25762306a36Sopenharmony_ci FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Let the child run. It should stop on the ptrace watchpoint */ 26062306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 26362306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 26462306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 26562306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction"); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * We stopped before executing the load, so perf should not have 26962306a36Sopenharmony_ci * recorded any events yet 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 27262306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 0, "perf recorded unexpected event"); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Single stepping over the load should increment the perf counter */ 27562306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_singlestep(pid, 0), "Failed to single step child"); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 27862306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 27962306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 28062306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_load + 4, "Failed to single step load instruction"); 28162306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 28262306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 1, "perf counter did not increment"); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Set up a ptrace watchpoint on the value again and trigger it. 28662306a36Sopenharmony_ci * The perf counter should not have incremented because we do not 28762306a36Sopenharmony_ci * execute the load yet. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint"); 29062306a36Sopenharmony_ci bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); 29162306a36Sopenharmony_ci FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); 29262306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); 29362306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 29662306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 29762306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 29862306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load trap"); 29962306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 30062306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 1, "perf counter should not have changed"); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Continuing over the load should increment the perf counter */ 30362306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 30662306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 30762306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 30862306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); 30962306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 31062306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 2, "perf counter did not increment"); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * If we set the child PC back to the load instruction, then continue, 31462306a36Sopenharmony_ci * we should reach the end trap (because ptrace is one-shot) and have 31562306a36Sopenharmony_ci * another perf event. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); 31862306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 32162306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 32262306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 32362306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); 32462306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 32562306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 3, "perf counter did not increment"); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * If we set the child PC back to the load instruction, set a ptrace 32962306a36Sopenharmony_ci * watchpoint on the load, then continue, we should immediately get 33062306a36Sopenharmony_ci * the ptrace trap without incrementing the perf counter 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint"); 33362306a36Sopenharmony_ci bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); 33462306a36Sopenharmony_ci FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); 33562306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); 33662306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 33962306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 34062306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 34162306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction"); 34262306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 34362306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed"); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * If we change the PC while stopped on the load instruction, we should 34762306a36Sopenharmony_ci * not increment the perf counter (because ptrace is before-execute, 34862306a36Sopenharmony_ci * perf is after-execute). 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load + 4), "Failed to set child PC"); 35162306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 35462306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 35562306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 35662306a36Sopenharmony_ci FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); 35762306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 35862306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed"); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Clean up child */ 36162306a36Sopenharmony_ci FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child"); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* 36762306a36Sopenharmony_ci * Tests the interaction between ptrace and perf when: 36862306a36Sopenharmony_ci * 1. perf watches a value 36962306a36Sopenharmony_ci * 2. ptrace watches a different value 37062306a36Sopenharmony_ci * 3. The perf value is read, then the ptrace value is read immediately after 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * A breakpoint implementation may accidentally misattribute/skip one of 37362306a36Sopenharmony_ci * the ptrace or perf handlers, as interrupt based work is done after perf 37462306a36Sopenharmony_ci * and before ptrace. 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * We expect the perf counter to increment before the ptrace watchpoint 37762306a36Sopenharmony_ci * triggers. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ciint perf_then_ptrace_test(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */ 38262306a36Sopenharmony_ci int bp_id; /* Breakpoint handle of ptrace watchpoint */ 38362306a36Sopenharmony_ci int perf_fd; /* File descriptor of perf performance counter */ 38462306a36Sopenharmony_ci u64 perf_count; /* Most recently fetched perf performance counter value */ 38562306a36Sopenharmony_ci pid_t pid; /* PID of child process */ 38662306a36Sopenharmony_ci void *pc; /* Most recently fetched child PC value */ 38762306a36Sopenharmony_ci int status; /* Stop status of child after waitpid */ 38862306a36Sopenharmony_ci unsigned long perf_value; /* Dummy value to be watched by perf */ 38962306a36Sopenharmony_ci unsigned long ptrace_value; /* Dummy value to be watched by ptrace */ 39062306a36Sopenharmony_ci int err; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci err = ptrace_fork_child(&pid); 39362306a36Sopenharmony_ci if (err) 39462306a36Sopenharmony_ci return err; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * If we are the child, run a subroutine that reads the perf value, 39862306a36Sopenharmony_ci * then reads the ptrace value with consecutive load instructions 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci if (!pid) { 40162306a36Sopenharmony_ci perf_then_ptrace_child(&perf_value, &ptrace_value); 40262306a36Sopenharmony_ci exit(0); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci err = check_watchpoints(pid); 40662306a36Sopenharmony_ci if (err) 40762306a36Sopenharmony_ci return err; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Place a perf watchpoint counter */ 41062306a36Sopenharmony_ci perf_fd = perf_watchpoint_open(pid, &perf_value, sizeof(perf_value)); 41162306a36Sopenharmony_ci FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter"); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Place a ptrace watchpoint */ 41462306a36Sopenharmony_ci ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, 41562306a36Sopenharmony_ci &ptrace_value, sizeof(ptrace_value)); 41662306a36Sopenharmony_ci bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); 41762306a36Sopenharmony_ci FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Let the child run. It should stop on the ptrace watchpoint */ 42062306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); 42362306a36Sopenharmony_ci FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); 42462306a36Sopenharmony_ci FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); 42562306a36Sopenharmony_ci FAIL_IF_MSG(pc != perf_then_ptrace_load2, "Child did not stop on ptrace load"); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* perf should have recorded the first load */ 42862306a36Sopenharmony_ci FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); 42962306a36Sopenharmony_ci FAIL_IF_MSG(perf_count != 1, "perf counter did not increment"); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Clean up child */ 43262306a36Sopenharmony_ci FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child"); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ciint main(int argc, char *argv[]) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci int err = 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci err |= test_harness(same_watch_addr_test, "same_watch_addr"); 44262306a36Sopenharmony_ci err |= test_harness(perf_then_ptrace_test, "perf_then_ptrace"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return err; 44562306a36Sopenharmony_ci} 446