I have a extremely complicated shell script, within which it calls a C++ program I want to debug via GDB. It is extremely hard to separate this c++ program from the shell since it has a lot of branches and a lot of environmental variables setting.
Is there a way to invoke GDB on this shell script? Looks like gdb requires me to call on a C++ program directly.
In addition to options mentioned by #diverscuba23, you could do the following:
gdb --args bash <script>
(assuming it's a bash script. Else adapt accordingly)
There are two options that you can do:
Invoke GDB directly within the shell script. This would imply that you don't have standard in and standard out redirected.
Run the shell script and then attach the debugger to the already running C++ process like so: gdb progname 1234 where 1234 is the process ID of the running C++ process.
If you need to do things before the program starts running then option 1 would be the better choice, otherwise option 2 is the cleaner way.
Modify the c++ application to print its pid and sleep 30 seconds (perhaps based on environment or an argument). Attach to the running instance with gdb.
I would probably modify the script to always call gdb (and revert this later) or add an option to call gdb. This will almost always be the easiest solution.
The next easiest would be to temporarily move your executable and replace it with a shell script that runs gdb on the moved program. For example, in the directory containing your program:
$ mv program _program
$ (echo "#!/bin/sh"; echo "exec gdb $PWD/_program") > program
$ chmod +x program
Could you just temporarily add gdb to your script?
Although the answers given are valid, sometimes you don't have permissions to change the script to execute gdb or to modify the program to add additional output to attach through pid.
Luckily, there is yet another way through the power of bash
Use ps, grep and awk to pick-out the pid for you after its been executed. You can do this by either wrapping the other script with your own or by just executing a command yourself.
That command might look something like this:
process.sh
#!/usr/bin/env bash
#setup for this example
#this will execute vim (with cmdline options) as a child to bash
#we will attempt to attach to this process
vim ~/.vimrc
To get gdb to attach, we'd just need to execute the following:
gdb --pid $(ps -ef | grep -ve grep | grep vim | awk '{print $2}')
I use ps -ef here to list the processes and their arguments. Sometimes, you'll have multiple instances of a program running and need to further grep down to the one you want
the grep -ve grep is there because the f option to ps will include the next grep in its list. If you don't need the command arguments for additional filtering, don't include the -f option for ps and ignore this piece
grep vim is where we're finding our desired process. If you needed more filtering, you could just do something like grep -E "vim.*vimrc" and filter down to exactly the process that you're trying to attach to
awk '{print $2}' simply outputs just the process' pid to stdout. Use $1 if you're using ps -e instead of ps -ef
My normal setup is to run such script that starts my process in 1 tmux pane and having typed something similar to the above in a bottom pane. That way if I need to adjust the filtering (for whatever reason), I can do it pretty quickly.
Usually though, it will be the same for a specific instance and I want to just attach automatically after its been started. I'll do the following instead:
runGdb.py
#!/usr/bin/env bash
./process.sh &
PID=$(ps -ef | grep -ve grep | grep -E "vim.*vimrc" | awk '{print $2}')
#or
#PID=$(ps -e | grep vim | awk '{print $1}')
gdb --pid $PID
This assumes that the original process can be safely run in the background.
Related
TL;DR; I guess the shell that std::system use, is sh. But, I'm not sure.
I tried to print the shell, using this code: std::system("echo $SHELL"), and the output was /bin/bash. It was weird for me. So, I wanted to see, what happens if I do that in sh? And, the same output: /bin/bash. Also, if I use a command like SHELL="/usr/bin/something", to set the SHELL variable to another string, it will print the new string that I set to it (/usr/bin/something), and it looks it's not a good way to see what shell it's using. Then, I tried to check it, using the ps command, and the output was: bash, a.out, ps. It was weird to see bash in this list. So, I created a custom shell, and change the shell in gnome-terminal to it:
#include <iostream>
int main()
{
std::string input;
while (true)
{
std::string command;
std::getline(std::cin, command);
std::system(command.c_str());
}
}
Now, it's easier to test, and I think, the results is better.
Then, I tried to test the ps command again, but in the custom shell, and the results was: test_shell, ps.
It was weird again. How the shell isn't sh, nor bash? And, the final test I did was: echo $0. And, the results was sh, in both custom shell, and normal program.
Edit
It seems like /bin/sh is linked to /bin/bash (ll /bin/sh command's output is /bin/sh -> bash), and actually, it seems like the only difference between sh and bash is filename, and the files's contents are the same. I checked the difference between these files with diff command too:
$ xxd /bin/sh > sh
$ xxd /bin/bash > bash
$ diff sh bash
(+ Yes, $SHELL doesn't means the running shell (I didn't know that when I was testing, and I just wanted to see what happens))
The GNU sources (https://github.com/lattera/glibc/blob/master/sysdeps/posix/system.c) say
/bin/sh
So, whatever /bin/sh is hardlinked to is the shell invoked by std::system() on Linux.
(This is correct, as /bin/sh is expected to be linked to a sane shell capable of doing things with the system.)
According to cppreference.com, std::system
calls the host environment's command processor (e.g. /bin/sh, cmd.exe, command.com)
This means the shell used will depend on the operating system.
On any POSIX OS (including Linux), the shell used by std::system is /bin/sh. (Though as the OP points out, /bin/sh could be a symlink to another shell.)
As for the SHELL environment variable, as has been pointed out in the comments, this environment variable cannot be used to reliably identify the running shell program. SHELL is defined by POSIX to
represent a pathname of the user's preferred command language interpreter
(source)
So basiclly im trying to use this:
int main()
{
system("adb kill-server \n"
"adb devices \n"
"adb start-server & \n"
"var=$(adb shell \"pidof com.rok\")\n"
"AFTER=`echo $var | sed 's/\\r//g'`\n"
"echo \"$AFTER\"\n"
"adb shell \"kill -STOP $AFTER\"\n"
"adb shell sleep 2\n"
"adb shell \"kill -CONT $AFTER\"");
return 0;
}
thing is this works in Clion without any error, but i must do this in visual studio and in visual studio i cannot do it like that i have to do every system command alone like:
system("adb kill-server");
system("adb devices");
system("adb start-server");
system("var=$(adb shell \"pidof com.rok\")");
system("AFTER=`echo $var | sed 's/\\r//g'`");
system("adb shell \"kill -STOP $AFTER\"");
so now the thing is when i run it like this everything works except this two lines:
system("var=$(adb shell \"pidof com.rok\")");
system("AFTER=`echo $var | sed 's/\\r//g'`");
even though they perfectly works on clion they dont in visual studio, i cannot find a way to solve this problem, this is the error:
'var' is not recognized as an internal or external command,
operable program or batch file.
'AFTER' is not recognized as an internal or external command,
operable program or batch file.
can anyone explain why this happens? and how can i solve this problem?
Each call of system() creates it's own shell as a subprocess, that's why multiple subsequent system() calls don't work if you e.g. try to set shell variables or do a cd command (expecting subsequent commands running in a specific directory).
The easiest way to do it, is to create a little temporary script file containing all the commands and execute this one with a single system() call:
// A raw string literal to make escaping easier
// (see https://en.cppreference.com/w/cpp/language/string_literal)
std::string cmds = R"xxx(
adb kill-server
adb devices
adb start-server &
var=$(adb shell "pidof com.rok")
AFTER=`echo $var | sed 's/\r//g'`
echo "$AFTER"
adb shell "kill -STOP $AFTER"
adb shell sleep 2
adb shell "kill -CONT $AFTER"
)xxx";
std::ofstream cmdFile("tempCmdFile.cmd");
cmdFile << cmds;
cmdFile.close();
system("tempCmdFile.cmd");
You probably should tidy up the tempCmdFile.cmd up afterwards (i.e. remove it). But I hope you grasp what the code above does.
Also I am not so sure that
AFTER=`echo $var | sed 's/\r//g'`
will work in a windows shell, as you expect it to do. You probably need a different solution for that, or a *nix like shell to run it (e.g. MinGw provides one).
I am writing a c++ code in my project that should tell whether my websphere mq server is running or not. In order to extract that we need to run "/opt/mqm/bin/amq status" to show whether it is running or not.Tricky thing is MQHOME=/opt/mqm is not constant across unix platforms.So,We agreed to a design to extract the MQHOME path from the absolute path of the process "amqzlaar0" which is an mq server process.So, we need to issue the below command that shows the process "amqzlaar0" along with its fullpath.Then,we will store the string in an array to extract MQHOME.
"ps -ef | grep amqzlaar0 | awk '{print $(NF-1)}' "
system() function is failing with exit code -1 when i use pipe symbol "|". But, if I issue just system("ps -ef"),it works.
Please help me on how to execute a pipe seperated command using system.
Your help is much appreciated.
Regards,
Sriram
I believe you should not run a command to check that amqzlaar0 is running, but query the proc(5) filesystem (on Linux).
Notice that /proc/ is not portable (e.g. not standardized in Posix). Some Unixes don't have it, and Solaris and Linux have very different /proc/ file systems.
If really want to run a command, use e.g. snprintf(3) to build the command (or std::string or std::ostringstream) then use popen(3) (and pclose) to run the command
Read Advanced Linux Programming to get a better view of Linux Programming. See also syscalls(2)
BTW, some people might have aliased e.g. grep (perhaps in their .bashrc), so you probably should put full paths in your command (so /bin/grep not grep etc...).
Just run ps -Ef. You're a C++ programmer. The equivalent of grep and awk is not hard in C++, and it's faster in C++ (doesn't require two additional processes)
I'm trying to setup my gdbinit to make gdb automatically attach to a certain program on a remote machine.
My script is something like:
define hook-run
target extended-remote | ssh -T remotemachine gdbserver --multi -
attach $pid
... <additional complicated stuff here>
end
My problem, of course, is that I'm missing $pid. I can find it by running ssh remotemachine ps | grep myprogram, but I'm not sure how to run that from within the gdb script and assign it into that $pid variable. How can I do that? I'm guessing I'm going to need some Python here...
I can find it by running ssh remotemachine ps | grep myprogram
I believe your choices are
use Python, or
escape to shell
For (2) you can use something like:
define hook-run
shell gen-remote-run.sh > .remote-cmd.gdb
source .remote-cmd.gdb
end
and put all the "magic" of figuring out remote PID into gen-remote-run.sh
I have a question - I'm running a process from the command line that has some problem and poops out every few hours or so. While I'm looking into the issue, I'd like to spawn the process from something that monitors STOUT for certain string/regex and kills and restarts the process if it outputs something that indicates that it's broken.
I know I could do this the vanilla way by rolling my own Python/Ruby script but I was wondering if there's any nifty tools I can use to make this a bit cleaner? This is on Windows but I have cygwin in case the answer involves a unix command line process.
program | grep CRASH_STRING | xargs -l 1 sh -c 'killall program && program'
Well, that will do it once. I'm not sure how to make that work in a loop. I thought about it some more, and it can probably be done by redirecting stdout to a named pipe. But the shell script will probably end up more unwieldy than writing a watchdog in a scripting language.
But the idea with a pipe is something like this:
mkfifo /tmp/fifo
program > /tmp/fifo&
while :
do
grep "CRASH_STRING" /tmp/fifo | xargs -l 1 sh -c 'killall program && program > /tmp/fifo&'
done