blob: 22b864e5d54c714e70e14967aae7f5bb07a5ff2b [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#include "aos/realtime.h"
2
Austin Schuh77f3f222022-06-10 16:49:21 -07003#include <dirent.h>
Austin Schuhcc6070c2020-10-10 20:25:56 -07004#include <malloc.h>
5#include <sched.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07006#include <sys/mman.h>
Austin Schuhcc6070c2020-10-10 20:25:56 -07007#include <sys/prctl.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07008#include <sys/resource.h>
9#include <sys/types.h>
10#include <unistd.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -070011
Tyler Chatowbf0609c2021-07-31 16:13:27 -070012#include <cerrno>
13#include <cstdint>
14#include <cstdio>
15#include <cstdlib>
16#include <cstring>
17
Alex Perrycb7da4b2019-08-28 19:35:56 -070018#include "glog/logging.h"
Austin Schuh62288252020-11-18 23:26:04 -080019#include "glog/raw_logging.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070020
James Kuszmaula791b762023-07-13 14:56:21 -070021#include "aos/uuid.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070022
Austin Schuh62288252020-11-18 23:26:04 -080023DEFINE_bool(
Austin Schuhbd938202023-05-16 22:42:11 -070024 die_on_malloc, true,
Austin Schuh62288252020-11-18 23:26:04 -080025 "If true, die when the application allocates memory in a RT section.");
Austin Schuh27553152020-11-18 21:26:37 -080026DEFINE_bool(skip_realtime_scheduler, false,
27 "If true, skip changing the scheduler. Pretend that we changed "
28 "the scheduler instead.");
29DEFINE_bool(skip_locking_memory, false,
30 "If true, skip locking memory. Pretend that we did it instead.");
31
Austin Schuh62288252020-11-18 23:26:04 -080032extern "C" {
Tyler Chatowbf0609c2021-07-31 16:13:27 -070033typedef void (*MallocHook_NewHook)(const void *ptr, size_t size);
Austin Schuh62288252020-11-18 23:26:04 -080034int MallocHook_AddNewHook(MallocHook_NewHook hook) __attribute__((weak));
35int MallocHook_RemoveNewHook(MallocHook_NewHook hook) __attribute__((weak));
36
Tyler Chatowbf0609c2021-07-31 16:13:27 -070037typedef void (*MallocHook_DeleteHook)(const void *ptr);
Austin Schuh62288252020-11-18 23:26:04 -080038int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) __attribute__((weak));
Tyler Chatowbf0609c2021-07-31 16:13:27 -070039int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook)
40 __attribute__((weak));
Tyler Chatow582c6c72021-07-16 13:45:07 -070041
42// Declare tc_malloc weak so we can check if it exists.
43void *tc_malloc(size_t size) __attribute__((weak));
44
45void *__libc_malloc(size_t size);
46void __libc_free(void *ptr);
Tyler Chatowbf0609c2021-07-31 16:13:27 -070047} // extern "C"
Austin Schuh62288252020-11-18 23:26:04 -080048
Alex Perrycb7da4b2019-08-28 19:35:56 -070049namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
50extern double FLAGS_tcmalloc_release_rate __attribute__((weak));
51}
52using FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead::
53 FLAGS_tcmalloc_release_rate;
54
55namespace aos {
56namespace logging {
57namespace internal {
58
59// Implemented in aos/logging/context.cc.
60void ReloadThreadName() __attribute__((weak));
61
62} // namespace internal
63} // namespace logging
64
65namespace {
66
Austin Schuh27553152020-11-18 21:26:37 -080067enum class SetLimitForRoot { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080068
Austin Schuh27553152020-11-18 21:26:37 -080069enum class AllowSoftLimitDecrease { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080070
71void SetSoftRLimit(
72 int resource, rlim64_t soft, SetLimitForRoot set_for_root,
Austin Schuh27553152020-11-18 21:26:37 -080073 std::string_view help_string,
James Kuszmaulb4874eb2020-01-18 17:50:35 -080074 AllowSoftLimitDecrease allow_decrease = AllowSoftLimitDecrease::kYes) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070075 bool am_root = getuid() == 0;
James Kuszmaulb4874eb2020-01-18 17:50:35 -080076 if (set_for_root == SetLimitForRoot::kYes || !am_root) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070077 struct rlimit64 rlim;
78 PCHECK(getrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070079 << ": getting limit for " << resource;
Alex Perrycb7da4b2019-08-28 19:35:56 -070080
James Kuszmaulb4874eb2020-01-18 17:50:35 -080081 if (allow_decrease == AllowSoftLimitDecrease::kYes) {
82 rlim.rlim_cur = soft;
83 } else {
84 rlim.rlim_cur = std::max(rlim.rlim_cur, soft);
85 }
Alex Perrycb7da4b2019-08-28 19:35:56 -070086 rlim.rlim_max = ::std::max(rlim.rlim_max, soft);
87
88 PCHECK(setrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070089 << ": changing limit for " << resource << " to " << rlim.rlim_cur
Austin Schuh27553152020-11-18 21:26:37 -080090 << " with max of " << rlim.rlim_max << help_string;
Alex Perrycb7da4b2019-08-28 19:35:56 -070091 }
92}
93
94} // namespace
95
96void LockAllMemory() {
Austin Schuhcc6070c2020-10-10 20:25:56 -070097 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -070098 // Allow locking as much as we want into RAM.
Austin Schuh27553152020-11-18 21:26:37 -080099 SetSoftRLimit(RLIMIT_MEMLOCK, RLIM_INFINITY, SetLimitForRoot::kNo,
100 "use --skip_locking_memory to not lock memory.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700101
Austin Schuh27553152020-11-18 21:26:37 -0800102 PCHECK(mlockall(MCL_CURRENT | MCL_FUTURE) == 0)
103 << ": Failed to lock memory, use --skip_locking_memory to bypass this. "
104 "Bypassing will impact RT performance.";
Alex Perrycb7da4b2019-08-28 19:35:56 -0700105
Brian Silverman4dbbcce2020-09-18 15:27:38 -0700106#if !__has_feature(address_sanitizer) && !__has_feature(memory_sanitizer)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700107 // Don't give freed memory back to the OS.
108 CHECK_EQ(1, mallopt(M_TRIM_THRESHOLD, -1));
109 // Don't use mmap for large malloc chunks.
110 CHECK_EQ(1, mallopt(M_MMAP_MAX, 0));
Austin Schuh85faf672020-09-10 22:58:46 -0700111#endif
Alex Perrycb7da4b2019-08-28 19:35:56 -0700112
113 if (&FLAGS_tcmalloc_release_rate) {
114 // Tell tcmalloc not to return memory.
115 FLAGS_tcmalloc_release_rate = 0.0;
116 }
117
118 // Forces the memory pages for all the stack space that we're ever going to
119 // use to be loaded into memory (so it can be locked there).
120 uint8_t data[4096 * 8];
121 // Not 0 because linux might optimize that to a 0-filled page.
122 memset(data, 1, sizeof(data));
Austin Schuh27553152020-11-18 21:26:37 -0800123 __asm__ __volatile__("" ::"m"(data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700124
125 static const size_t kHeapPreallocSize = 512 * 1024;
126 char *const heap_data = static_cast<char *>(malloc(kHeapPreallocSize));
127 memset(heap_data, 1, kHeapPreallocSize);
Austin Schuh27553152020-11-18 21:26:37 -0800128 __asm__ __volatile__("" ::"m"(heap_data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700129 free(heap_data);
130}
131
132void InitRT() {
Austin Schuh27553152020-11-18 21:26:37 -0800133 if (FLAGS_skip_locking_memory) {
134 LOG(WARNING) << "Ignoring request to lock all memory due to "
135 "--skip_locking_memory.";
136 return;
137 }
138
Austin Schuhcc6070c2020-10-10 20:25:56 -0700139 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700140 LockAllMemory();
141
Austin Schuh27553152020-11-18 21:26:37 -0800142 if (FLAGS_skip_realtime_scheduler) {
143 return;
144 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700145 // Only let rt processes run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800146 SetSoftRLimit(
147 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
148 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
149 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700150
151 // Allow rt processes up to priority 40.
Austin Schuh27553152020-11-18 21:26:37 -0800152 SetSoftRLimit(
153 RLIMIT_RTPRIO, 40, SetLimitForRoot::kNo,
154 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
155 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700156}
157
158void UnsetCurrentThreadRealtimePriority() {
159 struct sched_param param;
160 param.sched_priority = 0;
Brian Silverman6a54ff32020-04-28 16:41:39 -0700161 PCHECK(sched_setscheduler(0, SCHED_OTHER, &param) == 0);
Austin Schuhcc6070c2020-10-10 20:25:56 -0700162 MarkRealtime(false);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700163}
164
165void SetCurrentThreadAffinity(const cpu_set_t &cpuset) {
166 PCHECK(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700167}
168
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800169void SetCurrentThreadName(const std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700170 CHECK_LE(name.size(), 16u) << ": thread name '" << name << "' too long";
171 VLOG(1) << "This thread is changing to '" << name << "'";
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800172 std::string string_name(name);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700173 PCHECK(prctl(PR_SET_NAME, string_name.c_str()) == 0)
174 << ": changing name to " << string_name;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700175 if (&logging::internal::ReloadThreadName != nullptr) {
176 logging::internal::ReloadThreadName();
177 }
178}
179
Austin Schuhde973292021-10-12 18:09:49 -0700180cpu_set_t GetCurrentThreadAffinity() {
181 cpu_set_t result;
182 PCHECK(sched_getaffinity(0, sizeof(result), &result) == 0);
183 return result;
184}
185
Alex Perrycb7da4b2019-08-28 19:35:56 -0700186void SetCurrentThreadRealtimePriority(int priority) {
James Kuszmaula791b762023-07-13 14:56:21 -0700187 // Ensure that we won't get expensive reads of /dev/random when the realtime
188 // scheduler is running.
189 UUID::Random();
190
Austin Schuh27553152020-11-18 21:26:37 -0800191 if (FLAGS_skip_realtime_scheduler) {
192 LOG(WARNING) << "Ignoring request to switch to the RT scheduler due to "
193 "--skip_realtime_scheduler.";
194 return;
195 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700196 // Make sure we will only be allowed to run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800197 SetSoftRLimit(
198 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
199 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
200 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700201
Brian Silvermanb3826f52020-07-02 19:41:18 -0700202 // Raise our soft rlimit if necessary.
Austin Schuh27553152020-11-18 21:26:37 -0800203 SetSoftRLimit(
204 RLIMIT_RTPRIO, priority, SetLimitForRoot::kNo,
205 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
206 "warning.",
207 AllowSoftLimitDecrease::kNo);
Brian Silvermanb3826f52020-07-02 19:41:18 -0700208
Alex Perrycb7da4b2019-08-28 19:35:56 -0700209 struct sched_param param;
210 param.sched_priority = priority;
Austin Schuhcc6070c2020-10-10 20:25:56 -0700211 MarkRealtime(true);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700212 PCHECK(sched_setscheduler(0, SCHED_FIFO, &param) == 0)
Austin Schuh27553152020-11-18 21:26:37 -0800213 << ": changing to SCHED_FIFO with " << priority
214 << ", if you want to bypass this check for testing, use "
215 "--skip_realtime_scheduler";
Alex Perrycb7da4b2019-08-28 19:35:56 -0700216}
217
218void WriteCoreDumps() {
219 // Do create core files of unlimited size.
Austin Schuh27553152020-11-18 21:26:37 -0800220 SetSoftRLimit(RLIMIT_CORE, RLIM_INFINITY, SetLimitForRoot::kYes, "");
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800221}
222
223void ExpandStackSize() {
Austin Schuh27553152020-11-18 21:26:37 -0800224 SetSoftRLimit(RLIMIT_STACK, 1000000, SetLimitForRoot::kYes, "",
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800225 AllowSoftLimitDecrease::kNo);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700226}
227
Austin Schuhcc6070c2020-10-10 20:25:56 -0700228namespace {
Austin Schuhf239e332021-07-30 15:27:26 -0700229// Bool to track if malloc hooks have failed to be configured.
230bool has_malloc_hook = true;
Austin Schuhf7bfb652023-08-25 14:22:50 -0700231thread_local bool is_realtime = false;
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700232} // namespace
Austin Schuhcc6070c2020-10-10 20:25:56 -0700233
234bool MarkRealtime(bool realtime) {
Austin Schuhf239e332021-07-30 15:27:26 -0700235 if (realtime) {
236 // For some applications (generally tools built for the host in Bazel), we
237 // don't have malloc hooks available, but we also don't go realtime. Delay
238 // complaining in that case until we try to go RT and it matters.
Victor Valenciab009bfc2022-09-30 13:30:11 -0700239#if !__has_feature(address_sanitizer) && !__has_feature(memory_sanitizer)
Austin Schuhf239e332021-07-30 15:27:26 -0700240 CHECK(has_malloc_hook)
241 << ": Failed to register required malloc hooks before going realtime. "
242 "Disable --die_on_malloc to continue.";
Victor Valenciab009bfc2022-09-30 13:30:11 -0700243#endif
Austin Schuhf239e332021-07-30 15:27:26 -0700244 }
Austin Schuhcc6070c2020-10-10 20:25:56 -0700245 const bool prior = is_realtime;
246 is_realtime = realtime;
247 return prior;
248}
249
250void CheckRealtime() { CHECK(is_realtime); }
251
252void CheckNotRealtime() { CHECK(!is_realtime); }
253
254ScopedRealtimeRestorer::ScopedRealtimeRestorer() : prior_(is_realtime) {}
255
Austin Schuh62288252020-11-18 23:26:04 -0800256void NewHook(const void *ptr, size_t size) {
257 if (is_realtime) {
258 is_realtime = false;
259 RAW_LOG(FATAL, "Malloced %p -> %zu bytes", ptr, size);
260 }
261}
262
263void DeleteHook(const void *ptr) {
Austin Schuhf239e332021-07-30 15:27:26 -0700264 // It is legal to call free(nullptr) unconditionally and assume that it won't
265 // do anything. Eigen does this. So, if we are RT, ignore any of these
266 // calls.
267 if (is_realtime && ptr != nullptr) {
Austin Schuh62288252020-11-18 23:26:04 -0800268 is_realtime = false;
269 RAW_LOG(FATAL, "Delete Hook %p", ptr);
270 }
271}
272
Tyler Chatow582c6c72021-07-16 13:45:07 -0700273extern "C" {
274
275// malloc hooks for libc. Tcmalloc will replace everything it finds (malloc,
276// __libc_malloc, etc.), so we need its specific hook above as well.
277void *aos_malloc_hook(size_t size) {
278 if (FLAGS_die_on_malloc && aos::is_realtime) {
279 aos::is_realtime = false;
280 RAW_LOG(FATAL, "Malloced %zu bytes", size);
281 return nullptr;
282 } else {
283 return __libc_malloc(size);
284 }
285}
286
287void aos_free_hook(void *ptr) {
288 if (FLAGS_die_on_malloc && aos::is_realtime && ptr != nullptr) {
289 aos::is_realtime = false;
290 RAW_LOG(FATAL, "Deleted %p", ptr);
291 } else {
292 __libc_free(ptr);
293 }
294}
295
296void *malloc(size_t size) __attribute__((weak, alias("aos_malloc_hook")));
297void free(void *ptr) __attribute__((weak, alias("aos_free_hook")));
Tyler Chatow582c6c72021-07-16 13:45:07 -0700298}
299
Austin Schuh77f3f222022-06-10 16:49:21 -0700300void FatalUnsetRealtimePriority() {
Austin Schuh4737ad62022-12-30 14:47:03 -0800301 int saved_errno = errno;
Austin Schuh77f3f222022-06-10 16:49:21 -0700302 // Drop our priority first. We are about to do lots of work to undo
303 // everything, don't get overly clever.
304 struct sched_param param;
Austin Schuh4737ad62022-12-30 14:47:03 -0800305 param.sched_priority = 0;
Austin Schuh77f3f222022-06-10 16:49:21 -0700306 sched_setscheduler(0, SCHED_OTHER, &param);
307
308 is_realtime = false;
309
310 // Put all sub-tasks back to non-rt priority too.
311 DIR *dirp = opendir("/proc/self/task");
312 if (dirp) {
313 struct dirent *directory_entry;
314 while ((directory_entry = readdir(dirp)) != NULL) {
315 int thread_id = std::atoi(directory_entry->d_name);
316
317 // ignore . and .. which are zeroes for some reason
318 if (thread_id != 0) {
319 struct sched_param param;
Austin Schuh4737ad62022-12-30 14:47:03 -0800320 param.sched_priority = 0;
Austin Schuh77f3f222022-06-10 16:49:21 -0700321 sched_setscheduler(thread_id, SCHED_OTHER, &param);
322 }
323 }
324 closedir(dirp);
325 }
Austin Schuh4737ad62022-12-30 14:47:03 -0800326 errno = saved_errno;
Austin Schuh77f3f222022-06-10 16:49:21 -0700327}
328
Austin Schuh62288252020-11-18 23:26:04 -0800329void RegisterMallocHook() {
330 if (FLAGS_die_on_malloc) {
Tyler Chatow582c6c72021-07-16 13:45:07 -0700331 // tcmalloc redefines __libc_malloc, so use this as a feature test.
332 if (&__libc_malloc == &tc_malloc) {
James Kuszmaul4af7fa62023-08-05 13:33:22 -0700333 if (VLOG_IS_ON(1)) {
334 RAW_LOG(INFO, "Hooking tcmalloc for die_on_malloc");
335 }
Tyler Chatow582c6c72021-07-16 13:45:07 -0700336 if (&MallocHook_AddNewHook != nullptr) {
337 CHECK(MallocHook_AddNewHook(&NewHook));
338 } else {
339 has_malloc_hook = false;
340 }
341 if (&MallocHook_AddDeleteHook != nullptr) {
342 CHECK(MallocHook_AddDeleteHook(&DeleteHook));
343 } else {
344 has_malloc_hook = false;
345 }
Austin Schuh62288252020-11-18 23:26:04 -0800346 } else {
Austin Schuh6d8afc02023-05-28 13:27:42 -0700347 if (VLOG_IS_ON(1)) {
348 RAW_LOG(INFO, "Replacing glibc malloc");
349 }
Tyler Chatow582c6c72021-07-16 13:45:07 -0700350 if (&malloc != &aos_malloc_hook) {
351 has_malloc_hook = false;
352 }
353 if (&free != &aos_free_hook) {
354 has_malloc_hook = false;
355 }
Austin Schuh62288252020-11-18 23:26:04 -0800356 }
357 }
358}
359
Alex Perrycb7da4b2019-08-28 19:35:56 -0700360} // namespace aos