0

I have a gdb debug function my_debug() in a gdb Python script, which I'd like to run while stopped at a breakpoint, but from different contexts/cases:

  1. either by calling gdb in a sort of a batch mode (though without using --batch or --batch-silent), where I'd want the output of the debug script dumped to terminal/stdout; e.g. with
    "${GDB}" myprogram.exe -q -ex="set confirm off" -ex="set pagination off" -ex="source gdbscript.py" -ex "b compute_fibonacci" -ex "r" -ex="python my_debug()" -ex="exit"
    
  2. or by calling the script in an interactive debug session, where I'd like to fully save its output to a file, without any text printed to terminal; say with:
    (gdb) pipe python my_debug() | cat > my_debug_log.txt
    

I mostly have this working, though there is one problem - I'd like to run the thread X command totally silent/quiet: this works for case 1 - but not for case 2, where I still see "[Switching to thread ...]" message from thread X command being dumped to the file.

I cannot express just how much it pisses me off, that I do everything possible to suppress this text, and it still doesn't happen (at least, not in all cases); it truly causes me irritation to the point of psychological distress! Below is a bash script, test_gdb_ui.sh, that reproduces the problem; my questions are:

  • How come the suppression of the text output of thread X gdb command works in the 1st case, but not in the 2nd? My guess is it has something to do with stream redirection, but I cannot figure out why at this time
  • Is there anything I can do to have the output of thread X suppressed every time the gdb script runs - regardless of the context it runs in?

If you run test_gdb_ui.sh, it will create the following folder and files:

test_gdb_uiout/
├── gdbscript.py
└── myprogram.c

... and then it uses gcc to compile the myprogram.c into myprogram.exe. Finally it runs gdb, once as "batch" command, and at last interactively (where you need to paste command lines, as well as exit gdb, yourself).

Just to show the difference: when you run gdb as a "batch" command, you'll see something like:

Backtraces:

Thread <gdb.InferiorThread id=1.1 target-id="LWP 819043">

#0  clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:62
#1  0x00007ffff7ea4911 in __GI___clone_internal (cl_args= ...
...

Thread <gdb.InferiorThread id=1.2 target-id="LWP 819047">

#0  compute_fibonacci (arg=0x7fffffffde90) at myprogram.c:13
#1  0x00007ffff7e12ac3 in start_thread (arg=...
...

... but if you run interactively with redirect to file, you will see in the text file:

Backtraces:
[Switching to thread 1 (LWP 819052)]
#0  __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=819056, futex_word=0x7ffff7d7a910) at ./nptl/futex-internal.c:57

Thread <gdb.InferiorThread id=1.1 target-id="LWP 819052">

#0  __futex_abstimed_wait_common64 (private=128, ...
...

[Switching to thread 2 (LWP 819056)]
#0  compute_fibonacci (arg=0x7fffffffde90) at myprogram.c:13
13    fibo_task_t* task = (fibo_task_t*) arg;

Thread <gdb.InferiorThread id=1.2 target-id="LWP 819056">

#0  compute_fibonacci (arg=...
...

(darn it, my eyes and head hurt just from seeing that damn "[Switching to thread ...]" again)

What I've found in my research so far (related to my version of gdb, 12.1):

  • The "[Switching to thread ...]" is printed in the print_selected_thread_frame function, by something called uiout (passed as a function argument, of type struct ui_out*) via uiout->text ("[Switching to thread "); in gdb/thread.c
  • There is something called current_ui (in gdb 12.1; in latest versions, it is apparently called current_uiout), which might be related to above uiout, and apparently gets initialized from (stdin, stdout, stderr) (in latest versions, current_uiout gets initialized from gdb_stdout) gdb/main.c
  • When running with --batch-silent, gdb does gdb_stdout = new null_file(); (which is apparently how all output is suppressed) in gdb/main.c

I guess my problem has to do with the interaction of ui_out/gdb_stdout and the "real" stdout - but I cannot say for sure, as I unfortunately find the above confusing enough; so hopefully someone comes along to clarify properly.

Here is test_gdb_ui.sh:

# clean up previous
rm -rf test_gdb_uiout
# start again - generate files
mkdir test_gdb_uiout
cat > test_gdb_uiout/myprogram.c <<'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

// Shared structure to pass input/output to thread
typedef struct {
  int input;
  int result;
} fibo_task_t;

void* compute_fibonacci(void* arg) {
  fibo_task_t* task = (fibo_task_t*) arg;
  int n = task->input;
  if (n <= 1) {
    task->result = n;
  } else {
    int a = 0, b = 1;
    for (int i = 2; i <= n; ++i) {
      int temp = a + b;
      a = b;
      b = temp;
    }
    task->result = b;
  }
  return NULL;
}

int process_data(int number) {
  printf("Processing number in thread: %d\n", number);

  pthread_t thread;
  fibo_task_t task;
  task.input = number;
  task.result = 0;

  if (pthread_create(&thread, NULL, compute_fibonacci, &task) != 0) {
    perror("Failed to create thread");
    exit(1);
  }

  // Wait for thread to finish
  if (pthread_join(thread, NULL) != 0) {
    perror("Failed to join thread");
    exit(1);
  }

  return task.result;
}

int prepare_data() {
  srand((unsigned int) time(NULL));
  int number = (rand() % 31) + 5;  // Range: 5–35
  return process_data(number);
}

int main() {
  int result = prepare_data();
  printf("Fibonacci result: %d\n", result);
  return 0;
}
EOF

cat > test_gdb_uiout/gdbscript.py <<'EOF'
import gdb
import os
import sys

def run_gdb_command_quietly(cmd): # WORKS!
  # Save the original terminal file descriptors
  original_stdout_fd = os.dup(1)
  original_stderr_fd = os.dup(2)
  # Open /dev/null for writing
  null_fd = os.open(os.devnull, os.O_WRONLY)
  # Redirect stdout and stderr to /dev/null
  os.dup2(null_fd, 1)
  os.dup2(null_fd, 2)
  output = gdb.execute(cmd, from_tty=False, to_string=True)
  # Restore the original file descriptors
  os.dup2(original_stdout_fd, 1)
  os.dup2(original_stderr_fd, 2)
  # Close the /dev/null file descriptor
  os.close(null_fd)
  # just in case, return
  return output

def my_debug():
  print()
  print("=== my_debug start " + "="*20)
  print("\nRegisters:")
  print("PC:")
  gdb.execute("p/x $pc")
  print("\nGeneral Registers:")
  gdb.execute("info registers")

  print("\nBacktraces:")

  run_gdb_command_quietly("thread 1")
  current_thread = gdb.selected_thread()
  print(f"\nThread {current_thread}\n")
  btstr = gdb.execute("bt", from_tty=False, to_string=True)
  print(btstr)

  run_gdb_command_quietly("thread 2")
  current_thread = gdb.selected_thread()
  print(f"\nThread {current_thread}\n")
  btstr = gdb.execute("bt", from_tty=False, to_string=True)
  print(btstr)
  print("=== my_debug end   " + "="*20)
  print()
EOF

GDB="/src/gdb-v16.2-static/gdb"

set -x
{ echo "Starting test ..."; } 2>/dev/null
cd test_gdb_uiout

{ echo "Compiling myprogram.c"; } 2>/dev/null
gcc -g -Wall myprogram.c -o myprogram.exe

{ echo "Test command-line \"batch\" gdb"; } 2>/dev/null
"${GDB}" myprogram.exe -q -ex="set confirm off" -ex="set pagination off" -ex="source gdbscript.py" -ex "b compute_fibonacci" -ex "r" -ex="python my_debug()" -ex="exit"

{ echo "Test interactive gdb session: gdb will open,
set breakpoints, and run the program up to the
breakpoint; at that point, please copy/paste and
ENTER the line below at the (gdb) prompt:

pipe python my_debug() | cat > my_debug_log.txt

... and to check the resulting file, (copy/paste) ENTER the line below:

!cat my_debug_log.txt
"; } 2>/dev/null

"${GDB}" myprogram.exe -q -ex="set confirm off" -ex="set pagination off" -ex="source gdbscript.py" -ex "b compute_fibonacci" -ex "r"
2
  • Does InferiorThread.switch () also print that text? Commented Sep 27 at 9:59
  • I think set suppress-cli-notifications on should mostly do what you want. I think there are some edge cases where this doesn't work, e.g. if you switch to the thread that is currently selected it still prints. You can also do with suppress-cli-notifications on -- thread X to avoid having to toggle the setting back and forth. Commented Sep 28 at 14:06

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.