public inbox for frysk@sourceware.org
 help / color / mirror / Atom feed
From: Phil Muldoon <pmuldoon@redhat.com>
To: frysk@sourceware.org
Subject: Re: [SCM]  master: 2008-03-28  Phil Muldoon <pmuldoon@redhat.com>
Date: Fri, 28 Mar 2008 15:05:00 -0000	[thread overview]
Message-ID: <47ED0938.4050507@redhat.com> (raw)
In-Reply-To: <20080328145630.18231.qmail@sourceware.org>

pmuldoon@sourceware.org wrote:

This commit comprises the initial low level view of watchpoint creation. 
This allows watchpoints to be created on  IA32 and X8664. Other 
architectures can be added. These classes are mainly concerned with 
pushing the values to the debug registers and bit setting the 
appropriate scope and execution bits in the debug control register. As 
such the test only correct register setting. When the other side of 
watchpoints are enabled (capture and state) the tests will be expanded 
in that direction.

Regards

Phil
> The branch, master has been updated
>        via  f420379d810861961ed40f40544c75124f9701b5 (commit)
>       from  8ad1844bae3c034520abfb564d1a37d9b7ea933a (commit)
>
> Those revisions listed above that are new to this repository have
> not appeared on any other notification email.
>
> - Log -----------------------------------------------------------------
> commit f420379d810861961ed40f40544c75124f9701b5
> Author: Phil Muldoon <pmuldoon@redhat.com>
> Date:   Fri Mar 28 14:55:48 2008 +0000
>
>     2008-03-28  Phil Muldoon <pmuldoon@redhat.com>
>     
>             * Watchpoint.java: New. Initial Implementation.
>             * WatchpointFactory.java: Ditto.
>             * IA32Watchpoint.java: Ditto.
>             * X8664Watchpoint.java: Ditto.
>             * TestWatchpoint.java: Ditto.
>             * package.html: Ditto.
>
> -----------------------------------------------------------------------
>
> Summary of changes:
>  frysk-core/frysk/isa/watchpoints/ChangeLog         |   16 ++
>  .../frysk/isa/watchpoints/IA32Watchpoint.java      |  214 +++++++++++++++++++
>  .../frysk/isa/watchpoints/TestWatchpoint.java      |  224 ++++++++++++++++++++
>  .../watchpoints/Watchpoint.java}                   |  108 ++++------
>  .../watchpoints/WatchpointFactory.java}            |   23 ++-
>  .../frysk/isa/watchpoints/X8664Watchpoint.java     |  209 ++++++++++++++++++
>  frysk-core/frysk/isa/watchpoints/package.html      |   12 +
>  7 files changed, 735 insertions(+), 71 deletions(-)
>  create mode 100644 frysk-core/frysk/isa/watchpoints/ChangeLog
>  create mode 100644 frysk-core/frysk/isa/watchpoints/IA32Watchpoint.java
>  create mode 100644 frysk-core/frysk/isa/watchpoints/TestWatchpoint.java
>  copy frysk-core/frysk/{sysroot/SysRootCache.java => isa/watchpoints/Watchpoint.java} (53%)
>  copy frysk-core/frysk/{testbed/TestStatState.java => isa/watchpoints/WatchpointFactory.java} (84%)
>  create mode 100644 frysk-core/frysk/isa/watchpoints/X8664Watchpoint.java
>  create mode 100644 frysk-core/frysk/isa/watchpoints/package.html
>
> First 500 lines of diff:
> diff --git a/frysk-core/frysk/isa/watchpoints/ChangeLog b/frysk-core/frysk/isa/watchpoints/ChangeLog
> new file mode 100644
> index 0000000..0e4f56f
> --- /dev/null
> +++ b/frysk-core/frysk/isa/watchpoints/ChangeLog
> @@ -0,0 +1,16 @@
> +2008-03-28  Phil Muldoon <pmuldoon@redhat.com>
> +
> +	* Watchpoint.java: New. Initial Implementation.
> +	* WatchpointFactory.java: Ditto.
> +	* IA32Watchpoint.java: Ditto.
> +	* X8664Watchpoint.java: Ditto.
> +	* TestWatchpoint.java: Ditto.
> +	* package.html: Ditto.
> +	
> +\f
> +Local Variables:
> +mode: change-log
> +left-margin: 8
> +fill-column: 74
> +version-control: never
> +End:
> diff --git a/frysk-core/frysk/isa/watchpoints/IA32Watchpoint.java b/frysk-core/frysk/isa/watchpoints/IA32Watchpoint.java
> new file mode 100644
> index 0000000..3627d03
> --- /dev/null
> +++ b/frysk-core/frysk/isa/watchpoints/IA32Watchpoint.java
> @@ -0,0 +1,214 @@
> +// This file is part of the program FRYSK.
> +//
> +// Copyright 2005, 2006, 2007, 2008 Red Hat Inc.
> +//
> +// FRYSK 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; version 2 of the License.
> +//
> +// FRYSK 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 FRYSK; if not, write to the Free Software Foundation,
> +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> +// 
> +// In addition, as a special exception, Red Hat, Inc. gives You the
> +// additional right to link the code of FRYSK with code not covered
> +// under the GNU General Public License ("Non-GPL Code") and to
> +// distribute linked combinations including the two, subject to the
> +// limitations in this paragraph. Non-GPL Code permitted under this
> +// exception must only link to the code of FRYSK through those well
> +// defined interfaces identified in the file named EXCEPTION found in
> +// the source code files (the "Approved Interfaces"). The files of
> +// Non-GPL Code may instantiate templates or use macros or inline
> +// functions from the Approved Interfaces without causing the
> +// resulting work to be covered by the GNU General Public
> +// License. Only Red Hat, Inc. may make changes or additions to the
> +// list of Approved Interfaces. You must obey the GNU General Public
> +// License in all respects for all of the FRYSK code and other code
> +// used in conjunction with FRYSK except the Non-GPL Code covered by
> +// this exception. If you modify this file, you may extend this
> +// exception to your version of the file, but you are not obligated to
> +// do so. If you do not wish to provide this exception without
> +// modification, you must delete this exception statement from your
> +// version and license this file solely under the GPL without
> +// exception.
> +
> +package frysk.isa.watchpoints;
> +
> +import frysk.isa.registers.IA32Registers;
> +import frysk.proc.Task;
> +
> +class IA32Watchpoint extends Watchpoint {
> +
> +    // Architecture Watchpoint Count. Number of usable
> +    // Address-Breakpoint Registers (DR0-DR3)
> +    public IA32Watchpoint () {
> +	noOfWatchpoints = 4;
> +    }
> +
> +    /**
> +     * Builds and sets a hardware watchpoint on a task.
> +     *
> +     * @param task - task to set a watchpoint on.
> +     * @param index - watchpoint number to write. Architecture
> +     * dependent.
> +     * @param addr - linear virtual address to watch.
> +     * @param range - length of range to watch. Normally
> +     * 1,2 or 4 bytes.
> +     * @param writeOnly - When true, only trigger when address is
> +     * written. False, trigger when address is read or written to.
> +     * @param localOnly - set local extant only.
> +     */
> +    public void setWatchpoint(Task task, int index, 
> +	       long addr, int range,
> +	       boolean writeOnly, 
> +	       boolean localOnly) {
> +
> +	
> +	// turn bit off (b = bit no): l &= ~(1L << b)
> +	// turn bit on ( b= bit no):  l |= (1L << b);
> +	if ((range == 1) || (range == 2) || (range == 4)) {
> +	    
> +	    // Set the Debug register with the linear address.
> +	    task.setRegister(IA32Registers.DEBUG_REGS_GROUP.getRegisters()[index],
> +			     addr);
> +	    // Get the Debug Control Register
> +	    long debugControl = task.getRegister(IA32Registers.DEBUG_CONTROL);
> +	    
> +	    // First eight bits of register define the global/local
> +	    // status of each of the four DR registers. Two bits per
> +	    // register
> +	    
> +	    // Calculate "Global Exact Breakpoint #index Enabled" bit to set
> +	    int bitToSet = index * 2;
> +	    
> +	    if (localOnly) {
> +		// Set "Local Exact Breakpoint #index Enabled" to 0
> +		debugControl |= (1L << bitToSet);		
> +		// Set Global Exact Breakpoint to 1
> +		debugControl &= ~(1L << bitToSet+1);		
> +	    } else {
> +		// Set "Local Exact Breakpoint #index Enabled" to 0
> +		debugControl &= ~(1L << bitToSet);		
> +		// Set Global Exact Breakpoint to 1
> +		debugControl |= (1L << bitToSet+1);
> +	    }	    
> +	    
> +	    // Dending on the WP register to set, the next
> +	    // 4 bits are offset 4 * WP Count. On x8664 
> +	    // the control bits for DR0 start at bit 16,
> +	    // DR1 at 20 and so on. 
> +
> +	    // The next four bits are as follows:
> +
> +	    // Set watchpoint to read/write detection
> +	    // bits 11 = read/write. bits 01 = write only.
> +	    int typeOfWpTrap = 16 + (index *4);
> +	    
> +	    
> +	    if (writeOnly) {
> +		debugControl |= (1L << typeOfWpTrap);
> +		debugControl &= ~(1L << typeOfWpTrap+1);		
> +	    } else {
> +		debugControl |= (1L << typeOfWpTrap);
> +		debugControl |= (1L << typeOfWpTrap+1);
> +	    }
> +
> +	    // Set watch point length
> +	    // 00 = 1 byte, 01 = 2 bytes, 11 = 4 bytes
> +	    // 10 = 8 bytes
> +	    int length = typeOfWpTrap + 2;
> +	    switch (range) {
> +	    case 1:
> +		debugControl &= ~(1L << length);
> +		debugControl &= ~(1L << length+1);
> +		break;
> +	    case 2:
> +		debugControl &= ~(1L << length);
> +		debugControl |= (1L << length+1);
> +		break;
> +	    case 4:
> +		debugControl |=(1L << length);
> +		debugControl |= (1L << length+1);
> +		break;
> +	    case 8:
> +		debugControl |= (1L << length);
> +		debugControl &= ~(1L << length+1);
> +		break;
> +	    }
> +
> +	    task.setRegister(IA32Registers.DEBUG_CONTROL, debugControl);
> +	}
> +	else
> +	    throw new RuntimeException("Invalid size for watchpoint " +
> +				       "range. Has to be 1, 2, 4 or 8");
> +    }
> +
> +    /**
> +     * Reads a watchpoint. Takes a task, and an index.
> +     *
> +     * @param task - task to read a watchpoint from.
> +     * @param index - watchpoint number to read.
> +     *
> +     * @return long - value of register for watchpoint.
> +     */
> +    public long readWatchpoint(Task task, int index) {
> +	return task.getRegister(IA32Registers.DEBUG_REGS_GROUP.getRegisters()[index]);
> +    }		
> +
> +    /**
> +     * Deletes a watchpoint. Takes a task, and an index.
> +     *
> +     * @param task - task on which to delete a watchpoint.
> +     * @param index - watchpoint number to delete.
> +     *
> +     * @return long - value of register for wp
> +     */
> +    public final void deleteWatchpoint(Task task, int index) {
> +	    // Set the Debug register with the linear address.
> +	    task.setRegister(IA32Registers.DEBUG_REGS_GROUP.getRegisters()[index],
> +			     0x0L);
> +	    // Get the Debug Control Register
> +	    long debugControl = task.getRegister(IA32Registers.DEBUG_CONTROL);
> +	    
> +	    // First eight bits of register define the global/local
> +	    // status of each of the four DR registers. Two bits per
> +	    // register
> +	    
> +	    // Calculate "Global Exact Breakpoint #index Enabled" bit to set
> +	    int bitToSet = index * 2;
> +
> +	    // Clear all register bits to 0
> +	    debugControl &= ~(1L << bitToSet);
> +	    debugControl &= ~(1L << bitToSet+1);
> +
> +	    int typeOfWpTrap = 16 + (index *4);
> +	    debugControl &= ~(1L << typeOfWpTrap);
> +	    debugControl &= ~(1L << typeOfWpTrap+1);
> +
> +	    int length = typeOfWpTrap + 2;
> +	    debugControl &= ~(1L << length);
> +	    debugControl &= ~(1L << length+1);
> +
> +	    task.setRegister(IA32Registers.DEBUG_CONTROL, debugControl);
> +    }
> +
> +    
> +
> +    /**
> +     * Reads the Debug control register.
> +     *
> +     * @param task - task to read the debug control
> +     * register from.
> +     */
> +    public long readControlRegister(Task task) {
> +	return task.getRegister(IA32Registers.DEBUG_CONTROL);
> +    }
> +
> +}
> +
> +
> diff --git a/frysk-core/frysk/isa/watchpoints/TestWatchpoint.java b/frysk-core/frysk/isa/watchpoints/TestWatchpoint.java
> new file mode 100644
> index 0000000..e218689
> --- /dev/null
> +++ b/frysk-core/frysk/isa/watchpoints/TestWatchpoint.java
> @@ -0,0 +1,224 @@
> +// This file is part of the program FRYSK.
> +//
> +// Copyright 2008, Red Hat Inc.
> +//
> +// FRYSK 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; version 2 of the License.
> +//
> +// FRYSK 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 FRYSK; if not, write to the Free Software Foundation,
> +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> +// 
> +// In addition, as a special exception, Red Hat, Inc. gives You the
> +// additional right to link the code of FRYSK with code not covered
> +// under the GNU General Public License ("Non-GPL Code") and to
> +// distribute linked combinations including the two, subject to the
> +// limitations in this paragraph. Non-GPL Code permitted under this
> +// exception must only link to the code of FRYSK through those well
> +// defined interfaces identified in the file named EXCEPTION found in
> +// the source code files (the "Approved Interfaces"). The files of
> +// Non-GPL Code may instantiate templates or use macros or inline
> +// functions from the Approved Interfaces without causing the
> +// resulting work to be covered by the GNU General Public
> +// License. Only Red Hat, Inc. may make changes or additions to the
> +// list of Approved Interfaces. You must obey the GNU General Public
> +// License in all respects for all of the FRYSK code and other code
> +// used in conjunction with FRYSK except the Non-GPL Code covered by
> +// this exception. If you modify this file, you may extend this
> +// exception to your version of the file, but you are not obligated to
> +// do so. If you do not wish to provide this exception without
> +// modification, you must delete this exception statement from your
> +// version and license this file solely under the GPL without
> +// exception.
> +
> +
> +package frysk.isa.watchpoints;
> +
> +import frysk.proc.Proc;
> +import frysk.proc.Task;
> +import frysk.testbed.DaemonBlockedAtEntry;
> +import frysk.testbed.TestLib;
> +public class TestWatchpoint extends TestLib {
> +  
> +
> +    public void testWatchFourBytesBitPattern() {
> +	// Test Four byte bit pattern in a cumulative fasion
> +	// across all WP registers. Assume global exact and 
> +	// read/write bit is set. At end, delete all watchpoints
> +	// and ensure debug control register is as we found it.
> +	if (unresolvedOnPPC(5991)) 
> +	    return;
> +	Proc proc = giveMeABlockedProc();
> +	Task task = proc.getMainTask();
> +	long address = 0x1000;
> +	long debugControlRegister;
> +	Watchpoint wp = WatchpointFactory.getWatchpoint(task.getISA());
> +	long savedDebugControlRegister = wp.readControlRegister(task);
> +	for (int i=0; i<4; i++) {
> +
> +
> +	    wp.setWatchpoint(task, i, address, 4, false, false);
> +	    // simple first test. Does register contain address?
> +	    assertEquals("Saved watchpoint and address are similar", 
> +		 address, wp.readWatchpoint(task, i));
> +	    
> +	    debugControlRegister = wp.readControlRegister(task);
> +	
> +	    // Test Debug Control Register Bit Pattern. Global Exact.
> +	    assertEquals(i + " wp local exact bit", false, 
> +		    testBit(debugControlRegister, 0 + (i*2)));
> +	    assertEquals(i + " wp global exact bit", true, 
> +		    testBit(debugControlRegister,  1 + (i*2)));
> +
> +	    // Test Debug Control Register. Fire on Read and Write
> +	    assertEquals(i + "wp r/w bit 0", true, 
> +		    testBit(debugControlRegister, 16 + (i * 4)));
> +	    assertEquals(i + "wp r/w bit 1 ", true, 
> +		    testBit(debugControlRegister, 17 + (i * 4)));
> +
> +	    // Test Debug Control Register. Test Length four bytes.
> +	    assertEquals(i +"wp length bit 0", true, 
> +		    testBit(debugControlRegister, 18 + (i * 4)));
> +	    assertEquals(i + "wo length bit 1", true ,
> +		    testBit(debugControlRegister, 19 + (i * 4)));
> +	}
> +	
> +	for (int j=0; j < 4; j++) {	    
> +	    wp.deleteWatchpoint(task,j);
> +	    assertEquals("Deleted Watchpoint is 0", 
> +		    wp.readWatchpoint(task, j),0);
> +	}
> +
> +	assertEquals("Debug control register is left as we originally found it", 
> +		savedDebugControlRegister,wp.readControlRegister(task));
> +	
> +    }
> +
> +    public void testWatchOneByteBitPattern() {
> +	// Test One byte bit pattern in a cumulative fasion
> +	// across all WP registers. Assume global exact and 
> +	// read/write bit is set. At end, delete all watchpoints
> +	// and ensure debug control register is as we found it.
> +	if (unresolvedOnPPC(5991)) 
> +	    return;
> +	Proc proc = giveMeABlockedProc();
> +	Task task = proc.getMainTask();
> +	long address = 0x1000;
> +	long debugControlRegister;
> +	Watchpoint wp = WatchpointFactory.getWatchpoint(task.getISA());
> +	long savedDebugControlRegister = wp.readControlRegister(task);
> +	for (int i=0; i<4; i++) {
> +
> +
> +	    wp.setWatchpoint(task, i, address, 1, true, false);
> +	    // simple first test. Does register contain address?
> +	    assertEquals("Saved watchpoint and address are similar", 
> +		 address, wp.readWatchpoint(task, i));
> +	    
> +	    debugControlRegister = wp.readControlRegister(task);
> +	
> +	    // Test Debug Control Register Bit Pattern. Global Exact.
> +	    assertEquals(i + " wp local exact bit", false, 
> +		    testBit(debugControlRegister, 0 + (i*2)));
> +	    assertEquals(i + " wp global exact bit", true, 
> +		    testBit(debugControlRegister,  1 + (i*2)));
> +
> +	    // Test Debug Control Register. Fire on Read and Write
> +	    assertEquals(i + "wp r/w bit 0", true, 
> +		    testBit(debugControlRegister, 16 + (i * 4)));
> +	    assertEquals(i + "wp r/w bit 1 ", false, 
> +		    testBit(debugControlRegister, 17 + (i * 4)));
> +
> +	    // Test Debug Control Register. Test Length four bytes.
> +	    assertEquals(i +"wp length bit 0", false, 
> +		    testBit(debugControlRegister, 18 + (i * 4)));
> +	    assertEquals(i + "wo length bit 1", false,
> +		    testBit(debugControlRegister, 19 + (i * 4)));
> +	}
> +	
> +	for (int j=0; j < 4; j++) {	    
> +	    wp.deleteWatchpoint(task,j);
> +	    assertEquals("Deleted Watchpoint is 0", 
> +		    wp.readWatchpoint(task, j),0);
> +	}
> +
> +	assertEquals("Debug control register is left as we originally found it", 
> +		savedDebugControlRegister,wp.readControlRegister(task));
> +	
> +    }
> +
> +
> +    public void testWatchTwoByteBitPattern() {
> +	// Test two byte bit pattern AND test local exact only.
> +	// Test bit pattern in a cumulative fasion
> +	// across all WP registers. Assume local exact and 
> +	// but read/write bit is set. At end, delete all watchpoints
> +	// and ensure debug control register is as we found it.
> +	if (unresolvedOnPPC(5991)) 
> +	    return;
> +	Proc proc = giveMeABlockedProc();
> +	Task task = proc.getMainTask();
> +	long address = 0x0;
> +	long debugControlRegister;	
> +	Watchpoint wp = WatchpointFactory.getWatchpoint(task.getISA());
> +	long savedDebugControlRegister = wp.readControlRegister(task);
> +
> +	for (int i=0; i<4; i++) {
> +	    wp.setWatchpoint(task, i, address, 1, false, true);
> +
> +	    // simple first test. Does register contain address?
> +	    assertEquals("Saved watchpoint and address are similar", 
> +		 address, wp.readWatchpoint(task, i));
> +	    
> +	    debugControlRegister = wp.readControlRegister(task);
> +	
> +	    // Test Debug Control Register Bit Pattern. Local Exact.
> +	    assertEquals(i + " wp local exact bit", true, 
> +		    testBit(debugControlRegister, 0 + (i*2)));
> +	    assertEquals(i + " wp global exact bit", false, 
> +		    testBit(debugControlRegister,  1 + (i*2)));
> +
> +	    // Test Debug Control Register. Fire on Read and Write
> +	    assertEquals(i + "wp r/w bit 0", true, 
> +		    testBit(debugControlRegister, 16 + (i * 4)));
> +	    assertEquals(i + "wp r/w bit 1 ", true, 
> +		    testBit(debugControlRegister, 17 + (i * 4)));
> +
> +	    // Test Debug Control Register. Test Length four bytes.
> +	    assertEquals(i +"wp length bit 0", false, 
> +		    testBit(debugControlRegister, 18 + (i * 4)));
> +	    assertEquals(i + "wo length bit 1", false ,
> +		    testBit(debugControlRegister, 19 + (i * 4)));
> +	}
> +	
> +	for (int j=0; j < 4; j++) {	    
> +	    wp.deleteWatchpoint(task,j);
> +	    assertEquals("Deleted Watchpoint is 0", 
> +		    wp.readWatchpoint(task, j),0);
> +	}
> +
> +	assertEquals("Debug control register is left as we originally found it", 
> +		savedDebugControlRegister,wp.readControlRegister(task));
> +
> +    }
> +
> +    private boolean testBit(long register, int bitToTest) {
> +	return (register & (1L << bitToTest)) != 0;
> +    }
> +    
> +    private Proc giveMeABlockedProc ()
> +    {
> +      String[] nocmds = {};
> +      DaemonBlockedAtEntry ackProc = new DaemonBlockedAtEntry(nocmds);
> +      assertNotNull(ackProc);
> +      return ackProc.getMainTask().getProc();
> +    }
> +
> +}    
> +
> diff --git a/frysk-core/frysk/sysroot/SysRootCache.java b/frysk-core/frysk/isa/watchpoints/Watchpoint.java
> similarity index 53%
> copy from frysk-core/frysk/sysroot/SysRootCache.java
> copy to frysk-core/frysk/isa/watchpoints/Watchpoint.java
> index 1057198..fc8c234 100644
> --- a/frysk-core/frysk/sysroot/SysRootCache.java
> +++ b/frysk-core/frysk/isa/watchpoints/Watchpoint.java
> @@ -1,4 +1,5 @@
>  // This file is part of the program FRYSK.
> +//
>  // Copyright 2008, Red Hat Inc.
>  //
>  // FRYSK is free software; you can redistribute it and/or modify it
> @@ -36,84 +37,69 @@
>  // version and license this file solely under the GPL without
>  // exception.
>  
> -package frysk.sysroot;
> +package frysk.isa.watchpoints;
>  
> -import java.io.File;
> -import java.util.WeakHashMap;
>  
>  import frysk.proc.Task;
>  
> -/**
> - * Map from a Task's executable to its special root directory.
> - */
>
>
> hooks/post-receive
> --
> frysk system monitor/debugger
>   

           reply	other threads:[~2008-03-28 15:05 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <20080328145630.18231.qmail@sourceware.org>]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=47ED0938.4050507@redhat.com \
    --to=pmuldoon@redhat.com \
    --cc=frysk@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).