2

Similar to How to check how long a process has been running?, I am trying to get the elapsed time for a process in seconds in an embedded Linux system using BusyBox and sh (not bash). The difference is that I would like it in pure seconds, not mm:ss format.

I can parse it out of the result of ps but am unable to get expr to convert from mm:ss to seconds. Using set -vx to help with debugging I can see the valid expr command but it fails with a syntax error. Copying and pasting it works fine.

After running this for added diagnostics:

root@embedded:~# set -vx

I enter:

root@embedded:~# $(/opt/bin/busybox ps -o pid,etime,time | grep 1156 | sed "s/ *[0-9]\+ \+\([0-9]\+\):\([0-9]\+\) .*/\/opt\/bin\/busybox expr \1 \\\* 60 + \2/")

And get this response:

$(/opt/bin/busybox ps -o pid,etime,time | grep 1156 | sed "s/ *[0-9]\+ \+\([0-9]\+\):\([0-9]\+\) .*/\/opt\/bin\/busybox expr \1 \\\* 60 + \2/")
+ /opt/bin/busybox ps -o pid,etime,time
+ grep 1156
+ sed s/ *[0-9]\+ \+\([0-9]\+\):\([0-9]\+\) .*/\/opt\/bin\/busybox expr \1 \\* 60 + \2/
+ /opt/bin/busybox expr 2 \* 60 + 03
expr: syntax error
root@embedded:~#

I have additionally tried multiple layers of \ escapes and also ''.

What do I need to do to get the escaping of the * to pass through to the expr command line?

  • it looks to me like you have an extra "busybox" call in there; try removing the busybox part before the expr call. – Jeff Schaller Jun 8 '15 at 13:26
  • This is being developed in test code where there are two copies of BusyBox. I am explicitly using the testing version for ps because the system version didn't include support for -o. In the second case I was just trying it out to see if there was a difference between the two binaries for expr as well. – altendky Jun 8 '15 at 16:22
  • Please post just your code, I'm not sure if I understand what bits are your code and what bits are traces and prompts and so on. – Gilles Jun 8 '15 at 22:42
  • @Gilles, I agree. I'll neaten it up when I get back on that system. – altendky Jun 9 '15 at 11:13
5

eval might help this case...

~ $ $(echo expr 1 \\* 2)
+ echo expr 1 \* 2
+ expr 1 \* 2
expr: syntax error
~ $ eval $(echo expr 1 \\* 2)
+ echo expr 1 \* 2
+ eval expr 1 \* 2
+ expr 1 * 2
2

But it might be better to look up /proc/$pid/stat on Linux.

pid=1155
hz=$(getconf CLK_TCK)
uptime=$(awk '{print $1}' < /proc/uptime)
starttime=$(awk '{print $22}' < /proc/$pid/stat)
echo $(( ${uptime%.*} - $starttime / $hz ))

If /usr/bin/getconf is unavailable, you need to find your system's CLK_TCK (or USER_HZ) value. I think you can assume it as 100 in most cases.

  • 1
    +1 for the /proc solution. But don't use the expr method. That code is already too complex and fragile. – Mikel Jun 8 '15 at 13:25
  • Given that the only comments I've seen about ticks/jiffies are to avoid dealing with them unless you want to carefully maintain your code, I'm going to stay away from that approach (and no, I don't presently have getconf). Your eval did work but I now have it working without as well. :[ I swear I tried anywhere from 0-6 `s before the *` and it didn't work, but it now does with \\*. Apparently I was debugging too many different avenues at once and ended up not having tested what I thought I had. Bugger and sorry. – altendky Jun 8 '15 at 17:02
1

Embarrassingly, I seem to have failed to test as completely as I had thought. It turns out that modifying what I posted in my question to contain only two escapes (\\*) makes it work fine.

root@embedded:~# $(/opt/bin/busybox ps -o pid,etime | sed -n "s/ *1155 \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \\* 60 + \2/p")
$(/opt/bin/busybox ps -o pid,etime | sed -n "s/ *1155 \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \\* 60 + \2/p")
+ /opt/bin/busybox ps -o pid,etime
+ sed -n s/ *1155 \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \* 60 + \2/p
+ expr 293 * 60 + 54
17634

Note that I also integrated the grep into sed with the -n 's///p' approach (global no-print and then print on match).

Thanks to @yaegashi for getting me to try \\ again...

  • Nope, this fails. The difference was the existence of files in the directory or not. If there are no files, then * remains unsubstituted. Otherwise, it expands and messes up the expr. – altendky Jun 8 '15 at 17:49
1

assumping process is 16752 (which you seems to be able to figure out)

expr $(date +%s) - $(stat -c %Y /proc/16752/environ )

where

  • date +%s is current date in second since the epoch
  • stat -c %Y /proc/16752/environ is "creation date" of /proc/16752/environ, that is the moment where proc #16752 was launched

edit:

  • maybe /proc/$PID/environ is the wrong pseudo file, /proc/$PID/exe should be used instead.

  • if time changed on host, both result from ps -p $PID -h -o etime and timestamp from /proc/$PID/exe are likely to be wrong. (unsure how to check that).

  • This system set's it's time on startup from the application. Since this occurs between the process start and the checking of the process elapsed time, it is pretty much guaranteed to be wrong. Such timestamps are generally hazardous even when not guaranteed to be a problem. Thanks for taking the time to respond. – altendky Jun 8 '15 at 16:29
  • I am glad you found a solution, are you sure there is a difference between ps'etime and my solution ? – Archemar Jun 9 '15 at 3:57
  • The user (or application in my case) can change the system time between the process start and checking of elapsed time. You can try elapsed() { expr $(date +%s) - $(stat -c %Y /proc/${MYPID}/environ ); }; sleep 10 & export MYPID=$!; elapsed; sleep 1; elapsed; sudo date +%F\ %T -s "$(date -d "$(date +%F\ %T) 1 hour ago")"; elapsed. My result is 0, then 1, then -3599. Don't forget to reset your time, optionally like sudo rdate 0.us.pool.ntp.org (it takes a minute or two for me). Wall clock time is a hazardous way to keep track of durations and should be avoided when uptime is usable. – altendky Jun 9 '15 at 11:07
0

@yaegashi had the approach right but my sed call seems to introduce the need for a third \.

root@embedded:/data# set -vx
root@embedded:/data# echo Process Uptime: $(eval $(/opt/bin/busybox ps -o pid,etime | sed -n "s/ *${APP_PID} \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \\\* 60 + \2/p"))
echo Process Uptime: $(eval $(/opt/bin/busybox ps -o pid,etime | sed -n "s/ *${APP_PID} \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \\\* 60 + \2/p"))
+ /opt/bin/busybox ps -o pid,etime
+ sed -n s/ *1156 \+\([0-9]\+\):\([0-9]\+\).*/expr \1 \\* 60 + \2/p
+ eval expr 2 \* 60 + 46
+ expr 2 * 60 + 46
+ echo Process Uptime: 166
Process Uptime: 166

Fingers crossed that this not only works now, but also continues to do so next time I use it...

0

This is an awk script which will convert the 0-00:00:00 output to seconds. It is used like this...

ps -eo pid,time,user,comm | awk '{print $1"|"$2"|"$3"|"$4$5$6$7$8$9$10}' | awk -f _os_return_process_time_in_seconds.awk

Here is the script. Just wrote if for another purpose. May have issues still.

BEGIN {
   FS="|"
}

{
   pid=$1
   time=$2
   user=$3
   comm=$4
   split_count=split($2,days_array,"\-")
   if ( split_count == 2 ) {
      days=days_array[1]
      hr_min_sec=days_array[2]
   }
   else {
      days=0
      hr_min_sec=days_array[1]
   }
   split_count=split(hr_min_sec,hr_min_sec_array,":")
   if ( split_count == 3 ) {
      hours=hr_min_sec_array[1]
      minutes=hr_min_sec_array[2]
      seconds=hr_min_sec_array[3]
   }
   else if ( split_count == 2 ) {
      hours=0
      minutes=hr_min_sec_array[1]
      seconds=hr_min_sec_array[2]
   }
   else if ( split_count == 1 ) {
      hours=0
      minutes=0
      seconds=hr_min_sec_array[1]
   }
   total_seconds=(days*24*60*60)+(hours*60*60)+(minutes*60)+(seconds)
   print pid"|"total_seconds"|"user"|"comm
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.