Tor Browser poll() interval too small - BUSY WAITING - 150% CPU usage - on Fedora 36, using system tor
I am running the system tor daemon that comes with Fedora 36, with these RPMs installed:
# rpm -qa 'tor*'
torsocks-2.3.0-11.fc36.x86_64
tor-0.4.6.9-2.fc36.x86_64
torbrowser-launcher-0.3.5-4.fc36.noarch
and torBrowser has installed itself under ~/.local/share/torbrowser,
having updated itself several times by now, it now reports version:
11.0.14 (based on Mozilla Firefox 91.10.0esr) (64-bit)
$ cat ~/.config/torbrowser/settings.json\n
{"tbl_version": "0.3.5", "installed": false, "download_over_tor": false,\n
"tor_socks_address": "127.0.0.1:9050", "mirror": "https://dist.torproject.org/", "force_en-US": false}
I run the tor service with a custom torrc, which only differs from the default
by setting its ExitNodes to be in the USA (I am in Ireland), and is run with
the
$ systemctl start tor@custom.service
so that the daemon uses my custom torrc:
$ ps -ef | grep usr/bin/tor
toranon 768987 1 0 Jun12 ? 00:00:09 /usr/bin/tor --runasdaemon 0 --defaults-torrc /usr/share/tor/defaults-torrc -f /etc/tor/custom.torrc\n
# cat /etc/tor/custom.torrc
# This file was generated by Tor; if you edit it, comments will not be preserved
# The old torrc file was renamed to torrc.orig.1, and Tor will ignore it
%include /etc/tor/torrc
EntryNodes {us} StrictNodes 1
ExitNodes {us} StrictNodes 1
I therefore must run torBrowser with the script:
$ cat ~/bin/torBrowser.sh
#!/bin/bash
#
# Script to run tor browser with system tor service:
#
declare -x \
TOR_SOCKS_PORT=9050 \
TOR_CONTROL_PORT=9051 \
TOR_SKIP_LAUNCH=1 \
TOR_CONTROL_SOCKET=/run/tor/control \
TOR_CONTROL_COOKIE_AUTH_FILE=/run/tor/control.authcookie ;
. ~/J/daemon.sh
daemon -n tor-browser -l ~/.local/share/torbrowser/tbb/x86_64/tor-browser_en-US/Browser/firefox
(~/J/daemon.sh provides the daemon() function which does:
setsid(); ioctl( tty_fd, TIOCNOTTY, 0);
and ignores SIGHUP.
).
I am finding 2 problems with this setup :
-
torbrowser, after a period of inactivity, enters a ridiculously over-frequent poll() loop, doing classic busy-waiting and consuming a huge amount of CPU time:
42742 jvd 20 0 4270720 329112 93572 R 149.4% 2.1 385:47.05 firefox.real
This is really silly , since Linux provides so many alternatives to poll() these days!
My Firefox normal browser consumes about 2% CPU with over 120 tabs open, but the above 149% CPU usage is from a torBrowser with just 1 tab open; TCP shows NO actual network traffic being processed by torBrowser, it is just Idle, and polling its sockets - see strace excerpt below.
Using SIGIO, for instance, one can enable O_ASYNC for socket or pipe FDs, and arrange to receive real-time SIGIO signals as soon as a received packet has been written to the kernel buffer for a socket, and enter pause(), so that the process consumes 0% user-CPU while waiting for incoming traffic, and is woken up immediately by a signal when an incoming packet arrives.
These incoming SIGIO signals can be received on a signal FD or event stream.
-
I have to initially visit a geo-location site, eg. 'geolocation.com', to check if I am really using a USA exit node.
Sometimes, after a period of inactivity, either A) torBrowser thinks it cannot connect to system tor, and starts up its internal tor; B) the system tor has given up on / dropped its USA ExitNode
but anyway sometimes my exit node on startup ends up in the EU, so I have to restart the system tor daemon as above, then restart the torBrowser, go to geolocation.com and then my exit node will always be in USA for the session.
Strace Excerpt showing torBrowser's busy-waiting:
# strace -vvvttt -f -p 42742
[pid 42742] 1655121095.707533 recvmsg(5, <unfinished ...>
[pid 42770] 1655121095.707556 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}, {fd=106, events=POLLIN|POLLPRI}], 3, -1 <unfinished ...>
[pid 42742] 1655121095.707569 <... recvmsg resumed>{msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42770] 1655121095.707585 <... poll resumed>) = 1 ([{fd=106, revents=POLLIN}])
[pid 42742] 1655121095.707603 poll([{fd=5, events=POLLIN}, {fd=6, events=POLLIN}, {fd=8, events=POLLIN}, {fd=44, events=POLLIN}, {fd=77, events=POLLIN}, {fd=88, events=POLLIN}, {fd=157, events=POLLIN}], 7, 0 <unfinished ...>
[pid 42770] 1655121095.707629 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}], 2, -1 <unfinished ...>
[pid 42742] 1655121095.707641 <... poll resumed>) = 1 ([{fd=44, revents=POLLIN}])
[pid 42742] 1655121095.707665 read(44, "\372", 1) = 1
[pid 42742] 1655121095.707701 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.707734 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.707772 poll([{fd=5, events=POLLIN}, {fd=6, events=POLLIN}, {fd=8, events=POLLIN}, {fd=44, events=POLLIN}, {fd=77, events=POLLIN}, {fd=88, events=POLLIN}, {fd=157, events=POLLIN}], 7, 0) = 0 (Timeout)
[pid 42742] 1655121095.707821 ioctl(106, FIONREAD, [0]) = 0
[pid 42742] 1655121095.707859 ioctl(106, FIONREAD, [0]) = 0
[pid 42742] 1655121095.707888 recvfrom(106, "", 0, 0, NULL, NULL) = 0
[pid 42742] 1655121095.707955 write(45, "\372", 1) = 1
[pid 42742] 1655121095.708012 write(36, "M", 1 <unfinished ...>
[pid 42770] 1655121095.708043 <... poll resumed>) = 1 ([{fd=35, revents=POLLIN}])
[pid 42742] 1655121095.708055 <... write resumed>) = 1
[pid 42770] 1655121095.708074 futex(0x7fa3e6915720, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 42742] 1655121095.708085 futex(0x7fa3e6915720, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 42770] 1655121095.708105 <... futex resumed>) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.708116 <... futex resumed>) = 0
[pid 42770] 1655121095.708135 read(35, "M", 2048) = 1
[pid 42742] 1655121095.708163 recvmsg(5, <unfinished ...>
[pid 42770] 1655121095.708189 futex(0x7fa3e6915720, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 42742] 1655121095.708203 <... recvmsg resumed>{msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42770] 1655121095.708226 <... futex resumed>) = 0
[pid 42742] 1655121095.708237 recvmsg(5, <unfinished ...>
[pid 42770] 1655121095.708262 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}, {fd=106, events=POLLIN|POLLPRI}], 3, -1 <unfinished ...>
[pid 42742] 1655121095.708279 <... recvmsg resumed>{msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42770] 1655121095.708302 <... poll resumed>) = 1 ([{fd=106, revents=POLLIN}])
[pid 42742] 1655121095.708326 poll([{fd=5, events=POLLIN}, {fd=6, events=POLLIN}, {fd=8, events=POLLIN}, {fd=44, events=POLLIN}, {fd=77, events=POLLIN}, {fd=88, events=POLLIN}, {fd=157, events=POLLIN}], 7, 0 <unfinished ...>
[pid 42770] 1655121095.708361 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}], 2, -1 <unfinished ...>
[pid 42742] 1655121095.708378 <... poll resumed>) = 1 ([{fd=44, revents=POLLIN}])
[pid 42742] 1655121095.708405 read(44, "\372", 1) = 1
[pid 42742] 1655121095.708444 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.708481 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.708522 poll([{fd=5, events=POLLIN}, {fd=6, events=POLLIN}, {fd=8, events=POLLIN}, {fd=44, events=POLLIN}, {fd=77, events=POLLIN}, {fd=88, events=POLLIN}, {fd=157, events=POLLIN}], 7, 0) = 0 (Timeout)
[pid 42742] 1655121095.708581 ioctl(106, FIONREAD, [0]) = 0
[pid 42742] 1655121095.708620 ioctl(106, FIONREAD, [0]) = 0
[pid 42742] 1655121095.708700 recvfrom(106, "", 0, 0, NULL, NULL) = 0
[pid 42742] 1655121095.708802 write(45, "\372", 1) = 1
[pid 42742] 1655121095.708915 write(36, "M", 1 <unfinished ...>
[pid 42770] 1655121095.708972 <... poll resumed>) = 1 ([{fd=35, revents=POLLIN}])
[pid 42742] 1655121095.708987 <... write resumed>) = 1
[pid 42770] 1655121095.709008 futex(0x7fa3e6915720, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 42742] 1655121095.709021 futex(0x7fa3e6915720, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 42770] 1655121095.709045 <... futex resumed>) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.709058 <... futex resumed>) = 0
[pid 42770] 1655121095.709077 read(35, "M", 2048) = 1
[pid 42742] 1655121095.709111 recvmsg(5, <unfinished ...>
[pid 42770] 1655121095.709136 futex(0x7fa3e6915720, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 42742] 1655121095.709149 <... recvmsg resumed>{msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42770] 1655121095.709172 <... futex resumed>) = 0
[pid 42742] 1655121095.709183 recvmsg(5, <unfinished ...>
[pid 42770] 1655121095.709208 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}, {fd=106, events=POLLIN|POLLPRI}], 3, -1 <unfinished ...>
[pid 42742] 1655121095.709224 <... recvmsg resumed>{msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42770] 1655121095.709246 <... poll resumed>) = 1 ([{fd=106, revents=POLLIN}])
[pid 42742] 1655121095.709266 poll([{fd=5, events=POLLIN}, {fd=6, events=POLLIN}, {fd=8, events=POLLIN}, {fd=44, events=POLLIN}, {fd=77, events=POLLIN}, {fd=88, events=POLLIN}, {fd=157, events=POLLIN}], 7, 0 <unfinished ...>
[pid 42770] 1655121095.709293 poll([{fd=35, events=POLLIN|POLLPRI}, {fd=79, events=POLLIN|POLLPRI}], 2, -1 <unfinished ...>
[pid 42742] 1655121095.709310 <... poll resumed>) = 1 ([{fd=44, revents=POLLIN}])
[pid 42742] 1655121095.709353 read(44, "\372", 1) = 1
[pid 42742] 1655121095.709406 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
[pid 42742] 1655121095.709439 recvmsg(5, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable)
This 40 micro-second poll interval is too small !
My laptops' fans are very loud and the whole laptop gets too hot.
I have normal Firefox connecting to YouTube + BBC audio streams, with over 120 tabs open , and it uses about 2% CPU , compared to 149% for ONE torBrowser tab open to duckduckgoOnion search!
I have to do:
$ kill -STOP $(cat ~/torBrowser.pid)
when not using torbrowser, and
$ kill -CONT $(cat ~/torBrowser.pid)
when I want to use torbrowser.
This is really unacceptable!
I will work over the next few weeks to replace torBrowser's polling mechanism with something more suitable for Linux, in a forked branch of the torbrowser code, unless you already have plans to do so - I am a C/C++ developer of 28+years experience, I have worked on Mozilla code base before, this would be an interesting task.