Forum: Ruby Processes and forks

Posted by baqtwo front (baq_s)
on 2013-01-24 18:04
Hi all, I have a quick problem that I am sure someone here has an
elegant solution for.

I have a ruby script that runs in a loop an external linux process via
gdb:

...
until checks.empty? do
...
    begin
        Timeout::timeout(5) {
            system("/usr/bin/gdb --args #{proc} 2>/dev/null")}
        rescue Timeout::Error
    end
...
end
...

What I am trying to do is have gdb terminate after 5 seconds of activity
and move on to debugging the next task. What I am finding however is
that after 5 seconds, my ruby script quits and returns to the command
line and the gdb and debugged processes are left running in the
background.

I can then hit fg and it will start again but this is not what I want; I
want my script to execute gdb and launch it to debug a program, after 5
seconds to terminate debugging and move on to the next binary to debug.

much regards
Posted by Carlo E. Prelz (Guest)
on 2013-01-24 18:56
(Received via mailing list)
Subject: Processes and forks
  Date: Fri 25 Jan 13 02:04:38AM +0900
Sorry for the delay!

Quoting baq s. (lists@ruby-forum.com):

>         Timeout::timeout(5) {
> line and the gdb and debugged processes are left running in the
> background.
>
> I can then hit fg and it will start again but this is not what I want; I
> want my script to execute gdb and launch it to debug a program, after 5
> seconds to terminate debugging and move on to the next binary to
> debug.

The biggest reason why your process does not die is most probably
because you use system (which starts a shell). The signal reaches the
shell but not gdb. I believe that gdb has a bit special way to
handle interrupts.

So, instead of system, you should use Process::spawn. But even then,
read carefully the doc: if you pass a command line (like in your
example), you will still have a shell process in the middle. To avoid
that, you must pass the parts of the command in an array. Every time I
do this, I have to spend a decent amount of time finding how to
correctly split the command.

Anyway, you may try with something like:

pid=Process::spawn(array)

begin
  Timeout::timeout(5) do
    Process::waitpid(pid)
  end
rescue Timeout::Error
  Process::kill(pid,'KILL')
  Process::waitpid(pid)
end

You will have to experiment...

Carlo
Posted by baqtwo front (baq_s)
on 2013-01-24 19:55
Hi Carlo,

many thanks for the detailed reply, much appreciated as you have given 
me very useful information to read about and test. All the best to you.
Posted by Carlo E. Prelz (Guest)
on 2013-01-24 19:58
(Received via mailing list)
Subject: Re: Processes and forks
  Date: Fri 25 Jan 13 03:55:03AM +0900

Quoting baqtwo front (lists@ruby-forum.com):

> many thanks for the detailed reply, much appreciated as you have given
> me very useful information to read about and test. All the best to
> you.

Glad to have been of help!

Carlo
Posted by Robert Klemme (robert_k78)
on 2013-01-24 21:34
(Received via mailing list)
On Thu, Jan 24, 2013 at 6:56 PM, Carlo E. Prelz <fluido@fluido.as> 
wrote:

> The biggest reason why your process does not die is most probably
> because you use system (which starts a shell). The signal reaches the
> shell but not gdb.

I don't think there is a signal at all because system just blocks
until the process dies and the timeout exception is raised *after* the
process has terminated and control comes back into the Ruby
interpreter.  Proof:

Test script:
$ cat k.sh
#!/usr/bin/bash

echo "PID $$"

for i in {0..30}; do
  trap "echo signal $i" $i
done

read -p ENTER # block

Demo to show signal output via pressing Ctrl-C:

$ ./k.sh
PID 2296
ENTERsignal 2
signal 2
signal 2
signal 2

signal 0

Now with Timeout:

$ time ruby -r timeout -e 'Timeout.timeout(2) { system "./k.sh" }'
PID 3552
ENTER
signal 0
-e:1:in `system': execution expired (Timeout::Error)
        from -e:1:in `block in <main>'
        from -e:1:in `<main>'

real    0m18.096s
user    0m0.046s
sys     0m0.201s

As you can see from real time I waited much longer than the timeout of
2 seconds before I pressed enter.

> So, instead of system, you should use Process::spawn. But even then,
> read carefully the doc: if you pass a command line (like in your
> example), you will still have a shell process in the middle. To avoid
> that, you must pass the parts of the command in an array. Every time I
> do this, I have to spend a decent amount of time finding how to
> correctly split the command.

That's generally good advice but I believe in this case not important
for the issue (see above).

>   Process::waitpid(pid)
> end

That looks like a good solution.

Kind regards

robert
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.