Why clang needs to fork into itself?

I noticed that even the simple command 'clang -c -emit-llvm c.cpp' calls fork, and forks into itself.
Why does it need to do this? This would potentially complicate profiling, debugging, etc. All tools should be fork-aware.
Also unnecessary operation must cause some performance hit.

It is understandable when compiler would, for example, fork into an external macro preprocessor, or assembler, or linker, but why into itself?

Yuri

It makes the error reporting a bit easier for catching problems that
cause assertions, etc.

-eric

I haven't see the code but potentially the purpose of forking is to
handle internal compiler errors, especially the case when the child
process crashes when processing an input code. That may be easily
detected and handled properly by the parent process, by collecting the
trace, logs and by encouraging user to fill an issue in the bug DB,
etc.

In article <CAAnoKN971ZHsDU-01s5pqbUK2qKNW=_rJy-t=u7ovDd7kOWFyg@mail.gmail.com>,
    Tomasz Mikolajczyk <tmmikolajczyk@gmail.com> writes:

I haven't see the code but potentially the purpose of forking is to
handle internal compiler errors, especially the case when the child
process crashes when processing an input code. That may be easily
detected and handled properly by the parent process, by collecting the
trace, logs and by encouraging user to fill an issue in the bug DB,
etc.

How is this going to work on Windows where fork semantics really aren't
supported?

I've used crash report generator mechanisms on both linux and Windows
that didn't require forking oneself.

Unfortunately, it is also incredibly wasteful. And lib/Support/Unix/Program.inc does not even use vfork(), which is at least a little lighter on system resources.

If I recall correctly, this point has already come up way in the past, but I cannot find the thread right now. I do recall that some fixes were proposed, but got shot down due to various problems... :-/

-Dimitry

I don't see your point with catching compiler errors and crashes. Isn't this the caller responsibility? Normally, make does this very well. What else can other instance of clang report about the first one that crashed? All error messages should either be flushed immediately, or there is the chance they will be lost in unflushed buffers during crash.

I think this "guarding" should be made optional, based on some CFLAGS option or maybe some environment variable. This shouldn't happen during normal runs.

Yuri

"forking" is a bit misleading. Clang spawns a new copy of itself, it doesn't
require fork semantics. The performance hit is probably a bit larger as
creating processes tends to be more expensive on windows but it still works
in exactly the same way as on unices.

- Ben

Unfortunately, it is also incredibly wasteful. And lib/Support/Unix/Program.inc does not even use vfork(), which is at least a little lighter on system resources.

It uses posix_spawn if available, that avoids the potential slowdown when
spawning from heavyweight processes. In fact glibc implements posix_spawn by
just calling vfork+exec. The BSDs (including OS X) have a direct implementation
of posix_spawn.

- Ben

As an alternative, on Windows we could rig up some kind of SEH filter to do crash recovery. Then we could save the subprocess invocation and speed things up.

I noticed that even the simple command 'clang -c -emit-llvm c.cpp' calls fork, and forks into itself.
Why does it need to do this? This would potentially complicate profiling, debugging, etc. All tools should be fork-aware.
Also unnecessary operation must cause some performance hit.

It is understandable when compiler would, for example, fork into an external macro preprocessor, or assembler, or linker, but why into itself?

Yuri

If you want to debug/profile clang, you can invoke it directly with the -cc1 flag, and passing the right arguments.

To get the full command line used to invoke the real compilation process, you can use the -### argument:

clang -### -c -emit-llvm c.cpp

For the record, in the early days, the clang driver was a separate binary that used to invoke the compiler (which was called ccc IIRC).
Some time ago, the driver and the compiler were merged into a single clang binary, but it continue to work the same way it used to do. That explains why it executes itself.

I don't see your point with catching compiler errors and crashes. Isn't this the caller responsibility?

The caller can't give as much information about the crash than the driver does.
The driver is able to generate a preprocessed file, and a shell script to reproduce the clang invocation that crashed, and it also logs a message to tell the user how to report the bug.
Moreover, you can't expect that all callers do that, and it is very helpful to get proper bug report when the compilation process crashes.

-- Jean-Daniel

I see.
So I wrote up my proposal to make this opt-in: http://llvm.org/bugs/show_bug.cgi?id=18638

Yuri

I don’t think the reasons why we spawn another binary have really been captured in this thread. The biggest reason is that the clang driver accepts multiple files to compile:

clang foo.c bar.c baz.c -o thing

… and runs one compile process for each source file (and in this case, one link process for the binary). Crash recovery is just a nice side-effect of having a separate driver and frontend. The main benefit is that we get a consistent execution model regardless of the number of files passed to the driver.

But this also is not necessary to do. clang can just go through them sequentially in the same process. Now it forks itself for each of them one by one sequentially.

Yuri

Hi,

With Visual C++, this structure prevents the debugger from working, since the breakpoints are placed on the main process and not the spawned process. The cumbersome workaround is to print and capture the child -cc1 command line and use it instead.

I’m not sure how much crash recovery is useful here, clang never crashes ;-).

Seriously, If clang faults doing something the whole compilation job is very likely to fail anyhow even if clang can recover and compile the other source files in the job.

Yaron

Hi,

With Visual C++, this structure prevents the debugger from working, since
the breakpoints are placed on the main process and not the spawned process.
The cumbersome workaround is to print and capture the child -cc1 command
line and use it instead.

Yep, we're all familiar with this - all of us working on Clang have been
doing this for a while now...

I'm not sure how much crash recovery is useful here, clang never crashes
;-).

The bug reports beg to differ (when you work on the compiler you see just
how much it can fail).

Seriously, If clang faults doing something the whole compilation job is
very likely to fail anyhow even if clang can recover and compile the other
source files in the job.

Crash reporting/handling isn't likely to allow the user to continue without
changing their code - but it will give them some useful information to
report bugs.

Hi,

Helping the developers reproduce bugs is very important, certainly.

What information do we get from a spawned process crash that we can’t get from a “combined” process crash? the command line invocation of the spawned process?

Yaron

Yaron Keren wrote:

What information do we get from a spawned process crash that we can't
get from a "combined" process crash? the command line invocation

> of the spawned process?

Command line is one problem. Also, in case of hard crash the forked process may become completely unusable so we won't be able to report anything at all.

-Y

I work on clang pretty much every day in Windows, and I do not have
difficulties with adding the proper -cc1 flags. It was a bit
surprising at first, but it didn't take long to become used to. If
it's overly onerous, which very rarely happens for me, I'll simply use
WinDbg and set .childdbg to 1
(http://msdn.microsoft.com/en-us/library/windows/hardware/ff562215(v=vs.85).aspx),
then do my debugging. Obviously, everyone debugs differently, so YMMV.

Also, one thing to point out, is that when the child process crashes,
one of the things it prints out is the exact cc1 command line options
used to invoke the process, with everything properly quoted and
command-line ready. So it's a matter of copy/paste to debug when you
have a crashing problem.

~Aaron

Breakpad is used by both chrome and firefox for this. If going this
path, please make sure the same technique is used for all systems.

Cheers,
Rafale

I believe there was a thread about not forking the driver a while ago, but I’m unable to find it. As far as I remember, Chris Lattner wanted to get rid of it for aesthetic reasons and to save the milliseconds of overhead it adds (I think doing this has originally been the plan, see the “fork/exec” section on http://lists.cs.uiuc.edu/pipermail/cfe-dev/2009-December/007211.html). At the end, the decision was made to keep the subprocess for cc1, but I don’t remember all the reasons. I think crash reporting was part of the discussion, but there was half a plan to keep that feature with in-process crash reporting somehow.

Maybe someone still has a copy of that thread in their inbox? Keywords “lattner dgregor cc1 crash fork exec” or similar might find it.