[ltrace] Add a patch for singlestep over atomic sequence on PPC

Petr Machata pmachata at fedoraproject.org
Mon Apr 30 13:35:07 UTC 2012


commit 4fa0e1498d67582cf349527c3b786433fbd01748
Author: Petr Machata <pmachata at redhat.com>
Date:   Mon Apr 30 15:34:42 2012 +0200

    Add a patch for singlestep over atomic sequence on PPC

 ltrace-0.6.0-ppc-lwarx.patch |  365 ++++++++++++++++++++++++++++++++++++++++++
 ltrace.spec                  |    2 +
 2 files changed, 367 insertions(+), 0 deletions(-)
---
diff --git a/ltrace-0.6.0-ppc-lwarx.patch b/ltrace-0.6.0-ppc-lwarx.patch
new file mode 100644
index 0000000..5328ede
--- /dev/null
+++ b/ltrace-0.6.0-ppc-lwarx.patch
@@ -0,0 +1,365 @@
+diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
+index 711b4a3..64c1821 100644
+--- a/sysdeps/linux-gnu/ppc/arch.h
++++ b/sysdeps/linux-gnu/ppc/arch.h
+@@ -15,6 +15,7 @@
+ 
+ /* Start of arch-specific functions.  */
+ #define ARCH_ENDIAN_BIG
++#define ARCH_HAVE_ATOMIC_SINGLESTEP
+ 
+ #define PPC_NOP { 0x60, 0x00, 0x00, 0x00 }
+ #define PPC_NOP_LENGTH 4
+diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
+index 8642157..05993de 100644
+--- a/sysdeps/linux-gnu/ppc/trace.c
++++ b/sysdeps/linux-gnu/ppc/trace.c
+@@ -197,3 +197,85 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
+ 	memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs));
+ 	memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs));
+ }
++
++/* The atomic skip code is mostly taken from GDB.  */
++
++/* Instruction masks used during single-stepping of atomic
++ * sequences.  This was lifted from GDB.  */
++#define LWARX_MASK 0xfc0007fe
++#define LWARX_INSTRUCTION 0x7c000028
++#define LDARX_INSTRUCTION 0x7c0000A8
++#define STWCX_MASK 0xfc0007ff
++#define STWCX_INSTRUCTION 0x7c00012d
++#define STDCX_INSTRUCTION 0x7c0001ad
++#define BC_MASK 0xfc000000
++#define BC_INSTRUCTION 0x40000000
++
++int
++arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
++		       int (*add_cb)(void *addr, void *data),
++		       void *add_cb_data)
++{
++	void *addr = sbp->addr;
++	debug(1, "pid=%d addr=%p", proc->pid, addr);
++
++	/* If the original instruction was lwarx/ldarx, we can't
++	 * single-step over it, instead we have to execute the whole
++	 * atomic block at once.  */
++	union {
++		uint32_t insn;
++		char buf[4];
++	} u;
++	memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH);
++
++	if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION
++	    && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION)
++		return 1;
++
++	int insn_count;
++	for (insn_count = 0; ; ++insn_count) {
++		addr += 4;
++		unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
++		if (l == (unsigned long)-1 && errno)
++			return -1;
++		uint32_t insn;
++#ifdef __powerpc64__
++		insn = l >> 32;
++#else
++		insn = l;
++#endif
++
++		/* If we hit a branch instruction, give up.  The
++		 * computation could escape that way and we'd have to
++		 * treat that case specially.  */
++		if ((insn & BC_MASK) == BC_INSTRUCTION) {
++			debug(1, "pid=%d, found branch at %p, giving up",
++			      proc->pid, addr);
++			return -1;
++		}
++
++		if ((insn & STWCX_MASK) == STWCX_INSTRUCTION
++		    || (insn & STWCX_MASK) == STDCX_INSTRUCTION) {
++			debug(1, "pid=%d, found end of atomic block at %p",
++			      proc->pid, addr);
++			break;
++		}
++
++		/* Arbitrary cut-off.  If we didn't find the
++		 * terminating instruction by now, just give up.  */
++		if (insn_count > 16) {
++			debug(1, "pid=%d, couldn't find end of atomic block",
++			      proc->pid);
++			return -1;
++		}
++	}
++
++	/* Put the breakpoint to the next instruction.  */
++	addr += 4;
++	if (add_cb(addr, add_cb_data) < 0)
++		return -1;
++
++	debug(1, "PTRACE_CONT");
++	ptrace(PTRACE_CONT, proc->pid, 0, 0);
++	return 0;
++}
+diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
+index 9ecea1e..d962048 100644
+--- a/sysdeps/linux-gnu/trace.c
++++ b/sysdeps/linux-gnu/trace.c
+@@ -249,6 +249,9 @@ struct process_stopping_handler
+ 	/* The pointer being re-enabled.  */
+ 	struct breakpoint *breakpoint_being_enabled;
+ 
++	/* Artificial atomic skip breakpoint, if any needed.  */
++	void *atomic_skip_bp_addr;
++
+ 	enum {
+ 		/* We are waiting for everyone to land in t/T.  */
+ 		psh_stopping = 0,
+@@ -612,12 +615,84 @@ all_stops_accountable(struct pid_set * pids)
+ 	return 1;
+ }
+ 
+-static void
+-singlestep(Process * proc)
++/* The protocol is: 0 for success, negative for failure, positive if
++ * default singlestep is to be used.  */
++int arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
++			   int (*add_cb)(void *addr, void *data),
++			   void *add_cb_data);
++
++#ifndef ARCH_HAVE_ATOMIC_SINGLESTEP
++int
++arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
++		       int (*add_cb)(void *addr, void *data),
++		       void *add_cb_data)
++{
++	return 1;
++}
++#endif
++
++static int
++atomic_singlestep_add_bp(void *addr, void *data)
++{
++	struct process_stopping_handler *self = data;
++	struct Process *proc = self->task_enabling_breakpoint;
++
++	/* Only support single address as of now.  */
++	assert(self->atomic_skip_bp_addr == NULL);
++
++	self->atomic_skip_bp_addr = addr + 4;
++	insert_breakpoint(proc->leader, self->atomic_skip_bp_addr, NULL, 1);
++
++	return 0;
++}
++
++static int
++singlestep(struct process_stopping_handler *self)
+ {
++	struct Process *proc = self->task_enabling_breakpoint;
++
++	int status = arch_atomic_singlestep(self->task_enabling_breakpoint,
++					    self->breakpoint_being_enabled,
++					    &atomic_singlestep_add_bp, self);
++
++	/* Propagate failure and success.  */
++	if (status <= 0)
++		return status;
++
++	/* Otherwise do the default action: singlestep.  */
+ 	debug(1, "PTRACE_SINGLESTEP");
+-	if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0))
++	if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) {
+ 		perror("PTRACE_SINGLESTEP");
++		return -1;
++	}
++	return 0;
++}
++
++static void
++post_singlestep(struct process_stopping_handler *self, Event **eventp)
++{
++	continue_for_sigstop_delivery(&self->pids);
++
++	if ((*eventp)->type == EVENT_BREAKPOINT)
++		*eventp = NULL; // handled
++
++	if (self->atomic_skip_bp_addr != 0)
++		delete_breakpoint(self->task_enabling_breakpoint->leader,
++				  self->atomic_skip_bp_addr);
++
++	self->breakpoint_being_enabled = NULL;
++}
++
++static void
++singlestep_error(struct process_stopping_handler *self, Event **eventp)
++{
++	struct Process *teb = self->task_enabling_breakpoint;
++	struct breakpoint *sbp = self->breakpoint_being_enabled;
++	fprintf(stderr, "%d couldn't singlestep over %s (%p)\n",
++		teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL,
++		sbp->addr);
++	delete_breakpoint(teb->leader, sbp->addr);
++	post_singlestep(self, eventp);
+ }
+ 
+ /* This event handler is installed when we are in the process of
+@@ -670,7 +745,11 @@ process_stopping_on_event(Event_Handler * super, Event * event)
+ 			      teb->pid);
+ 			if (sbp->enabled)
+ 				disable_breakpoint(teb, sbp);
+-			singlestep(teb);
++			if (singlestep(self) < 0) {
++				singlestep_error(self, &event);
++				goto psh_sinking;
++			}
++
+ 			self->state = state = psh_singlestep;
+ 		}
+ 		break;
+@@ -682,7 +761,10 @@ process_stopping_on_event(Event_Handler * super, Event * event)
+ 
+ 			/* This is not the singlestep that we are waiting for.  */
+ 			if (event->type == EVENT_SIGNAL) {
+-				singlestep(task);
++				if (singlestep(self) < 0) {
++					singlestep_error(self, &event);
++					goto psh_sinking;
++				}
+ 				break;
+ 			}
+ 
+@@ -692,18 +774,13 @@ process_stopping_on_event(Event_Handler * super, Event * event)
+ 			if (sbp->enabled)
+ 				enable_breakpoint(teb, sbp);
+ 
+-			continue_for_sigstop_delivery(&self->pids);
+-
+-			self->breakpoint_being_enabled = NULL;
+-			self->state = state = psh_sinking;
+-
+-			if (event->type == EVENT_BREAKPOINT)
+-				event = NULL; // handled
+-		} else
+-			break;
+-
+-		/* fall-through */
++			post_singlestep(self, &event);
++			goto psh_sinking;
++		}
++		break;
+ 
++	psh_sinking:
++		state = self->state = psh_sinking;
+ 	case psh_sinking:
+ 		if (await_sigstop_delivery(&self->pids, task_info, event))
+ 			process_stopping_done(self, leader);
+diff --git a/testsuite/ltrace.torture/ppc-lwarx.c b/testsuite/ltrace.torture/ppc-lwarx.c
+new file mode 100644
+index 0000000..0716407
+--- /dev/null
++++ b/testsuite/ltrace.torture/ppc-lwarx.c
+@@ -0,0 +1,44 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <stdint.h>
++
++__attribute__((noinline, optimize(3))) void
++atomic_add(uint32_t *a, uint32_t b)
++{
++	__asm__ volatile("lwarx 9,0,%0\n"
++			 "add 9,9,%2\n"
++			 "stwcx. 9,0,%0\n"
++			 "bne- atomic_add\n"
++			 : "=r"(a)
++			 : "0"(a), "r"(b)
++			 : "%r9");
++}
++
++uint32_t a = 0;
++
++__attribute__((optimize(0))) int
++main(int argc, char **argv)
++{
++	atomic_add(&a, 5);
++	atomic_add(&a, 10);
++	atomic_add(&a, 15);
++	return a;
++}
+diff --git a/testsuite/ltrace.torture/ppc-lwarx.exp b/testsuite/ltrace.torture/ppc-lwarx.exp
+new file mode 100644
+index 0000000..bc2eba4
+--- /dev/null
++++ b/testsuite/ltrace.torture/ppc-lwarx.exp
+@@ -0,0 +1,55 @@
++# This file is part of ltrace.
++# Copyright (C) 2012 Petr Machata, Red Hat Inc.
++#
++# This program is free software; you can redistribute it and/or
++# modify it under the terms of the GNU General Public License as
++# published by the Free Software Foundation; either version 2 of the
++# License, or (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++# 02110-1301 USA
++
++set testfile "ppc-lwarx"
++set srcfile ${testfile}.c
++set binfile ${testfile}
++
++if [get_compiler_info $binfile] {
++  return -1
++}
++
++if { [istarget powerpc*-*] } then {
++    verbose "compiling source file now....."
++    if { [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable {debug} ] != "" } {
++	 send_user "Testcase compile failed, so all tests in this file will automatically fail\n."
++    }
++
++    # set options for ltrace.
++    ltrace_options "-x" "atomic_add" "-e" "!atoi"
++
++    # Run PUT for ltarce.
++    set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
++
++    # Check the output of this program.
++    verbose "ltrace runtest output: $exec_output\n"
++    if [regexp {ELF from incompatible architecture} $exec_output] {
++	    fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!"
++	    return 
++    } elseif [ regexp {Couldn't get .hash data} $exec_output ] {
++	    fail "Couldn't get .hash data!"
++	    return
++    }
++
++    set pattern "atomic_add(.*, 5,.*)"
++    ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++    set pattern "atomic_add(.*, 10,.*)"
++    ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++    set pattern "atomic_add(.*, 15,.*)"
++    ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++}
diff --git a/ltrace.spec b/ltrace.spec
index cb27ee3..64838a5 100644
--- a/ltrace.spec
+++ b/ltrace.spec
@@ -31,6 +31,7 @@ Patch13: ltrace-0.6.0-process-start.patch
 Patch14: ltrace-0.6.0-selinux.patch
 Patch15: ltrace-0.6.0-detach-sleeping.patch
 Patch16: ltrace-0.6.0-tail-return.patch
+Patch17: ltrace-0.6.0-ppc-lwarx.patch
 
 %description
 Ltrace is a debugging program which runs a specified command until the
@@ -60,6 +61,7 @@ execution of processes.
 %patch14 -p1
 %patch15 -p1
 %patch16 -p1
+%patch17 -p1
 sed -i -e 's/-o root -g root//' Makefile.in
 
 %build


More information about the scm-commits mailing list