Why would system() use 100% CPU inside the fork() syscall?

Viewed 179

I have a case where I create an ffmpeg ... command line and execute it with a simple system() call. I use that in my tests and I use that call within a require to make sure that it returns 0 as expected, I also print the command so I can easily see what ran and failed:

std::cerr << "--- command: " << cmd << std::endl;
CATCH_REQUIRE(system(cmd) == 0);  // <-- get stuck inside there

Once in a while, that process locks up, using 100% of one CPU core. Looking at where that happens, I found out that it was in the do_system() function¹ of the C library at the point where it does a syscall. Comparing to the source code, that would be the FORK () call.

#ifdef FORK
  pid = FORK ();
#else
  pid = __fork ();
#endif

The following is the corresponding assembly found with gdb, i.e.

ps -ef | grep process-name
sudo gdb -p <pid>

First I used where to see the stack. The last call was to do_system().

Then I typed disassemble to get this output:

   0x00007ffb9274b129 <+361>:   lea    0x1c(%rsp),%rdx
   0x00007ffb9274b12e <+366>:   xor    %esi,%esi
   0x00007ffb9274b130 <+368>:   mov    $0x100011,%edi
   0x00007ffb9274b135 <+373>:   mov    $0x38,%eax
   0x00007ffb9274b13a <+378>:   syscall 
=> 0x00007ffb9274b13c <+380>:   cmp    $0xfffffffffffff000,%rax
   0x00007ffb9274b142 <+386>:   ja     0x7ffb9274b2d0 <do_system+784>
   0x00007ffb9274b148 <+392>:   test   %eax,%eax
   0x00007ffb9274b14a <+394>:   mov    %eax,%edi
   0x00007ffb9274b14c <+396>:   mov    %eax,0x1c(%rsp)
   0x00007ffb9274b150 <+400>:   je     0x7ffb9274b3c7 <do_system+1031>

First, when I saw that I tried to continue to see what happens. I typed the following:

gdb> stepi

and it went back to 100% CPU usage for one core and never stopped again. Since I was running as root, I'm pretty confident that the stepi would function even inside the C library code.

I could understand some form of lock up in the C library, especially since I use threads (although at the point only the main thread is running, the other threads are all exiting before I call system()), but I have a hard time understanding why it would block inside the kernel fork() syscall.

Anyone encountered such a lock up before who would know why it occurs?

¹ see under sysdeps/posix/system.c (libc version 2.27 under Ubuntu 18.04)

1 Answers

If fork were to block, it would be taking 0% CPU, not 100%. So what you have here is almost certainly a tight loop calling fork repeatedly. Since the fork system call is (probably) the most expensive single thing in the loop, most of the time when you attach or probe with gdb, it will see it as being in the system call.

What you need to do is look at the context (the call scope(s) on the stack) to figure out what the tight loop that keeps calling fork is.

Related