GDB - Display output of target application in a separate window - c

I'm using GDB to debug some of my C applications. What I currently do is load the target application, set a breakpoint on line 30 and run it.
I would like to make GDB display the output of my own application in a new terminal window while I'm still able to control the breakpoint handling via the GDB terminal window, but I couldn't seem to find a proper switch. Is there any way making GDB display my program's output in its own window?

For people wondering how to use the GDB tty command here is a short description...
Open a new console window. We will redirect output from the program running under GDB here. This is our output window.
Run the tty command in the output window. This will show the name of the tty being used by the underlying console.
$ tty
/dev/pts/4
Open another console window and start GDB here. Let us call this the GDB window.
Now run the tty command in GDB using the tty file name obtained above and then start the debugging process.
(gdb) tty /dev/pts/4
(gdb) run
Now you should be able to see the program output separately in the output window.
Note: The GDB set new-console on command does not work on Linux! It is meant to be run on windows only. Use the tty method described above on Linux.

You can use set new-console on to accomplish this as shown here.

Another way to do this would be to start your target program with gdbserver (assuming it is available to you). Then you can connect GDB started in a separate window to gdbserver.
GNU gdbserver documentation
From window A:
gdbserver :12345 myprog [args...]
From window B:
gdb myprog
GNU gdb 6.6
...
(gdb) target remote localhost:12345
Remote debugging using localhost:12345
0x009867c0 in ?? ()
(gdb) b main
Breakpoint 1 at 0x804834a: file myprog.c, line 40.
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0xffff8904) at myprog.c:40
40 int i = 1;
(gdb)

The best way I know is to redirect the output of the program to a file, and then tail -f that file in another terminal. Redirection is done with run > filename, as documented in the GDB documentation.

GDB's tty command does work, but it doesn't work well with interactive programs, like if you wanted to debug bash. Even for non-interactive programs, you get the following:
warning: GDB: Failed to set controlling terminal: Operation not permitted
I wrote a small program to fix both of these issues:
// Open a pty and let it idle, so that a process spawned in a different window
// can attach to it, start a new session, and set it as the controlling
// terminal. Useful for gdb debugging with gdb's `tty` command.
#include <inttypes.h>
typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64;
typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64;
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <pty.h>
#include <liburing.h>
#define BSIZE 4096
void raw_terminal(void)
{
if (!isatty(0))
return;
struct termios t;
tcgetattr(0, &t);
t.c_lflag &= ~(ISIG | ICANON | ECHO);
tcsetattr(0, TCSANOW, &t);
}
// Refers to the state of a Joint /while it's waiting in io_uring_enter/.
enum State {
READ,
WRITE
};
// Joins two fds together, like splice, but not a syscall and works on any two
// fds.
struct Joint {
u8 buf[BSIZE];
i32 ifd;
i32 ofd;
enum State state;
u32 nread;
};
void roll_joint(struct Joint *j, struct io_uring *ur, i32 ifd, i32 ofd)
{
j->ifd = ifd;
j->ofd = ofd;
j->state = READ;
struct io_uring_sqe *sqe = io_uring_get_sqe(ur);
io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(ur);
}
i32 main(i32 argc, char **argv)
{
raw_terminal();
struct io_uring ur;
assert(io_uring_queue_init(256, &ur, 0) == 0);
i32 ptm, pts;
assert(openpty(&ptm, &pts, NULL, NULL, NULL) == 0);
dprintf(2, "pid = %u tty = %s\n", getpid(), ttyname(pts));
struct Joint jkbd;
roll_joint(&jkbd, &ur, 0, ptm);
struct Joint jscreen;
roll_joint(&jscreen, &ur, ptm, 1);
for (;;) {
struct io_uring_cqe *cqe;
for (;;) {
// Actions like suspend to RAM can interrupt the io_uring_enter
// syscall. If we get interrupted, try again. For all other errors,
// bail. Also, wait_cqe negates the error for no reason. It never
// returns positive numbers. Very silly.
u32 res = -io_uring_wait_cqe(&ur, &cqe);
if (res == 0)
break;
else if (res != EINTR) {
dprintf(2, "io_uring_enter returns errno %d\n", res);
exit(res);
}
}
struct Joint *j = io_uring_cqe_get_data(cqe);
if (j->state == READ) {
// Exiting READ state. Finish with the read...
j->nread = cqe->res;
assert(j->nread > 0);
// Now, start the write.
j->state = WRITE;
struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
io_uring_prep_write(sqe, j->ofd, j->buf, j->nread, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(&ur);
}
else if (j->state == WRITE) {
// Exiting WRITE state. Finish with the write...
i64 nwritten = cqe->res;
assert(nwritten == j->nread);
// Now, start the read.
j->state = READ;
struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(&ur);
}
io_uring_cqe_seen(&ur, cqe);
}
io_uring_queue_exit(&ur);
return 0;
}
Suppose you save the program to idleterm.c. To compile it:
> gcc -o idleterm idleterm.c -luring
To use it, start one terminal window, and on that window, run idleterm. It will print the name of the tty to attach to:
> ./idleterm
pid = 3405922 tty = /dev/pts/0
█
Copy that tty path and paste it into a gdb session in a second window:
> gdb bash
Reading symbols from bash...
(No debugging symbols found in bash)
(gdb) tty /dev/pts/0
(gdb) r
Starting program: /usr/bin/bash
…
A bash prompt will appear in the first window. All of the interactions with bash requiring special TTY behaviour will work normally, including ^C, ^Z, etc.
idleterm passes all keyboard input through to the child process being debugged, including ^C and ^Z. So in order to stop idleterm, you'll have to kill it from a separate window. This is why idleterm prints its pid. Copy the pid, then paste it into the kill command:
> kill 3405922
If there's only one instance of idleterm running on the system, you can of course just use killall.

Just use tty command. If you want output of your program redirected to /dev/pts/5 type:
tty /dev/pts/5

With lldb on Mac the following runs the program in w new terminal window while the debugger controls from the original window:
$ lldb <prog>
(lldb) b main # if you want to set a breakpoint
(lldb) process launch --tty -- <args>
This runs the program as a process in specified tty (terminal window):
$ tty # (in other window, get tty name)
/dev/ttys002
$ lldb
(lldb) b main # if you want to set a breakpoint
(lldb) process launch --tty=/dev/ttys002 -- <args>

Related

Is there any way to prevent a background process from writing to the stdout channel when the shell is reading a command?

I'm writing a shell in C. The shell has internal and external commands. The external commands can be extended with an ampersand (&) so they run in the background.
When I type e.g program&, the program executes with no problem in the background, making the shell available while the program is executing.
But, the output of the program can get mixed with the normal output of the shell. And that's what I'm trying to fix, with no success.
Note: In the example above I talked about program as any other command. The program basically sleeps and then prints "Hello, World!". Also, "program" is in /bin/, which is my default directory of external commands.
sish> pwd
[current directory]
sish> program&
sish> [now if I don't type anything...] Hello, World!
[now the current command is being written here].
For example, in bash, this behavior is different, it seems that, if the user is "still" writing the command, the output of the background program is holding (or waiting), so the user can execute a program. Hence, not messing with the output.
I read a bit about signals, I tried to setup a signal handler that printed something on SIGCHLD but it also had the same behavior.
CODE:
while(1) {
int internal=0;
int background=0;
int redirect=0; // 1 - output ; 2 - input
// those variables are not important for the question
printf("sish> ");
fgets(command,BUFFER_SIZE,stdin);
}
... (some lines that are not important for the question)
child = fork(); // pid_t child -> global variable
if(!child) {
if(redirect==1) {
int fd = open(words[2],O_WRONLY|O_CREAT|O_TRUNC,0660);
dup2(fd,1);
execlp(words[0],words[0],NULL);
}
else if(redirect==2) {
int fd = open(words[2],O_RDWR);
dup2(fd,0);
execlp(words[0],words[0],NULL);
}
else {
if(execvp(bin_path,words)==-1) {
printf("Error! Does the program exist?\n");
}
}
}
NOTES:
- I KNOW I'M NOT CHECKING FOR ERRORS IN THE FORK, I WILL ADD THAT WHEN I SOLVE THIS BUG.
- I ALSO PRINTED THE STDERR WITH perror, I GOT NOTHING.
I expect this (e.g):
sish> pwd
[current directory]
sish> program&
sish> [I wait...] pwd
[current directory]
Hello, World!

Prevent read() systemcall returing with 0 when run as background process

I have a piece of software that is able to read commands from stdin for debug purposes in a separate thread. When my software runs as foreground process read behaves as expected, its blocking and waits for input by the user, i.e the thread sleeps.
When the software is run as a background process, read constantly returns 0 (possible EOF detected?).
The problem here is, that this specific read is in a while(true) loop. It runs as fast as it can and steals precious CPU load on my embedded device.
I tried redirecting /dev/null to the process but the behavior was the same. I am running my custom Linux on an ARM Cortex A5 board.
The problematic piece of code follows and is run inside its own thread:
char bufferUserInput[256];
const int sizeOfBuffer = SIZE_OF_ARRAY(bufferUserInput);
while (1)
{
int n = read(0, bufferUserInput, sizeOfBuffer); //filedes = 0 equals to reading from stdin
printf("n is: %d\n", n);
printf("Errno: %s",strerror(errno));
if (n == 1)
{
continue;
}
if ((1 < n)
&& (n < sizeOfBuffer)
&& ('\n' == bufferUserInput[n - 1]))
{
printf("\r\n");
bufferUserInput[n - 1] = '\0';
ProcessUserInput(&bufferUserInput[0]);
} else
{
n = 0;
}
}
I am looking for a way to prevent read from constantly returning when running in the background and wait for user input (which of course will never come).
If you start your program in the "background" (as ./program &) from a shell script, it's stdin will be redirected from /dev/null (with some exceptions).
Trying to read from /dev/null will always return 0 (EOF).
Example (on linux):
sh -c 'ls -l /proc/self/fd/0 & wait'
... -> /dev/null
sh -c 'dd & wait'
... -> 0 bytes copied, etc
The fix from the link above should also work for you:
#! /bin/sh
...
exec 3<&0
./your_program <&3 &
...
When stdin is not a terminal, read is returning with 0 because you are at the end of the file. read only blocks after reading all available input when there could be more input in the future, which is considered to be possible for terminals, pipes, sockets, etc. but not for regular files nor for /dev/null. (Yes, another process could make a regular file bigger, but that possibility isn't considered in the specification for read.)
Ignoring the various problems with your read loop that other people have pointed out (which you should fix anyway, as this will make reading debug commands from the user more reliable) the simplest change to your code that will fix the problem you're having right now is: check on startup whether stdin is a terminal, and don't launch the debug thread if it isn't. You do that with the isatty function, declared in unistd.h.
#include <stdio.h>
#include <unistd.h>
// ...
int main(void)
{
if (isatty(fileno(stdin)))
start_debug_thread();
// ...
}
(Depending on your usage context, it might also make sense to run the debug thread when stdin is a pipe or a socket, but I would personally not bother, I would rely on ssh to provide a remote (pseudo-)terminal when necessary.)
read() doesn't return 0 when reading from the terminal in a backgrounded process.
It either continues to block while causing a SIGTTIN to be sent to the process (which may break the blocking and cause retval=-1,errno=EINTR to be returned or it causes retval=-1, errno EIO if SIGTTIN is ignore.
The snippet below demonstrates this:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main()
{
char c[256];
ssize_t nr;
signal(SIGTTIN,SIG_IGN);
nr = read(0,&c,sizeof(c));
printf("%zd\n", nr);
if(0>nr) perror(0);
fflush(stdout);
}
The code snippet you've shown can't possibly test reveal 0-returns since you never test for zero-ness in the return value.

Communication between interactive ocaml interpreter and another process

I need to load a *.ml file into the Ocaml toplevel (the interactive interpreter, when you type 'ocaml' in a shell) and then send an instruction from a Matlab process, get back the result of the instruction, send back another instruction, ...
I've made this C program. The parent process gets the Matlab's instruction from a named pipe, sends it to the child process (with ocaml running) and gets the response back so it can send it to Matlab.
But there is some kind of bug: when I send an instruction, I get back some weird characters, I send another instruction and then I receive the response of the first instruction...
(I didn't copy the perror() test to have less text)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
// Parent -> Child
int pipe_in[2];
// Child -> parent
int pipe_out[2];
/*
pipe[0] = output
pipe[1] = input
*/
pipe(pipe_in);
pipe(pipe_out);
pid_t pid;
if ((pid = fork()) == 0) {
// CHILD SIDE
close(pipe_in[1]);
close(pipe_out[0]);
dup2(pipe_in[0], STDIN_FILENO);
dup2(pipe_out[1], STDOUT_FILENO);
dup2(pipe_out[1], STDERR_FILENO);
close(pipe_in[0]);
close(pipe_out[1]);
char *args[] = {"ocaml", NULL};
execvp("ocaml", args);
printf("FAIL\n");
exit(EXIT_FAILURE);
} else {
// PARENT SIDE
printf("[*] PID : %d\n", (int) pid);
close(pipe_in[0]);
close(pipe_out[1]);
char cmd[1024];
char feedback[1024];
ssize_t cmd_read;
ssize_t feedback_read = sizeof(feedback);
while (1) {
// Get the instruction from Matlab.
printf("[>] ");
int fifo_in = open("/tmp/pipe_in", O_RDONLY);
cmd_read = read(fifo_in, cmd, sizeof(cmd));
close(fifo_in);
printf("%s\n", cmd);
// Send the instruction to the ocaml interpreter.
write(pipe_in[1], cmd, cmd_read);
// Read the response of the ocaml interpreter.
while (feedback_read == sizeof(feedback)) {
feedback_read = read(pipe_out[0], feedback, sizeof(feedback));
printf("[-] %d\n", (int) feedback_read);
}
printf("[<] %s\n", feedback);
// Send to Matlab the response.
int fifo_out = open("/tmp/pipe_out", O_WRONLY);
write(fifo_out, feedback, feedback_read);
close(fifo_out);
cmd_read = 0;
feedback_read = sizeof(feedback);
}
close(pipe_in[1]);
close(pipe_out[0]);
}
}
I compile the code with gcc -Wall -std=c99 -o tphr tphr.c
I run the programm in one shell and in another :
> printf 'let x = 10;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
OCaml version 4.03.0
# %
> printf 'let y = 5;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
val x : int = 10
# %
How can I fix the result ?
If, by "weird characters", you mean
OCaml version 4.03.0
that is simply because this is what the OCaml toplevel prints out on startup. So you need to read this line when your own program starts up.
If you mean the # symbol, also known as the prompt, you can turn it off by running ocaml -nopromt.
You don't want to run the interactive ocaml toplevel here for multiple reasons:
the toplevel parses the configfiles of the user and then loads different modules. This can change the available values, change behaviour, and make your matlab process get different results per user.
the output of the toplevel may change between versions making it difficult to parse and return the right reply to matlab
Did you know that you can call the toplevel from ocaml bytecode to interprete strings? I suggest dumping the C code and writing ocaml byte code to read from the pipe, interpret the command and reply with the result.

opening a C program in new terminal window

gcc version 5.3.0 20151204 (Ubuntu 5.3.0-3ubuntu1~14.04)
I read this and and I find this line:
int exit_status = system("gnome-terminal");
so when I add it to my code it only open a new terminal window (well that's what he was asking for) but my program runs in the old one.
is there any way to run my program in a new terminal window.
and also when the program finish executing, the terminal window get closed like I typed the exit command
system("gnome-terminal"); will run the given command, wait for it to exit, and then continue with your program. That's why your program continues to run in the current terminal window.
Rather than trying to do this in C, it probably makes more sense to write a shell script wrapper for your program, and use that script to launch your program in a new terminal window:
#!/bin/bash
gnome-terminal -e ./your-program-name your program arguments
Make the script executable (chmod +x script-name), and then you can run it just like you would a C program. You can even have it forward the arguments from the script to your actual program:
#!/bin/bash
gnome-terminal -e ./your-program-name "$#"
Note that rather than using gnome-terminal (which assumes the user has gnome installed), you can use the more neutral x-terminal-emulator command instead (see How can I make a script that opens terminal windows and executes commands in them?).
If you really want to do this from your C program, then I'd recommend doing something like this:
#include <stdio.h>
#include <stdlib.h>
char cmd[1024];
int main(int argc, char *argv[]){
// re-launch in new window, if needed
char *new_window_val = getenv("IN_NEW_WINDOW");
const char *user_arg = argc < 2 ? "" : argv[1];
if (!new_window_val || new_window_val[0] != '1') {
snprintf(cmd, sizeof(cmd), "gnome-terminal -e IN_NEW_WINDOW=1 %s %s", argv[0], user_arg);
printf("RELAUNCH! %s\n", cmd);
return system(cmd);
}
// do normal stuff
printf("User text: %s\n", argv[1]);
return 0;
}
Using an environment variable (IN_NEW_WINDOW in this case) to check if you've already launched in a new window should make it so that the new window only opens once. Note that the above code assumes a program with only one argument.
However, I still think using the wrapper script is a better solution.

C: setup pseudoterminal and open with xterm

The following simplified piece of code is executed by a thread in the background. The thread runs until he is told to exit (by user input).
In the code below I have removed some error checking for better readability. Even with error checking the code works well and both the master and the slave are created and/or opened.
...
int master, slave;
char *slavename;
char *cc;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
slave = open(slavename, O_RDWR);
printf("master: %d\n",master);
printf("slavename: %s\n",slavename);
On my machine the output is the following:
master: 3
slavename: /dev/pts/4
So I thought that opening an xterm with the command xterm -S4/3 (4 = pt-slave, 3 = pt-master) while my program is running should open a new xterm window for the created pseudoterminal. But xterm just starts running without giving an error or any further informations but does not open a window at all. Any suggestions on that?
EDIT:
Now with Wumpus Q. Wumbley's help xterm starts normally, but I can't redirect any output to it. I tried:
dup2(slave, 1);
dup2(slave, 2);
printf("Some test message\n");
and opening the slave with fopen and then using fprinf. Both didn't work.
The xterm process needs to get access to the file descriptor somehow. The intended usage of this feature is probably to launch xterm as a child process of the one that created the pty. There are other ways, though. You could use SCM_RIGHTS file descriptor passing (pretty complicated) or, if you have a Linux-style /proc filesystem try this:
xterm -S4/3 3<>/proc/$PID_OF_YOUR_OTHER_PROGRAM/fd/3
'
You've probably seen shell redirection operators before: < for stdin, > for stdout, 2> for stderr (file descriptor 2). Maybe you've also seen other file descriptors being opend for input or output with things like 3<inputfile 4>outputfile. Well the 3<> operator here is another one. It opens file descriptor 3 in read/write mode. And /proc/PID/fd/NUM is a convenient way to access files opened by another process.
I don't know about the rest of the question. I haven't tried to use this mode of xterm before.
OK, the trick with /proc was a bad idea. It's equivalent to a fresh open of /dev/ptmx, creating a new unrelated pty.
You're going to have to make the xterm a child of your pty-creating program.
Here's the test program I used to explore the feature. It's sloppy but it revealed some interesting things. One interesting thing is that xterm writes its window ID to the pty master after successful initialization. This is something you'll need to deal with. It appears as a line of input on the tty before the actual user input begins.
Another interesting thing is that xterm (the version in Debian at least) crashes if you use -S/dev/pts/2/3 in spite of that being specifically mentioned in the man page as an allowed format.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int master;
char *slavename, window[64], buf[64];
FILE *slave;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
printf("master: %d\n", master);
printf("slavename: %s\n", slavename);
snprintf(buf, sizeof buf, "-S%s/%d", strrchr(slavename,'/')+1, master);
if(!fork()) {
execlp("xterm", "xterm", buf, (char *)0);
_exit(1);
}
slave = fopen(slavename, "r+");
fgets(window, sizeof window, slave);
printf("window: %s\n", window);
fputs("say something: ", slave);
fgets(buf, sizeof buf, slave);
fprintf(slave, "you said %s\nexiting in 3 seconds...\n", buf);
sleep(3);
return 0;
}

Resources