Bug 14531

Summary: gdb.base/dprintf.exp fails in newlib environment
Product: gdb Reporter: Yufeng Zhang <yufeng.zhang>
Component: testsuiteAssignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: normal CC: pedro, qiyao
Priority: P2    
Version: HEAD   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Yufeng Zhang 2012-08-30 17:30:57 UTC
I have seen the following failures in a bare-metal environment with newlib:

set dprintf-function fprintf^M
(gdb) PASS: gdb.base/dprintf.exp: Set dprintf function
set dprintf-channel stderr^M
(gdb) PASS: gdb.base/dprintf.exp: Set dprintf channel
jump *start^M
Line 0 is not in `foo'.  Jump anyway? (y or n) y^M
Continuing at 0x400178.^M
^M
Breakpoint 2, main (argc=1, argv=0x2041bfe0) at gdb/testsuite/gdb.base/dprintf.c:33^M
33        int loc = 1234;^M
(gdb) continue^M
Continuing.^M
kickoff 1234^M
also to stderr 1234^M
No symbol "stderr" in current context.^M
(gdb) FAIL: gdb.base/dprintf.exp: 1st dprintf, fprintf
continue^M
Continuing.^M
No symbol "stderr" in current context.^M
(gdb) FAIL: gdb.base/dprintf.exp: 2nd dprintf, fprintf
---------------------- CUT ----------------------

The reason of the failure is because that 'stderr' in the newlib has the following definition:

#ifndef _REENT_ONLY
#define stdin   (_REENT->_stdin)
#define stdout  (_REENT->_stdout)
#define stderr  (_REENT->_stderr)
#else /* _REENT_ONLY */
#define stdin   (_impure_ptr->_stdin)
#define stdout  (_impure_ptr->_stdout)
#define stderr  (_impure_ptr->_stderr)
#endif /* _REENT_ONLY */

while in the GNU/Linux env, there are 'stderr's defined as both the macro and variable:

/* Standard streams.  */
extern struct _IO_FILE *stdin;          /* Standard input stream.  */
extern struct _IO_FILE *stdout;         /* Standard output stream.  */
extern struct _IO_FILE *stderr;         /* Standard error output stream.  */
#ifdef __STDC__
/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin
#define stdout stdout
#define stderr stderr
#endif

This is probably why this test doesn't fail in GNU/Linux but newlib.  I think we need a more robust way to test 'dprintf' with 'set dprintf-function fprintf' and 'set dprintf-channel stderr'.  The C standard has specified that 'stderr' is just a macro.
Comment 1 Yao Qi 2012-09-08 14:39:26 UTC
(In reply to comment #0)
> I have seen the following failures in a bare-metal environment with newlib:
> 
> set dprintf-function fprintf^M
> (gdb) PASS: gdb.base/dprintf.exp: Set dprintf function
> set dprintf-channel stderr^M
> (gdb) PASS: gdb.base/dprintf.exp: Set dprintf channel
> jump *start^M
> Line 0 is not in `foo'.  Jump anyway? (y or n) y^M
> Continuing at 0x400178.^M
> ^M
> Breakpoint 2, main (argc=1, argv=0x2041bfe0) at
> gdb/testsuite/gdb.base/dprintf.c:33^M
> 33        int loc = 1234;^M
> (gdb) continue^M
> Continuing.^M
> kickoff 1234^M
> also to stderr 1234^M
> No symbol "stderr" in current context.^M
> (gdb) FAIL: gdb.base/dprintf.exp: 1st dprintf, fprintf
> continue^M
> Continuing.^M
> No symbol "stderr" in current context.^M
> (gdb) FAIL: gdb.base/dprintf.exp: 2nd dprintf, fprintf

I'd like to treat it as an incompleteness of test case here.  IMO, command 'set dprintf-channel' gives user the freedom to set the right channel to display messages, so the user has to set the right channel.

The test should be smart to enough to 'set dprintf-channel' correctly if 'stderr' doesn't exist at all.

Maybe we can do 'set dprintf-channel _impure_ptr->_stderr' for newlib??
Comment 2 Pedro Alves 2012-09-10 13:27:13 UTC
How about adding a function like 

 FILE *get_stderr () { return stderr; }

to the test's .c file, and do 

  set dprintf-function fprintf
  set dprintf-channel get_stderr()

instead of 

  set dprintf-function fprintf
  set dprintf-channel stderr

IOW, avoid the need of macro debug info for all targets.

AFAICS, the code does:

  else if (strcmp (dprintf_style, dprintf_style_call) == 0)
    {
      if (!dprintf_function)
        error (_("No function supplied for dprintf call"));

      if (dprintf_channel && strlen (dprintf_channel) > 0)
        printf_line = xstrprintf ("call (void) %s (%s,%s)",
                                  dprintf_function,
                                  dprintf_channel,
                                  dprintf_args);
      else
        printf_line = xstrprintf ("call (void) %s (%s)",
                                  dprintf_function,
                                  dprintf_args);
    }

So that'll expand to one infcall to

  fprintf (get_stderr (), ...)

instead of:

  fprintf (stderr, ...)

Which is the same thing -- still one infcall.
Comment 3 Yufeng Zhang 2012-09-18 09:49:03 UTC
(In reply to comment #1)
> Maybe we can do 'set dprintf-channel _impure_ptr->_stderr' for newlib??

This is not ideal solution as 'stderr' is not always _impure_ptr->_stderr in newlib; see newlib/libc/include/stdio.h:

#ifndef _REENT_ONLY
#define stdin   (_REENT->_stdin)
#define stdout  (_REENT->_stdout)
#define stderr  (_REENT->_stderr)
#else /* _REENT_ONLY */
#define stdin   (_impure_ptr->_stdin)
#define stdout  (_impure_ptr->_stdout)
#define stderr  (_impure_ptr->_stderr)
#endif /* _REENT_ONLY */

(In reply to comment #2)
> How about adding a function like 
> 
>  FILE *get_stderr () { return stderr; }
> 
> to the test's .c file, and do 
[snip]

Sound like a good idea!