Discussion:
child process initialization as part of fork()
Mark Geisert
2016-03-08 04:09:19 UTC
Permalink
I've finally figured out why my recent profiling updates don't often work
for the case of a profiled program fork()ing with both parent and child
continuing on until exit without exec()ing. There is some static data in
libgmon.a that needs updating to be correct in the child after a fork().
I would like to make use of gcrt0.c's _monstartup() to determine when the
data needs to be updated, but there's a catch.

_monstartup() is declared "__attribute__((__constructor__))" which is
ideal for my purposes. But investigation has revealed that during fork()
the constructor is being run by the parent process, rather than the child
process as one would expect.

Is this behavior required by the current fork() implementation? Some kind
of chicken-and-egg problem avoidance maybe?

I suspect changing gmon from a static library to a DLL and using NO_COPY
could avoid the issue but that has a performance impact I'd like to avoid.
Thanks,

..mark
Mark Geisert
2016-03-08 08:20:04 UTC
Permalink
-------- note.h
#include <stdio.h>
#include <unistd.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

static void
note (const char *fmt, ...)
{
va_list ap;
char buf[160];
int count;
DWORD done;
HANDLE errh = GetStdHandle (STD_ERROR_HANDLE);

va_start (ap, fmt);
count = sprintf (buf, "%d: ", getpid ());
count += vsprintf (&buf[count], fmt, ap);
va_end (ap);

WriteFile (errh, buf, count, &done, NULL);
FlushFileBuffers (errh);
}

-------- stc.c
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include "note.h"

void constructor (void) __attribute__((__constructor__));
void destructor (void) __attribute__((__destructor__));

void
constructor (void)
{
note ("I'm a constructor\n");
}

void
destructor (void)
{
note ("I'm a destructor\n");
}

int
main (int argc, char *argv[])
{
int isparent = 0;
int status;
int winfo = 0;

note ("I'm the parent\n");
status = fork ();
if (status == -1)
return errno;

if (status)
isparent++;
else
note ("I'm the child\n");

sleep (3 + isparent);

if (isparent) {
status = wait (&winfo);
if (status == -1)
return errno;
}

return 0;
}

-------- compile, link, and run
~ gcc -nostdlib -pg -g -o stc stc.c build/gcrt0.o build/crt0.o
build/libgmon.a build/libcygwin.a /c/Windows/system32/kernel32.dll -lgcc

~ strace -o stc.out --mask=debug+sigp+syscall ./stc
5592: _monstartup called
5592: monstartup called
5592: moncontrol called
5592: profile_off called
5592: profile_on called
5592: _monstartup called <-- this is the odd message I'm asking about
5592: I'm a constructor
5592: I'm the parent
5504: I'm the child
5504: I'm a destructor
5504: _mcleanup called
5504: moncontrol called
5504: profile_off called
5592: I'm a destructor
5592: _mcleanup called
5592: moncontrol called
5592: profile_off called

~

In the above compilation command, "build" is a symlink to
/oss/build/x86_64-unknown-cygwin/winsup/cygwin/

I've patched several routines in Cygwin DLL and libgmon.a to call my
note() function just to announce they've been reached and to say what pid
they're running in. It looks like libgmon's _monstartup() is called
twice, which is expected, but both times in the parent's process, which is
not expected.

..mark
Mark Geisert
2016-03-08 08:29:07 UTC
Permalink
Sending corrected log to avoid minor confusion...
Post by Mark Geisert
-------- compile, link, and run
~ gcc -nostdlib -pg -g -o stc stc.c build/gcrt0.o build/crt0.o
build/libgmon.a build/libcygwin.a /c/Windows/system32/kernel32.dll -lgcc
~ strace -o stc.out --mask=debug+sigp+syscall ./stc
5592: _monstartup called
5592: monstartup called
5592: moncontrol called
5592: profile_off called
5592: profile_on called
5592: _monstartup called <-- this is the odd message I'm asking about
5592: I'm a constructor
5592: I'm the parent
5504: I'm the child
5504: I'm a destructor
5504: _mcleanup called
5504: moncontrol called
5504: profile_off called
5592: I'm a destructor
5592: _mcleanup called
5592: moncontrol called
5592: profile_off called
~
Corinna Vinschen
2016-03-08 12:50:48 UTC
Permalink
Hi Mark,
Post by Mark Geisert
-------- compile, link, and run
~ gcc -nostdlib -pg -g -o stc stc.c build/gcrt0.o build/crt0.o
build/libgmon.a build/libcygwin.a /c/Windows/system32/kernel32.dll -lgcc
~ strace -o stc.out --mask=debug+sigp+syscall ./stc 5592: _monstartup called
5592: monstartup called
5592: moncontrol called
5592: profile_off called
5592: profile_on called
5592: _monstartup called <-- this is the odd message I'm asking about
5592: I'm a constructor
5592: I'm the parent
5504: I'm the child
5504: I'm a destructor
5504: _mcleanup called
5504: moncontrol called
5504: profile_off called
5592: I'm a destructor
5592: _mcleanup called
5592: moncontrol called
5592: profile_off called
~
In the above compilation command, "build" is a symlink to
/oss/build/x86_64-unknown-cygwin/winsup/cygwin/
I've patched several routines in Cygwin DLL and libgmon.a to call my note()
function just to announce they've been reached and to say what pid they're
running in. It looks like libgmon's _monstartup() is called twice, which is
expected, but both times in the parent's process, which is not expected.
I'm not set up to test this locally. Can you please check the *Windows*
PID as well in your note function? There's a chance this is a false
positive. Dependent on when the constructors are called, the Cygwin
PID might not be set yet in the child.


Corinna
--
Corinna Vinschen Please, send mails regarding Cygwin to
Cygwin Maintainer cygwin AT cygwin DOT com
Red Hat
Mark Geisert
2016-03-08 17:55:41 UTC
Permalink
Sorry for the noise. I had assumed something and posted about it before
checking more thoroughly...
Post by Corinna Vinschen
Hi Mark,
Post by Mark Geisert
-------- compile, link, and run
~ gcc -nostdlib -pg -g -o stc stc.c build/gcrt0.o build/crt0.o
build/libgmon.a build/libcygwin.a /c/Windows/system32/kernel32.dll -lgcc
~ strace -o stc.out --mask=debug+sigp+syscall ./stc
5592: _monstartup called
5592: monstartup called
5592: moncontrol called
5592: profile_off called
5592: profile_on called
5592: _monstartup called <-- this is the odd message I'm asking about
5592: I'm a constructor
5592: I'm the parent
...parent 5592 does the fork() here...
Post by Corinna Vinschen
Post by Mark Geisert
5504: I'm the child
5504: I'm a destructor
5504: _mcleanup called
5504: moncontrol called
5504: profile_off called
5592: I'm a destructor
5592: _mcleanup called
5592: moncontrol called
5592: profile_off called
~
In the above compilation command, "build" is a symlink to
/oss/build/x86_64-unknown-cygwin/winsup/cygwin/
I've patched several routines in Cygwin DLL and libgmon.a to call my note()
function just to announce they've been reached and to say what pid they're
running in. It looks like libgmon's _monstartup() is called twice, which is
expected, but both times in the parent's process, which is not expected.
I'm not set up to test this locally. Can you please check the *Windows*
PID as well in your note function? There's a chance this is a false
positive. Dependent on when the constructors are called, the Cygwin
PID might not be set yet in the child.
I checked Windows pids as well as Cygwin pids and they are 1:1 throughout the
STC. But I've now noticed the 2nd call to _monstartup is being done before
main() is entered in the parent. So it has nothing to do with fork() setting up
the child.

And now that I think about it, fork() copies the "constructed" address space
from parent to child so there should be no need to run constructors again. I
need another mechanism to update libgmon static data and start up a profiling
thread in the child after a fork(). Maybe pthread_atfork() is the way.

..mark

Loading...