protonscr

Excessive CPU time in ivrclientcore_get_generic_interface

steamvrclosed
ValveSoftware/SteamVR-for-Linux#148 · opened 2018-10-18 by ryao · updated 2019-08-10 · 11 comments · github
Rryao 2018-10-18 github

How To Collect SteamVR System Information:

  1. Click the 'SteamVR beta' menu in the upper left of the SteamVR Status window
  2. Click 'Create System Report'
  3. Click 'Copy to Clipboard'
  4. Browse to https://gist.github.com/
  5. Click in the text entry box (by the 1)
  6. Paste clipboard contents
  7. Click 'Create Public Gist'
  8. Add URL of your new Gist below

Your system information

  • Steam client version (build number or date): 153939410
  • Distribution (e.g. Ubuntu): Gentoo
  • Graphics driver version (run nvidia-settings): 396.54.09
  • Gist for SteamVR System Information: https://gist.github.com/ryao/787519c68533865934bcde0e3cf9cb8c
  • Opted into Steam client beta?: Yes
  • Opted into SteamVR beta?: No
  • Have you checked for system updates?: Yes

Please describe your issue in as much detail as possible:

The Lab.exe is CPU bound on a single core, causing GPU pipeline stalls with Proton 3.16-3 beta. This is not a regression. It has been around since Proton first debuted, but various issues kept me from feeling comfortable filing a bug report due to difficulty doing decent triage.

Today, I was able to profile it with perf in a way that reveals the issue. Here is a flame graph demonstrating it:

Flame graph

For those unfamiliar with flame graphs, the Y axis is stack frames of stacks. The X axis is the percentage of samples a given stack was seen. You can read more about it here:

https://github.com/brendangregg/FlameGraph

Anyway, this shows excessive CPU time in ivrclientcore_get_generic_interface, or more specifically, in IPC::CreateMutexWithNamespace. 72% of CPU time is being spent creating mutexes. I have no idea why it is doing that. The source code is not public, so I cannot triage/debug further.

Steps for reproducing this issue:

  1. Install Linux
  2. Install Steam
  3. Install SteamVR
  4. Install The Lab
  5. Try playing it.
  6. While it is running, profile using perf record -ag -F 997 -- sleep 10.
  7. Then you can use something like perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl --title='The Lab on Proton 3.16-3: 10 seconds idle in Longbow sampled at 997Hz' > /tmp/lab.svg to get a flame graph showing the issue.

You will need this patch in order for the stackcollapse-perf.pl script to work:

https://github.com/brendangregg/FlameGraph/pull/185

Hh1z1 2018-10-24 github

See #143

Rryao 2018-10-26 github

@h1z1 I think that is a separate issue. I tried making .local/share/Steam/steamapps/common/SteamVR/bin/linux64/chmod_trace_marker into a setuid program to see if it makes a difference and it just isn't being executed. The flame graph showing __GI___kill taking most of our CPU time also implies that this issue has nothing to do with chmod_trace_marker.

Rryao 2018-10-26 github

I did some more profiling. These are the syscalls being executed by TheLab.exe over a 10 second interval:

SYSCALL                   COUNT
kill                   10961289
readv                    165884
poll                     126740
write                     52679
getpid                    25323
clock_gettime              5377
futex                      4482
sched_yield                3319
select                     3058
openat                     1932

It is doing ~1.1 million kill syscalls per second, which is absurd. Some more profiling shows that these are all sigzero and are being sent a few processes:

vserver ~ # /usr/share/bcc/tools/argdist -p 13482 -C 'p:c:kill(int pid, int sig):int:pid'     
[15:16:39]
p:c:kill(int pid, int sig):int:pid
        COUNT      EVENT
        13528      pid = 10803
        22349      pid = 13930
        24663      pid = 32159
        37024      pid = 7686
        103899     pid = 13482
        259419     pid = 7886
[15:16:40]
p:c:kill(int pid, int sig):int:pid
        COUNT      EVENT
        12844      pid = 10803
        21373      pid = 13930
        23570      pid = 32159
        35152      pid = 7686
        98926      pid = 13482
        247366     pid = 7886
[15:16:41]
p:c:kill(int pid, int sig):int:pid
        COUNT      EVENT
        13148      pid = 10803
        21798      pid = 13930
        24047      pid = 32159
        35984      pid = 7686
        101143     pid = 13482
        253200     pid = 7886
^Cvserver ~ # /usr/share/bcc/tools/argdist -p 13482 -C 'p:c:kill(int pid, int sig):int:sig'
[15:16:50]
p:c:kill(int pid, int sig):int:sig
        COUNT      EVENT
        440520     sig = 0
[15:16:51]
p:c:kill(int pid, int sig):int:sig
        COUNT      EVENT
        455396     sig = 0
[15:16:52]
p:c:kill(int pid, int sig):int:sig
        COUNT      EVENT
        448923     sig = 0

The profiling data showing what processes are being signaled does not show a million signals per second, but this is such a tight loop that the act of collecting the syscall arguments is likely incurring significant overhead. Here are what each of the processes in question are:

richard   7686  7661  5 15:00 pts/12   00:00:54 /home/richard/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrserver -waitformonitor -ipccontrolfilepath /tmp/SteamVR-IPCControlFile-7663
richard   7886  7661 44 15:00 pts/12   00:07:49 /home/richard/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrcompositor
richard  10803  7661 83 15:01 pts/12   00:14:35 /home/richard/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrmonitor -nokillprocess
richard  13482 13288 99 15:02 pts/12   00:17:05 Z:\home\richard\.local\share\Steam\steamapps\common\The Lab\TheLab\win64\TheLab.exe -popupwindow
richard  13930 13482  5 15:02 pts/12   00:00:56 /home/richard/.local/share/Steam/steamapps/common/SteamVR//bin/linux64/vrdashboard
richard  32159 32086 16 15:00 pts/12   00:03:04 /home/richard/.local/share/Steam/ubuntu12_32/steam

It is surprising that TheLab.exe is doing signal 0 on itself. It sends the second largest number of signals to itself.

In any case, the result of the signal operation really should be cached. You can have an asynchronous worker thread update it once per second. Some code to handle the possibility of it being wrong would be needed, but kernel preemption means that you would need that code anyway because you have a TOCTOU race either way. In specific, the other process could be killed before you do whatever you are relying on signal 0 to say is safe to do.

Rryao 2018-10-26 github

Just a FYI, the call to kill is being done in IPC::CreateMutex. It goes through the Global Offset Table. When control moves to the kill function in glibc, it immediately executes these two instructions to do the syscall:

   362c0:       b8 3e 00 00 00          mov    $0x3e,%eax
   362c5:       0f 05                   syscall

The consequence is that the stack unwinder does not recognize the frame for IPC::CreateMutex as being different from the frame for the kill call. At least, that is my current conjecture. I have noticed the stack frame doing a syscall through glibc absent from stack traces when syscalls were recorded during perf profiling in the past, but until now, I have never looked into the possible cause.

Edit: I disassembled the wrong version of glibc for an example (32-bit rather than 64-bit), but my point about it entering the syscall immediately is applicable to both cases.

Rryao 2018-10-26 github

If Valve cannot fix this any time soon, would Valve please open source vrclient.so so that I could fix it for them?

Rryao 2018-10-26 github

Alternatively, I would be happy to settle for a “If you are such a big fan of our software that you would go through the hassle of reverse engineering and modifying binary code to fix this and post a binary patch before we fix it ourselves, we will not sue you.”.

Hh1z1 2018-10-27 github

Might interest you to run gpuvis (f6), it's a awsome little utility that gets fed data from steam via the kernel perf events. It's not always aparent what the cause is. The read calls themselves are quick. In my case it also identified an unrelated bug in the kernel wrt the clocksource.

Rryao 2018-10-27 github

That does not work with the Nvidia driver and I am already using perf events manually. It might be useful for non-VR things on my systems that use Intel graphics though. Thanks.

PPlagman 2018-10-27 github

gpuvis is a frontend to visualize any perf events, with specialized visualizers for CPU and GPU scheduling events, and print events. You're right that there's no NVIDIA GPU scheduling events to visualize at the moment, but it's still valuable on the CPU/print side even on NVIDIA machines.

Llostgoat 2019-08-09 github

This issue should be fixed in SteamVR 1.7.2

Rryao 2019-08-10 github

I can confirm that it is fixed. :)

Proton versions