Re: [RFC] "single step" atomic instruction sequences as a whole on PPC

Emi SUZUKI
2007-05-10T10:57:31Z

[-- Attachment #1: Type: Text/Plain, Size: 4662 bytes --]

Hello Jan,

From: Jan Kratochvil <jan.kratochvil at redhat.com>
Subject: Re: [RFC] "single step" atomic instruction sequences as a whole on PPC
Date: Wed, 09 May 2007 20:33:19 +0200

> please check the attached two testcases and run them at least 100x etc.
>
> Unfortunately the threaded one fails for me in some 7% of cases IMO due to
> a race at the `infrun.c' line:
> remove_status = remove_breakpoints ();

I'm not sure, but I guess the issue you may see on your testcase
resembles to the session log below:

--
GNU gdb 6.6.50.20070510-cvs
Copyright (C) 2007 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "powerpc64-linux"...
Using host libthread_db library "/lib64/libthread_db.so.1".
(gdb) tb 161
Breakpoint 1 at 0x10001230: file gdb/testsuite/gdb.threads/atomic-seq-threaded.c, line 161.
(gdb) r
Starting program: gdb/testsuite/gdb.threads/atomic-seq-threaded
[Thread debugging using libthread_db enabled]
[New Thread 4160631792 (LWP 11609)]
[New Thread 4160627904 (LWP 11612)]
[Switching to Thread 4160631792 (LWP 11609)]
main (argc=1, argv=0xffe92484)
at gdb/testsuite/gdb.threads/atomic-seq-threaded.c:161
161 assert (i == 0); /* _create_behind_ */
(gdb) b 167
Breakpoint 2 at 0x1000126c: file gdb/testsuite/gdb.threads/atomic-seq-threaded.c, line 167.
(gdb) b 151
Breakpoint 3 at 0x100011d0: file gdb/testsuite/gdb.threads/atomic-seq-threaded.c, line 151.
(gdb) b 133
Breakpoint 4 at 0x10001120: file gdb/testsuite/gdb.threads/atomic-seq-threaded.c, line 133.
(gdb) set can-use-hw-watchpoints 0
(gdb) watch unused
Watchpoint 5: unused
(gdb) c
Continuing.
[Switching to Thread 4160627904 (LWP 11612)]

Breakpoint 4, start1 (arg=0x0)
at gdb/testsuite/gdb.threads/atomic-seq-threaded.c:133
133 return arg; /* _delete1_ */
(gdb) d
Delete all breakpoints? (y or n) y
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 4160631792 (LWP 11609)]
0x0ff3b748 in __libc_enable_asynccancel () from /lib/libc.so.6
(gdb) disassemble 0x0ff3b734 0x0ff3b750
Dump of assembler code from 0xff3b734 to 0xff3b750:
0x0ff3b734 <__libc_enable_asynccancel+52>: addi r9,r11,100
0x0ff3b738 <__libc_enable_asynccancel+56>: lwarx r10,0,r9
0x0ff3b73c <__libc_enable_asynccancel+60>: cmpw r10,r3
0x0ff3b740 <__libc_enable_asynccancel+64>: bne- 0xff3b74c <__libc_enable_asynccancel+76>
0x0ff3b744 <__libc_enable_asynccancel+68>: stwcx. r0,0,r9
0x0ff3b748 <__libc_enable_asynccancel+72>: bne- 0xff3b738 <__libc_enable_asynccancel+56>
0x0ff3b74c <__libc_enable_asynccancel+76>: isync
End of assembler dump.
--

If it is, the line in `infrun.c' you have pointed is not the cause.
But whether it is or not, I think there is an issue for handling
multiple trap events while doing software single stepping.

In the testcase, GDB always does hardware single stepping by setting a
software watchpoint. And when a thread running through an atomic
sequence of instruction, it will be done by software one. So, during
one thread is running through an atomic sequence of instructions, GDB
can detects multiple SIGTRAP events on the target process, as you have
already known.

When that multiple SIGTRAP events occured, GDB selects one event and
cancels the other if the cause of SIGTRAP is a breakpoint hit, or just
leave it pended. The procedure will be done by `cancel_breakpoints_callback'
in linux-nat.c. And the pended events will be deteced and noticed
when the next time the target resumes.

The probrem is that GDB doesn't check if the breakpoint is inserted
for software single stepping when cancelling the trap event: when the
event occured by a software single step breakpoint is not selected,
GDB would not cancel it but leave it pended.

When the next time the target resumes, GDB restores the pended event.
But if you have removed the watchpoint that the target get stopped by
before resuming, GDB can never decide the cause of SIGTRAP anymore.
The session log above shows the phenomenon.

I attach the source code modified from yours, Jan, which is much
easier to reproduce the issue on my environment. And I'm so sorry for
not sending any patch for resolving it, for my copyright assignment to
FSF is STILL NOT ready.

And Luis, it's just FYI: 'Emi' is one of common female names in
Japan :-)

--
Emi SUZUKI / emi-suzuki at tjsys.co.jp

[-- Attachment #2: atomic-seq-threaded.c --]
[-- Type: Text/Plain, Size: 4294 bytes --]

/* This testcase is part of GDB, the GNU debugger.

Copyright 2007 Free Software Foundation, 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 Street, Fifth Floor, Boston,
MA 02110-1301, USA. */

/* Test stepping over RISC atomic sequences.
This variant testcases the code for stepping another thread while skipping
over the atomic sequence in the former thread
(STEPPING_PAST_SINGLESTEP_BREAKPOINT).
Code comes from gcc/testsuite/gcc.dg/sync-2.c */

/* { dg-options "-march=i486" { target { { i?86-*-* x86_64-*-* } && ilp32 } } } */
/* { dg-options "-mcpu=v9" { target sparc*-*-* } } */

/* Test functionality of the intrinsics for 'short' and 'char'. */

#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>

#define LOOPS 2

static int unused;

static char AI[18];
static char init_qi[18] = { 3,5,7,9,0,0,0,0,-1,0,0,0,0,0,-1,0,0,0 };
static char test_qi[18] = { 3,5,7,9,1,4,22,-12,7,8,9,7,1,-12,7,8,9,7 };

static void
do_qi (void)
{
if (__sync_fetch_and_add(AI+4, 1) != 0)
abort ();
if (__sync_fetch_and_add(AI+5, 4) != 0)
abort ();
if (__sync_fetch_and_add(AI+6, 22) != 0)
abort ();
if (__sync_fetch_and_sub(AI+7, 12) != 0)
abort ();
if (__sync_fetch_and_and(AI+8, 7) != (char)-1)
abort ();
if (__sync_fetch_and_or(AI+9, 8) != 0)
abort ();
if (__sync_fetch_and_xor(AI+10, 9) != 0)
abort ();
if (__sync_fetch_and_nand(AI+11, 7) != 0)
abort ();

if (__sync_add_and_fetch(AI+12, 1) != 1)
abort ();
if (__sync_sub_and_fetch(AI+13, 12) != (char)-12)
abort ();
if (__sync_and_and_fetch(AI+14, 7) != 7)
abort ();
if (__sync_or_and_fetch(AI+15, 8) != 8)
abort ();
if (__sync_xor_and_fetch(AI+16, 9) != 9)
abort ();
if (__sync_nand_and_fetch(AI+17, 7) != 7)
abort ();
}

static short AL[18];
static short init_hi[18] = { 3,5,7,9,0,0,0,0,-1,0,0,0,0,0,-1,0,0,0 };
static short test_hi[18] = { 3,5,7,9,1,4,22,-12,7,8,9,7,1,-12,7,8,9,7 };

static void
do_hi (void)
{
if (__sync_fetch_and_add(AL+4, 1) != 0)
abort ();
if (__sync_fetch_and_add(AL+5, 4) != 0)
abort ();
if (__sync_fetch_and_add(AL+6, 22) != 0)
abort ();
if (__sync_fetch_and_sub(AL+7, 12) != 0)
abort ();
if (__sync_fetch_and_and(AL+8, 7) != -1)
abort ();
if (__sync_fetch_and_or(AL+9, 8) != 0)
abort ();
if (__sync_fetch_and_xor(AL+10, 9) != 0)
abort ();
if (__sync_fetch_and_nand(AL+11, 7) != 0)
abort ();

if (__sync_add_and_fetch(AL+12, 1) != 1)
abort ();
if (__sync_sub_and_fetch(AL+13, 12) != -12)
abort ();
if (__sync_and_and_fetch(AL+14, 7) != 7)
abort ();
if (__sync_or_and_fetch(AL+15, 8) != 8)
abort ();
if (__sync_xor_and_fetch(AL+16, 9) != 9)
abort ();
if (__sync_nand_and_fetch(AL+17, 7) != 7)
abort ();
}

static void *
start1 (void *arg)
{
unsigned loop;
sleep(1);

for (loop = 0; loop < LOOPS; loop++)
{
memcpy(AI, init_qi, sizeof(init_qi));

do_qi ();

if (memcmp (AI, test_qi, sizeof(test_qi)))
abort ();
}

return arg; /* _delete1_ */
}

static void *
start2 (void *arg)
{
unsigned loop;

for (loop = 0; loop < LOOPS; loop++)
{
memcpy(AL, init_hi, sizeof(init_hi));

do_hi ();

if (memcmp (AL, test_hi, sizeof(test_hi)))
abort ();
}

return arg; /* _delete2_ */
}

int
main (int argc, char **argv)
{
pthread_t thread;
int i;

i = pthread_create (&thread, NULL, start1, NULL); /* _create_ */
assert (i == 0); /* _create_behind_ */

sleep(1);

start2 (NULL);

i = pthread_join (thread, NULL); /* _delete_ */
assert (i == 0);

return 0; /* _success_ */
}