NuSphere Forums Forum Index
NuSphere Forums
Reply to topic
Debugging forked processes (using pcntl_fork)


Joined: 28 Sep 2013
Posts: 20
Location: Pantin, IDF, France
Reply with quote
Hello,

I am trying to use PhpED for debugging the following script (in CLI mode):

Code:
#! /usr/local/bin/php
<?php
$to_create = array('Apple', 'Pear', 'Banana', 'Orange');
$pids = array();

foreach ($to_create as $current) {
   $newpid = pcntl_fork();
   if ($newpid == -1) {
      die('Unable to fork');
   }

   if ($newpid == 0) {
      // Son process
      break;
   }
   $pids[$newpid] = $newpid;
   $current = false;
}

$curpid = posix_getpid();

if ($current) {
   $seconds = rand(5, 15);
   echo "$current process pid = $curpid. Now waiting $seconds seconds.\n";
   sleep($seconds);
   echo "\nEnd of $current process";
} else {
   echo "Father process Pid = $curpid\n";

   foreach ($pids as $pid) {
      pcntl_waitpid($pid, $status);
      unset($pids[$pid]);
   }
   echo "\nEnd of father process.\n";
}
?>


The script forks itself in 4 son processes using the pcntl_fork function, and next the father process waits for the end of all of its sons.

PhpED seems to work rather erratically in this case, by constantly switching between processes after each step.

Is there a way to manage this properly?
Normally the debugging should stay in the initial process, but some mean should be provided to switch between concurrent processes.

Regards,

Gingko
View user's profileFind all posts by GingkoSend private message
Guru master

Joined: 24 Jul 2009
Posts: 724
Reply with quote
Debug sessions have session ID's associated with them and that ID would be stored in the DBG module which is part of the running PHP process. When you fork, that takes a copy of the process including all of its data structures. That would include the session ID, so I guess PhpED gets confused when the are now 5 DBG processes all sending debug messages with the same session ID.

I don't know if there is a way of changing the session ID of an active DBG module, you could try using DebugBreak() in each of your forked processes and generating a random or unique number as a session ID.

Similar to this (I don't think you would need to specify any other parameters):

Code:
DebugBreak(rand(1, 999999));


That might cause the DBG module to store that new session ID instead of the original one that came from the fork.

This might even disable the debugger for a process but I haven't tested it:

Code:
DebugBreak(rand(1, 999999) . ';d=0');


You will still have a possible problem that all the breakpoints will be shared, but PhpED might at least create separate sessions for you to debug.

Alternatively, you could try starting your main process without the debugger then after forking, in each of the forked processes start the debugger on demand by using DebugBreak()
View user's profileFind all posts by plugnplaySend private message
Debugging forked processes (using pcntl_fork)


Joined: 28 Sep 2013
Posts: 20
Location: Pantin, IDF, France
Reply with quote
Hello and thank you very much for your reply.

There is at least one thing that are different between forked processes, it is the "pid" (Process ID).
I mean, the one that you get using posix_getpid() like what I do in my script.

Isn't it possible to apply, to each debugger single stepping, a kind of internal "breakpoint condition" checking that the "pid" after the step is the same as the "pid" that we had just before, skipping the breakpoint if it is not the same?

Regards,

Gingko
View user's profileFind all posts by GingkoSend private message
Guru master

Joined: 24 Jul 2009
Posts: 724
Reply with quote
I think the PID is almost the only difference.

If you are debugging multiple forked processes, you are probably going to have problems if you cannot get the NuSphere DBG session ID to change.

If you just wanted to debug a specific child, you could probably do something using PhpED conditional breakpoints; you would have to apply that expression to every breakpoint.

The expression can be any PHP expression so maybe using a function from the global namespace would make that much easier for you to manage and change in the future without editing all your breakpoints, plus your function could do some reasonably involved checking to determine if the breakpoint was allowed.
View user's profileFind all posts by plugnplaySend private message


Joined: 28 Sep 2013
Posts: 20
Location: Pantin, IDF, France
Reply with quote
plugnplay wrote:
If you just wanted to debug a specific child, you could probably do something using PhpED conditional breakpoints; you would have to apply that expression to every breakpoint.

This is more or less what I was suggesting, but conditional breakpoints, as far as I know, cannot apply to single stepping.

If I have to set a conditional breakpoint on each line that I have to debug, this can quickly become very heavy, boring and counterproductive…

I am also a user of Microsoft Visual Studio, so I can tell you that this software, for example, works very well in debugging across multiple processes and threads in C++ language.

Gingko
View user's profileFind all posts by GingkoSend private message
Guru master

Joined: 24 Jul 2009
Posts: 724
Reply with quote
I am also a .NET developer and was doing that for a long time before I first used PHP. VisualStudio attaches to processes and will suspend processes at each instruction or statement, so it works in an entirely different way. Also process forking is not even a Windows concept, so it is not something VS has to cope with.

PHP debuggers use a concept of debug session ID's, xdebug does the same as NuSphere DBG, it is a few years since I used Zend debugger but I think that did as well. PHP debuggers do not attach to the PHP process because they are a PHP extension and part of the process.

You copied (forked) the process, which means you copied the debugger module and all of its data, so PhpED does not know that is a different PHP process. That's not PhpED's fault, that is how forking processes work.

If you can clear or change the debug session ID's in each of the forked PHP processes, you should find single step works. That is probably important to getting debugging to work for you.

I can guarantee you that the NuSphere debugger is better at debugging PHP than the other PHP debuggers and works more similarly to VisualStudio. In particular, NuSphere is the only PHP debugger that will allow you to set an arbitrary execution point in code so that you can re-execute that code without having to restart the whole debug session.

Expression true global could effectively give you a breakpoint on every statement, similar to single stepping. I'm not sure it is a good way of doing it, fixing the session ID is probably better.
View user's profileFind all posts by plugnplaySend private message


Joined: 28 Sep 2013
Posts: 20
Location: Pantin, IDF, France
Reply with quote
plugnplay wrote:
You copied (forked) the process, which means you copied the debugger module and all of its data, so PhpED does not know that is a different PHP process.

Even if it explicitly checks for current PID change on each debugging step?? Confused Confused
(sorry for bugging you, but I really have trouble to understand that…)

plugnplay wrote:
If you can clear or change the debug session ID's in each of the forked PHP processes, you should find single step works.

Ok, but can I really do that ?
Where should I write "DebugBreak(rand(1, 999999)); or "DebugBreak(mt_rand(1, 999999). ';d=0');"?
I tried right after "pcntl_fork();", but the behaviour change is not obvious.

Note also that I probably need to use srand(<PID>) before rand because otherwise it tends to return the same value in all son processes.

I tried a little with "Expression true global" with "posix_getpid() == <somepid>". I will try more, but I can already tell that it more or less transforms "run" into single stepping.

Gingko
View user's profileFind all posts by GingkoSend private message
Guru master

Joined: 24 Jul 2009
Posts: 724
Reply with quote
I don't know if NuSphere DBG allows the changing of the session ID. I've just done a little test that indicates possibly not.

You are correct about seeding rand and you could also use mt_rand which produces better randomness. I normally use mt_rand() in all my code.

If you use DebugBreak() before activating the debugger, then it will active the debugger for you. You might have to specify a PhpED host address in the DebugBreak():

Code:
DebugBreak($uniqueSessionID . '@' . $myPhpEdIpAddress);


You can also use DebugBreak(-1) to drop a debug session.

I'm wondering if what you will need to do is:

1. Start the CLI running without debug
2. At some point after forking, use DebugBreak() to create new debug sessions, maybe specifying your own unique debug session id. You could choose to use DebugBreak() on just one child and/or the parent.
3. Each of those debug sessions will then hopefully be unique within PhpED and not interfere with each other.

I just tested that and it appeared to work.
View user's profileFind all posts by plugnplaySend private message
Debugging forked processes (using pcntl_fork)
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
All times are GMT - 5 Hours  
Page 1 of 1  

  
  
 Reply to topic