1 |
So this article[1] from 2017 popped up again on the tech radar via hackernews[2] and a few other sites[3]. It |
2 |
annotates how if the envvar TZ is undefined on a Linux system, it causes glibc to generate a number of |
3 |
additional syscalls, mainly stat-related calls (in my tests, newfstatat()). If defined to an actual value, |
4 |
such as ":/etc/localtime" (or even an empty string), glibc will instead generate far fewer, if any at all, of |
5 |
these stat-related syscalls. |
6 |
|
7 |
Apparently, TZ is accessed quite frequently, so this has a compound effect, according to the article, in glibc |
8 |
making thousands of unnecessary stat-related syscalls to /etc/localtime (which must be hard-coded somewhere in |
9 |
glibc for this case). Given the article's age (five years old), I tested the example C program out, and it |
10 |
does appear to still be accurate on a modern glibc-based system. When TZ is undefined, I get exactly nine |
11 |
newfstatat calls on /etc/localtime. If I define TZ to ":/etc/localtime", I do not get any of these newfstatat |
12 |
calls, and if I set TZ to an empty string, glibc will call openat() against "/usr/share/zoneinfo/Universal" |
13 |
and then generate exactly two newfstatat syscalls on that handle to read it. |
14 |
|
15 |
I ran strace() against the undefined TZ case and the ":/etc/localtime" case, normalized the hex addresses to |
16 |
get a clean diff, and this is what it looks like: |
17 |
|
18 |
--- a 2023-01-18 20:30:36.826805343 -0500 |
19 |
+++ b 2023-01-18 20:30:45.106983600 -0500 |
20 |
@@ -1,4 +1,4 @@ |
21 |
-# strace ./tz_test |
22 |
+# TZ=":/etc/localtime" strace ./tz_test |
23 |
execve("./tz_test", ["./tz_test"], 0xhhhhhhhhhhhh /* XX vars */) = 0 |
24 |
brk(NULL) = 0xhhhhhhhhhhhh |
25 |
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xhhhhhhhhhhhh |
26 |
@@ -61,15 +61,6 @@ read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0 |
27 |
lseek(3, -2260, SEEK_CUR) = 1292 |
28 |
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\6\0\0\0\0"..., 3584) = 2260 |
29 |
close(3) = 0 |
30 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
31 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
32 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
33 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
34 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
35 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
36 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
37 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
38 |
-newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=3552, ...}, 0) = 0 |
39 |
write(1, "Godspeed, dear friend!\n", 23Godspeed, dear friend! |
40 |
) = 23 |
41 |
exit_group(0) = ? |
42 |
|
43 |
For comparison, I tested the same program on FreeBSD and it does not exhibit this behavior at all, regardless |
44 |
of whether TZ is undefined, a value, or an empty string. I have yet to make a similar test on a mips/musl |
45 |
chroot to see how musl handles this. |
46 |
|
47 |
There is a rather old (2010) StackOverflow question[4] about it as well, and someone left an answer in March |
48 |
of last year about the specific code in glibc that handles TZ if it is set or is an empty string. |
49 |
|
50 |
So is adding a default definition of TZ to our base system /etc/profile something we want to look at? I |
51 |
haven't tried any other methods of benchmarking to see if not making those additional syscalls is just placebo |
52 |
or if there are actual impacts. Given how long this oddity has been around, I can't tell if it's a genuine |
53 |
bug in glibc, an unoptimized corner case, or just a big nothingburger. |
54 |
|
55 |
|
56 |
1. https://blog.packagecloud.io/set-environment-variable-save-thousands-of-system-calls/ |
57 |
2. https://news.ycombinator.com/item?id=34346346 |
58 |
3. https://vermaden.wordpress.com/posts/ |
59 |
4. |
60 |
https://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux |
61 |
|
62 |
|
63 |
Thoughts? |
64 |
|
65 |
-- |
66 |
Joshua Kinard |
67 |
Gentoo/MIPS |
68 |
kumba@g.o |
69 |
rsa6144/5C63F4E3F5C6C943 2015-04-27 |
70 |
177C 1972 1FB8 F254 BAD0 3E72 5C63 F4E3 F5C6 C943 |
71 |
|
72 |
"The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by |
73 |
moment, lost in that vast, terrible in-between." |
74 |
|
75 |
--Emperor Turhan, Centauri Republic |