Why doesn't "xargs sudo perf top" work as expected? - linux

I want to profile a process, so first get its pid, then use "perf top" to check it:
$ pgrep program
14472
$ sudo perf top -p 14472
It works as expected:
Then I want to use pipe to connect these two commands, so I use xargs:
$ pgrep program | sudo xargs perf top -p
But this time "perf top" seems not work normally:
I compared processes of these two operations:
(1) Run pgrep and perf separately:
$ ps -ef | grep perf
root 18468 16827 0 09:34 pts/3 00:00:00 sudo perf top -p 14472
root 18469 18468 91 09:34 pts/3 00:00:06 perf top -p 14472
nanxiao 18477 18295 0 09:34 pts/4 00:00:00 grep --color=auto perf
(2) Use xargs to connect pgrep and perf:
$ ps -ef | grep perf
nanxiao 18250 16827 0 09:32 pts/3 00:00:00 xargs sudo perf top -p
root 18251 18250 0 09:32 pts/3 00:00:00 sudo perf top -p 14472
root 18252 18251 87 09:32 pts/3 00:01:47 perf top -p 14472
nanxiao 18442 18295 0 09:34 pts/4 00:00:00 grep --color=auto perf
IMHO, it seems same. Anyone can give some clues? Thanks in advance!
P.S., my OS is CentOS 7.

After checking manual again, I find -o option can fix this issue:
-o, --open-tty
Reopen stdin as /dev/tty in the child process before executing
the command. This is useful if you want xargs to run an
interactive application.
The command is like this:
$ pgrep program | sudo xargs -o perf top -p
But unfortunately, CentOS 7's xargs is a little old, and doesn't provide this option.
The root cause is: without -o option, the stdin of perf program is /dev/null:
$ sudo lsof -p 1495
......
perf 1495 root 0r CHR 1,3 0t0 2052 /dev/null
......
And the perf is blocked in SLang_getkey():
......
FD_ZERO(&read_set);
FD_SET(0, &read_set);
if (delay_secs) {
timeout.tv_sec = delay_secs;
timeout.tv_usec = 0;
}
err = select(1, &read_set, NULL, NULL, ptimeout);
if (err == 0)
return K_TIMER;
if (err == -1) {
if (errno == EINTR)
return K_RESIZE;
return K_ERROR;
}
key = SLang_getkey();
if (key != K_ESC)
return key;
......
Read of /dev/null will return EOF, then select() will return 1.
With -o option the stdin of perf program is /dev/tty:
$ sudo lsof -p 1394
......
perf 1394 root 0u CHR 136,25 0t0 28 /dev/pts/25
......
In above code, the select() will return 0, and the whole function will return accordingly.

A better approach would be to directly run the top on the output of pgrep instead of piping over xargs. I believe top command by default does not read information over standard input
sudo perf top -p "$(pgrep program)"
This way the $(..) returns the output of the pgrep command and the returned value is passed as a positional argument value to the -p flag.

Related

issue with restarting autossh reverse tunnel on boot

I seem to have a weird issue:
I want to restart a reverse ssh tunnel on boot, I've tried it with an init script (that works fine when executed as user) and with an added line in /etc/rc.d but none of it works. What I get after boot is:
$ ps ax | grep autossh
397 pts/10 S+ 0:00 grep --color=auto autossh
1351 ? Ss 0:00 /usr/lib/autossh/autossh -M 22221 -N -o PubkeyAuthentication=yes -o PasswordAuthentication=no -i ~/.ssh/etherwan.key -R 19999:localhost:22 ubuntu#server
but I'm unable to login from server. So I did the following after boot:
$ sudo killall -KILL autossh
[sudo] password for ron:
$ /usr/bin/autossh -M 22221 -f -N -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -i ~/.ssh/etherwan.key -R 19999:localhost:22 ubuntu#server
upon which I can login using port 19999 just fine!
The keys permissions look like: (but root should not need to care, would it?)
$ ls -l ~/.ssh/etherwan.key
-r-------- 1 ron ron 1675 Nov 6 04:15 /home/ron/.ssh/etherwan.key
Replace ~/.ssh/etherwan.key in your rc.d script with /home/ron/.ssh/etherwan.key
The '~' character is expanded to the user's home directory by the shell, but rc.d scripts are run as root.

pgrep command not returning PID

I am trying to find the PID of a process (motion_sensor.py), but pgrep returns nothing. Why does it not return the process id?
pgrep -u www-data motion_sensor.py
ps -ef | grep "motion_sensor" returns
root 7149 1 93 Apr25 ? 15:59:08 python motion_sensor.py
www-data 31872 23531 0 14:09 ? 00:00:00 sh -c sudo python /home/pi/Desktop/PiControl/motion_sensor.py
root 31873 31872 0 14:09 ? 00:00:00 sudo python /home/pi/Desktop/PiControl/motion_sensor.py
root 31874 31873 47 14:09 ? 00:14:30 python /home/pi/Desktop/PiControl/motion_sensor.py
pi 32645 32202 0 14:39 pts/0 00:00:00 grep --color=auto motion_sensor.py
Normally pgrep applies the search pattern to process names. The process name in this case is python and not motion_sensor.py. If you want to grep for the full path rather than just the process name you need to pass -f:
pgrep -u www-data -f motion_sensor.py
Check man pgrep
the requirement is to find out PID of a process,
So you can try :
ps aux | grep www-data motion_sensor.py

kill one instance of ffserver out of multiple using specific configuration file

I have two instances of ffserver running both using different configuration file kept at different path.
I want to kill one of them using a script.
when i write following command :
ps -ef |grep ffs
it gives output :
root 6421 6394 0 18:47 pts/11 00:00:00 /root/bin/ffserver -f /root/newff/ffserver.conf
root 6575 6562 0 18:49 pts/11 00:00:02 /root/bin/ffserver -f /root/test/downloaded/complete/ffserver.conf
root 8453 3720 0 19:09 pts/11 00:00:00 grep ffs
Now i want to kill only one .
Is there any way to kill using command name like i can give command name with kill
pkill_like_command /root/bin/ffserver -f /root/newff/ffserver.conf
Please tell me how to do that
as simple pkill will not work.
There is an -f switch that works for pkill, so that matching considers the full command line. You can test without killing using pgrep instead. So the command line would be for example (tested on Debian with procps 1:3.2.8-9):
pkill -f "ffserver.*/root/newff/ffserver.conf"
without pkill:
kill $( ps -ef | grep "ffserver.*/root/newff/ffserver.conf" | awk '{ print $2 }' )

bash process substitution can't work fine with tee

The real thing I want to do is like ps -ef|head -n1 && ps -ef|grep httpd. The output should be something like this.
UID PID PPID C STIME TTY TIME CMD
xxxxx 6888 6886 0 16:49 pts/1 00:00:00 grep --color=auto httpd
root 10992 1 0 13:56 ? 00:00:00 sudo ./myhttpd
root 10993 10992 0 13:56 ? 00:00:00 ./myhttpd
root 11107 10993 0 13:56 ? 00:00:00 ./myhttpd
root 12142 10993 0 14:00 ? 00:00:00 ./myhttpd
root 31871 10993 0 15:03 ? 00:00:00 ./myhttpd
But I hate duplicates. So, I want ps -ef to appear only once.
Considering bash process substitution, I tried ps -ef | tee > >(head -n1) >(grep httpd), but the only output is
UID PID PPID C STIME TTY TIME CMD
However, ps -ef | tee > >(head -n1) >(head -n2) can work fine in the following way
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:36 ? 00:00:00 /sbin/init
UID PID PPID C STIME TTY TIME CMD
Can anyone help me ?
You can do head and grep on the same stream.
ps -ef | (head -n 1; grep '[h]ttpd')
It might be marginally more efficient to refactor to use sed:
ps -ef | sed -n -e '1p' -e '/[h]ttpd/p'
... but not all sed dialects deal amicably with multiple -e options. Perhaps this is more portable:
ps -ef | sed '1b;/[h]ttpd/b;d'
Also note the old trick to refactor the regex so as not to match itself by using a character class.
This can be achieved simply with pgrep and ps.
ps -fp $(pgrep -d, -o -f httpd)
use AWK
ps -ef | awk 'NR==1 || /httpd/'
print out 1st line or any line contains "httpd"
or use sed
ps -ef | sed -n '1p;/httpd/p'
ps -f -C httpd --noheaders | head -n1

More elegant "ps aux | grep -v grep"

When I check list of processes and 'grep' out those that are interesting for me, the grep itself is also included in the results. For example, to list terminals:
$ ps aux | grep terminal
user 2064 0.0 0.6 181452 26460 ? Sl Feb13 5:41 gnome-terminal --working-directory=..
user 2979 0.0 0.0 4192 796 pts/3 S+ 11:07 0:00 grep --color=auto terminal
Normally I use ps aux | grep something | grep -v grep to get rid of the last entry... but it is not elegant :)
Do you have a more elegant hack to solve this issue (apart of wrapping all the command into a separate script, which is also not bad)
The usual technique is this:
ps aux | egrep '[t]erminal'
This will match lines containing terminal, which egrep '[t]erminal' does not! It also works on many flavours of Unix.
Use pgrep. It's more reliable.
This answer builds upon a prior pgrep answer. It also builds upon another answer combining the use of ps with pgrep. Here are some pertinent training examples:
$ pgrep -lf sshd
1902 sshd
$ pgrep -f sshd
1902
$ ps up $(pgrep -f sshd)
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
$ ps up $(pgrep -f sshddd)
error: list of process IDs must follow p
[stderr output truncated]
$ ps up $(pgrep -f sshddd) 2>&-
[no output]
The above can be used as a function:
$ psgrep() { ps up $(pgrep -f $#) 2>&-; }
$ psgrep sshd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
Compare with using ps with grep. The useful header row is not printed:
$ ps aux | grep [s]shd
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
You can filter in the ps command, e.g.
ps u -C gnome-terminal
(or search through /proc with find etc.)
One more alternative:
ps -fC terminal
Here the options:
-f does full-format listing. This option can be combined
with many other UNIX-style options to add additional
columns. It also causes the command arguments to be
printed. When used with -L, the NLWP (number of
threads) and LWP (thread ID) columns will be added. See
the c option, the format keyword args, and the format
keyword comm.
-C cmdlist Select by command name.
This selects the processes whose executable name is
given in cmdlist.
Disclaimer: I'm the author of this tool, but...
I'd use px:
~ $ px atom
PID COMMAND USERNAME CPU RAM COMMANDLINE
14321 crashpad_handler walles 0.01s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Electron Framework.framework/Resources/crashpad_handler --database=
16575 crashpad_handler walles 0.01s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Electron Framework.framework/Resources/crashpad_handler --database=
16573 Atom Helper walles 0.5s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/MacOS/Atom Helper --type=gpu-process --cha
16569 Atom walles 2.84s 1% /Users/walles/Downloads/Atom.app/Contents/MacOS/Atom --executed-from=/Users/walles/src/goworkspace/src/github.com/github
16591 Atom Helper walles 7.96s 2% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/MacOS/Atom Helper --type=renderer --no-san
Except for finding processes with a sensible command line interface it also does a lot of other useful things, more details on the project page.
Works on Linux and OS X, easily installed:
curl -Ls https://github.com/walles/px/raw/python/install.sh | bash
Using brackets to surround a character in the search pattern excludes the grep process since it doesn't contain the matching regex.
$ ps ax | grep 'syslogd'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
18108 s001 S+ 0:00.00 grep syslogd
$ ps ax | grep '[s]yslogd'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
$ ps ax | grep '[s]yslogd|grep'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
18144 s001 S+ 0:00.00 grep [s]yslogd|grep
Depending on the ultimate use case, you often want to prefer Awk instead.
ps aux | awk '/[t]erminal/'
This is particularly true when you have something like
ps aux | grep '[t]erminal' | awk '{print $1}' # useless use of grep!
where obviously the regex can be factored into the Awk script trivially:
ps aux | awk '/[t]erminal/ { print $1 }'
But really, don't reinvent this yourself. pgrep and friends have been around for a long time and handle this entire problem space much better than most ad hoc reimplementations.
Another option is to edit your .bash_profile (or other file that you keep bash aliases in) to create a function that greps 'grep' out of the results.
function mygrep {
grep -v grep | grep --color=auto $1
}
alias grep='mygrep'
The grep -v grep has to be first otherwise your --color=auto won't work for some reason.
This works if you're using bash; if you're using a different shell YMMV.

Resources