master
c 125 lines 3.24 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 static time_t cached_boottime = 0;
6 static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
7
8 #if defined(OS_LINUX)
9
10 static time_t calculate_boottime(void) {
11 char buf[8192];
12
13 char filename[FILENAME_MAX + 1];
14
15 // Try to read from /proc/stat first - this provides the absolute timestamp
16 snprintfz(filename, sizeof(filename), "%s/proc/stat",
17 netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
18 if (read_txt_file(filename, buf, sizeof(buf)) == 0) {
19 char *btime_line = strstr(buf, "btime ");
20 if (btime_line) {
21 time_t btime = (time_t)str2ull(btime_line + 6, NULL);
22 if (btime > 0)
23 return btime;
24 }
25 }
26
27 // If btime is not available, calculate it from uptime
28 snprintfz(filename, sizeof(filename), "%s/proc/uptime",
29 netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
30 if (read_txt_file(filename, buf, sizeof(buf)) == 0) {
31 double uptime;
32 if (sscanf(buf, "%lf", &uptime) == 1) {
33 time_t now = now_realtime_sec();
34 time_t boottime = now - (time_t)uptime;
35 if(boottime > 0)
36 return boottime;
37 }
38 }
39
40 return 0;
41 }
42
43 #elif defined(OS_FREEBSD) || defined(OS_MACOS)
44
45 #include <sys/sysctl.h>
46
47 static time_t calculate_boottime(void) {
48 struct timeval boottime;
49 size_t size = sizeof(boottime);
50
51 // kern.boottime provides the absolute timestamp
52 if (sysctlbyname("kern.boottime", &boottime, &size, NULL, 0) == 0)
53 return boottime.tv_sec;
54
55 return 0;
56 }
57
58 #elif defined(OS_WINDOWS)
59
60 #include <windows.h>
61
62 static time_t calculate_boottime(void) {
63 ULONGLONG uptime_ms = GetTickCount64();
64 if (uptime_ms > 0) {
65 FILETIME ft;
66 ULARGE_INTEGER now;
67
68 GetSystemTimeAsFileTime(&ft);
69 now.HighPart = ft.dwHighDateTime;
70 now.LowPart = ft.dwLowDateTime;
71
72 // Convert to Unix epoch (subtract Windows epoch)
73 ULONGLONG unix_time_ms = (now.QuadPart - 116444736000000000ULL) / 10000;
74 time_t boottime = (time_t)((unix_time_ms - uptime_ms) / 1000);
75
76 if(boottime > 0)
77 return boottime;
78 }
79
80 return 0;
81 }
82
83 #endif
84
85 static time_t get_stable_boottime(void) {
86 const int max_attempts = 100;
87 const int required_matches = 5;
88 time_t last_boottime = 0;
89 int matches = 0;
90
91 for(int i = 0; i < max_attempts; i++) {
92 time_t new_boottime = calculate_boottime();
93 if(new_boottime == 0)
94 new_boottime = now_realtime_sec() - now_boottime_sec();
95
96 if(new_boottime == last_boottime)
97 matches++;
98 else {
99 matches = 1;
100 last_boottime = new_boottime;
101 }
102
103 if(matches >= required_matches)
104 return new_boottime;
105
106 microsleep(1000); // 1ms
107 }
108
109 return 0;
110 }
111
112 time_t os_boottime(void) {
113 // Fast path - return cached value if available
114 if(cached_boottime > 0)
115 return cached_boottime;
116
117 spinlock_lock(&spinlock);
118
119 // Check again under lock in case another thread set it
120 if(cached_boottime == 0)
121 cached_boottime = get_stable_boottime();
122
123 spinlock_unlock(&spinlock);
124 return cached_boottime;
125 }