Why error message not goes into pipe nor redirected path in ‘crash’?

In the below example, the error always shows in the console.


crash> sym ffffffffa02ef86 > /dev/null
sym: invalid address: ffffffffa02ef86

This ‘sym’ command is implemented in ‘void cmd_sym(void)’ function in crash.


/*
 *  This command may be used to:
 *
 *   1. Translate a symbol to its value.
 *   2. Translate a value to it symbol.
 *   3. List all stored symbols.
 *   4. Query for symbols containing a string.
 *   5. Show the next and previous symbols.
 */
void
cmd_sym(void)
{
    int c;
    struct syment *sp, *spp, *spn;
    ulong value, show_flags; 
    ulong offset;
    int next, prev, multiples, others;
    char *name;
    int errflag;
    char buf[BUFSIZE];

    next = prev = others = 0;
    show_flags = SHOW_LINENUM | SHOW_RADIX();

        while ((c = getopt(argcnt, args, "lLQ:q:npsMm:")) != EOF) {
                switch(c)
        {
        
...
                if (errflag || !in_ksymbol_range(value)) {
                    error(INFO, "invalid address: %s\n",
                        args[optind]);
            ...
            
        
...

The error message is handled in ‘error()’ function which is an alias for ‘__error()’.


/*
 *  General purpose error reporting routine.  Type INFO prints the message
 *  and returns.  Type FATAL aborts the command in progress, and longjmps
 *  back to the appropriate recovery location.  If a FATAL occurs during 
 *  program initialization, exit() is called.
 *
 *  The idea is to get the message out so that it is seen by the user
 *  regardless of how the command output may be piped or redirected.
 *  Besides stderr, check whether the output is going to a file or pipe, and
 *  if so, intermingle the error message there as well.
 */
int
__error(int type, char *fmt, ...)
{
    int end_of_line, new_line;
        char buf[BUFSIZE];
    char *spacebuf;
        void *retaddr[NUMBER_STACKFRAMES] = { 0 };
    va_list ap;

    if (CRASHDEBUG(1) || (pc->flags & DROP_CORE)) {
        SAVE_RETURN_ADDRESS(retaddr);
        console("error() trace: %lx => %lx => %lx => %lx\n",
            retaddr[3], retaddr[2], retaddr[1], retaddr[0]);
    }

    va_start(ap, fmt);
    (void)vsnprintf(buf, BUFSIZE, fmt, ap);
        va_end(ap);

    if (!fmt && FATAL_ERROR(type)) {
        fprintf(stdout, "\n");
...

The output is handled in different ways based on argument configuration. As it is not easy to see which branch is handled, I have to decided to debug it with ‘gdb’. To do that, first need to download and compile crash. Once it’s done, we can start gdb with crash and set breakpoint on ‘__error()’.


$ gdb ./crash
  
(gdb) b __error
Breakpoint 3 at 0x46ed40: __error. (2 locations)

crash> sym ffff92a43386618

Breakpoint 3, __error (type=type@entry=1, 
    fmt=fmt@entry=0x8d1508 "invalid address: %s\n") at tools.c:54
54  {
(gdb) next

(gdb) list
81          new_line = TRUE;
82  
83      if (type == CONT)
84          spacebuf = space(strlen(pc->curcmd));
85      else
86          spacebuf = NULL;
87  
88      if (pc->stdpipe) {
89          fprintf(pc->stdpipe, "%s%s%s %s%s", 
90              new_line ? "\n" : "", 

(gdb) next

(gdb) list
93              type == WARNING ? "WARNING: " : 
94              type == NOTE ? "NOTE: " : "", 
95              buf);
96          fflush(pc->stdpipe);
97      } else { 
98          fprintf(stdout, "%s%s%s %s%s", 
99              new_line || end_of_line ? "\n" : "",
100             type == WARNING ? "WARNING" : 
101             type == NOTE ? "NOTE" : 
102             type == CONT ? spacebuf : pc->curcmd,

We can see that it was trying to run line 98. Let’s check ‘pc’ variable first.


(gdb) p pc
$13 = (struct program_context *) 0xd373a0 

(gdb) p ((struct program_context *) 0xd373a0)->stdpipe
$17 = (FILE *) 0x0
(gdb) p ((struct program_context *) 0xd373a0)->pipefd
$18 = {0, 0}
(gdb) p ((struct program_context *) 0xd373a0)->program_name
$19 = 0x7fffffffe5ef "crash"
(gdb) p ((struct program_context *) 0xd373a0)->console
$20 = 0x0
(gdb) p ((struct program_context *) 0xd373a0)->pipe_pid
$21 = 0
(gdb) p ((struct program_context *) 0xd373a0)->my_tty
$22 = "pts/0\000\000\000\000"

(gdb) set print pretty on
(gdb) set pagination off
(gdb) p *pc
$25 = {
  program_name = 0x7fffffffe5ef "crash", 
  program_path = 0x7fffffffe5d7 "/root/crash/crash-7.2.3/crash", 
  program_version = 0x8b24b3 "7.2.3", 
  gdb_version = 0x918929  "7.6", 
  prompt = 0xfdefa0 "crash> ", 
  flags = 2306687434145989639, 
  namelist = 0xfd48b0 "/usr/lib/debug/lib/modules/3.10.0-957.12.2.el7.x86_64/vmlinux", 
  dumpfile = 0x0, 
  live_memsrc = 0x849230 "/dev/crash", 
  system_map = 0x0, 
  namelist_debug = 0x0, 
  debuginfo_file = 0x0, 
  memory_module = 0x0, 
  memory_device = 0x849230 "/dev/crash", 
  machine_type = 0x84921d "X86_64", 
...
  read_vmcoreinfo = 0x4679e0 
}

So, the reason it doesn’t affected by ‘>’ or by ‘|’ is because the target is always ‘stdout’. One simple way to fix is change it to ‘fp’ which will follow redirection or pipe.


  } else {
/*    fprintf(stdout, "%s%s%s %s%s",  */
    fprintf(fp, "%s%s%s %s%s",
      new_line || end_of_line ? "\n" : "",

Leave a Comment

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.