blob: ef98671609968b2de1146077893731e95e844b0d [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#include "aos/realtime.h"
2
Austin Schuhcc6070c2020-10-10 20:25:56 -07003#include <malloc.h>
4#include <sched.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07005#include <sys/mman.h>
Austin Schuhcc6070c2020-10-10 20:25:56 -07006#include <sys/prctl.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include <sys/resource.h>
8#include <sys/types.h>
9#include <unistd.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -070010
Tyler Chatowbf0609c2021-07-31 16:13:27 -070011#include <cerrno>
12#include <cstdint>
13#include <cstdio>
14#include <cstdlib>
15#include <cstring>
16
Austin Schuhcc6070c2020-10-10 20:25:56 -070017#include "aos/thread_local.h"
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
Austin Schuh62288252020-11-18 23:26:04 -080021DEFINE_bool(
22 die_on_malloc, false,
23 "If true, die when the application allocates memory in a RT section.");
Austin Schuh27553152020-11-18 21:26:37 -080024DEFINE_bool(skip_realtime_scheduler, false,
25 "If true, skip changing the scheduler. Pretend that we changed "
26 "the scheduler instead.");
27DEFINE_bool(skip_locking_memory, false,
28 "If true, skip locking memory. Pretend that we did it instead.");
29
Austin Schuh62288252020-11-18 23:26:04 -080030extern "C" {
Tyler Chatowbf0609c2021-07-31 16:13:27 -070031typedef void (*MallocHook_NewHook)(const void *ptr, size_t size);
Austin Schuh62288252020-11-18 23:26:04 -080032int MallocHook_AddNewHook(MallocHook_NewHook hook) __attribute__((weak));
33int MallocHook_RemoveNewHook(MallocHook_NewHook hook) __attribute__((weak));
34
Tyler Chatowbf0609c2021-07-31 16:13:27 -070035typedef void (*MallocHook_DeleteHook)(const void *ptr);
Austin Schuh62288252020-11-18 23:26:04 -080036int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) __attribute__((weak));
Tyler Chatowbf0609c2021-07-31 16:13:27 -070037int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook)
38 __attribute__((weak));
Tyler Chatow582c6c72021-07-16 13:45:07 -070039
40// Declare tc_malloc weak so we can check if it exists.
41void *tc_malloc(size_t size) __attribute__((weak));
42
43void *__libc_malloc(size_t size);
44void __libc_free(void *ptr);
Tyler Chatowbf0609c2021-07-31 16:13:27 -070045} // extern "C"
Austin Schuh62288252020-11-18 23:26:04 -080046
Alex Perrycb7da4b2019-08-28 19:35:56 -070047namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
48extern double FLAGS_tcmalloc_release_rate __attribute__((weak));
49}
50using FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead::
51 FLAGS_tcmalloc_release_rate;
52
53namespace aos {
54namespace logging {
55namespace internal {
56
57// Implemented in aos/logging/context.cc.
58void ReloadThreadName() __attribute__((weak));
59
60} // namespace internal
61} // namespace logging
62
63namespace {
64
Austin Schuh27553152020-11-18 21:26:37 -080065enum class SetLimitForRoot { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080066
Austin Schuh27553152020-11-18 21:26:37 -080067enum class AllowSoftLimitDecrease { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080068
69void SetSoftRLimit(
70 int resource, rlim64_t soft, SetLimitForRoot set_for_root,
Austin Schuh27553152020-11-18 21:26:37 -080071 std::string_view help_string,
James Kuszmaulb4874eb2020-01-18 17:50:35 -080072 AllowSoftLimitDecrease allow_decrease = AllowSoftLimitDecrease::kYes) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070073 bool am_root = getuid() == 0;
James Kuszmaulb4874eb2020-01-18 17:50:35 -080074 if (set_for_root == SetLimitForRoot::kYes || !am_root) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070075 struct rlimit64 rlim;
76 PCHECK(getrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070077 << ": getting limit for " << resource;
Alex Perrycb7da4b2019-08-28 19:35:56 -070078
James Kuszmaulb4874eb2020-01-18 17:50:35 -080079 if (allow_decrease == AllowSoftLimitDecrease::kYes) {
80 rlim.rlim_cur = soft;
81 } else {
82 rlim.rlim_cur = std::max(rlim.rlim_cur, soft);
83 }
Alex Perrycb7da4b2019-08-28 19:35:56 -070084 rlim.rlim_max = ::std::max(rlim.rlim_max, soft);
85
86 PCHECK(setrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070087 << ": changing limit for " << resource << " to " << rlim.rlim_cur
Austin Schuh27553152020-11-18 21:26:37 -080088 << " with max of " << rlim.rlim_max << help_string;
Alex Perrycb7da4b2019-08-28 19:35:56 -070089 }
90}
91
92} // namespace
93
94void LockAllMemory() {
Austin Schuhcc6070c2020-10-10 20:25:56 -070095 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -070096 // Allow locking as much as we want into RAM.
Austin Schuh27553152020-11-18 21:26:37 -080097 SetSoftRLimit(RLIMIT_MEMLOCK, RLIM_INFINITY, SetLimitForRoot::kNo,
98 "use --skip_locking_memory to not lock memory.");
Alex Perrycb7da4b2019-08-28 19:35:56 -070099
Austin Schuh27553152020-11-18 21:26:37 -0800100 PCHECK(mlockall(MCL_CURRENT | MCL_FUTURE) == 0)
101 << ": Failed to lock memory, use --skip_locking_memory to bypass this. "
102 "Bypassing will impact RT performance.";
Alex Perrycb7da4b2019-08-28 19:35:56 -0700103
Brian Silverman4dbbcce2020-09-18 15:27:38 -0700104#if !__has_feature(address_sanitizer) && !__has_feature(memory_sanitizer)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700105 // Don't give freed memory back to the OS.
106 CHECK_EQ(1, mallopt(M_TRIM_THRESHOLD, -1));
107 // Don't use mmap for large malloc chunks.
108 CHECK_EQ(1, mallopt(M_MMAP_MAX, 0));
Austin Schuh85faf672020-09-10 22:58:46 -0700109#endif
Alex Perrycb7da4b2019-08-28 19:35:56 -0700110
111 if (&FLAGS_tcmalloc_release_rate) {
112 // Tell tcmalloc not to return memory.
113 FLAGS_tcmalloc_release_rate = 0.0;
114 }
115
116 // Forces the memory pages for all the stack space that we're ever going to
117 // use to be loaded into memory (so it can be locked there).
118 uint8_t data[4096 * 8];
119 // Not 0 because linux might optimize that to a 0-filled page.
120 memset(data, 1, sizeof(data));
Austin Schuh27553152020-11-18 21:26:37 -0800121 __asm__ __volatile__("" ::"m"(data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700122
123 static const size_t kHeapPreallocSize = 512 * 1024;
124 char *const heap_data = static_cast<char *>(malloc(kHeapPreallocSize));
125 memset(heap_data, 1, kHeapPreallocSize);
Austin Schuh27553152020-11-18 21:26:37 -0800126 __asm__ __volatile__("" ::"m"(heap_data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700127 free(heap_data);
128}
129
130void InitRT() {
Austin Schuh27553152020-11-18 21:26:37 -0800131 if (FLAGS_skip_locking_memory) {
132 LOG(WARNING) << "Ignoring request to lock all memory due to "
133 "--skip_locking_memory.";
134 return;
135 }
136
Austin Schuhcc6070c2020-10-10 20:25:56 -0700137 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700138 LockAllMemory();
139
Austin Schuh27553152020-11-18 21:26:37 -0800140 if (FLAGS_skip_realtime_scheduler) {
141 return;
142 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700143 // Only let rt processes run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800144 SetSoftRLimit(
145 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
146 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
147 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700148
149 // Allow rt processes up to priority 40.
Austin Schuh27553152020-11-18 21:26:37 -0800150 SetSoftRLimit(
151 RLIMIT_RTPRIO, 40, SetLimitForRoot::kNo,
152 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
153 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700154}
155
156void UnsetCurrentThreadRealtimePriority() {
157 struct sched_param param;
158 param.sched_priority = 0;
Brian Silverman6a54ff32020-04-28 16:41:39 -0700159 PCHECK(sched_setscheduler(0, SCHED_OTHER, &param) == 0);
Austin Schuhcc6070c2020-10-10 20:25:56 -0700160 MarkRealtime(false);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700161}
162
163void SetCurrentThreadAffinity(const cpu_set_t &cpuset) {
164 PCHECK(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700165}
166
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800167void SetCurrentThreadName(const std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700168 CHECK_LE(name.size(), 16u) << ": thread name '" << name << "' too long";
169 VLOG(1) << "This thread is changing to '" << name << "'";
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800170 std::string string_name(name);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700171 PCHECK(prctl(PR_SET_NAME, string_name.c_str()) == 0)
172 << ": changing name to " << string_name;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700173 if (&logging::internal::ReloadThreadName != nullptr) {
174 logging::internal::ReloadThreadName();
175 }
176}
177
Austin Schuhde973292021-10-12 18:09:49 -0700178cpu_set_t GetCurrentThreadAffinity() {
179 cpu_set_t result;
180 PCHECK(sched_getaffinity(0, sizeof(result), &result) == 0);
181 return result;
182}
183
Alex Perrycb7da4b2019-08-28 19:35:56 -0700184void SetCurrentThreadRealtimePriority(int priority) {
Austin Schuh27553152020-11-18 21:26:37 -0800185 if (FLAGS_skip_realtime_scheduler) {
186 LOG(WARNING) << "Ignoring request to switch to the RT scheduler due to "
187 "--skip_realtime_scheduler.";
188 return;
189 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700190 // Make sure we will only be allowed to run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800191 SetSoftRLimit(
192 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
193 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
194 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700195
Brian Silvermanb3826f52020-07-02 19:41:18 -0700196 // Raise our soft rlimit if necessary.
Austin Schuh27553152020-11-18 21:26:37 -0800197 SetSoftRLimit(
198 RLIMIT_RTPRIO, priority, SetLimitForRoot::kNo,
199 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
200 "warning.",
201 AllowSoftLimitDecrease::kNo);
Brian Silvermanb3826f52020-07-02 19:41:18 -0700202
Alex Perrycb7da4b2019-08-28 19:35:56 -0700203 struct sched_param param;
204 param.sched_priority = priority;
Austin Schuhcc6070c2020-10-10 20:25:56 -0700205 MarkRealtime(true);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700206 PCHECK(sched_setscheduler(0, SCHED_FIFO, &param) == 0)
Austin Schuh27553152020-11-18 21:26:37 -0800207 << ": changing to SCHED_FIFO with " << priority
208 << ", if you want to bypass this check for testing, use "
209 "--skip_realtime_scheduler";
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210}
211
212void WriteCoreDumps() {
213 // Do create core files of unlimited size.
Austin Schuh27553152020-11-18 21:26:37 -0800214 SetSoftRLimit(RLIMIT_CORE, RLIM_INFINITY, SetLimitForRoot::kYes, "");
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800215}
216
217void ExpandStackSize() {
Austin Schuh27553152020-11-18 21:26:37 -0800218 SetSoftRLimit(RLIMIT_STACK, 1000000, SetLimitForRoot::kYes, "",
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800219 AllowSoftLimitDecrease::kNo);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700220}
221
Austin Schuhcc6070c2020-10-10 20:25:56 -0700222namespace {
Austin Schuhf239e332021-07-30 15:27:26 -0700223// Bool to track if malloc hooks have failed to be configured.
224bool has_malloc_hook = true;
Austin Schuhcc6070c2020-10-10 20:25:56 -0700225AOS_THREAD_LOCAL bool is_realtime = false;
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700226} // namespace
Austin Schuhcc6070c2020-10-10 20:25:56 -0700227
228bool MarkRealtime(bool realtime) {
Austin Schuhf239e332021-07-30 15:27:26 -0700229 if (realtime) {
230 // For some applications (generally tools built for the host in Bazel), we
231 // don't have malloc hooks available, but we also don't go realtime. Delay
232 // complaining in that case until we try to go RT and it matters.
233 CHECK(has_malloc_hook)
234 << ": Failed to register required malloc hooks before going realtime. "
235 "Disable --die_on_malloc to continue.";
236 }
Austin Schuhcc6070c2020-10-10 20:25:56 -0700237 const bool prior = is_realtime;
238 is_realtime = realtime;
239 return prior;
240}
241
242void CheckRealtime() { CHECK(is_realtime); }
243
244void CheckNotRealtime() { CHECK(!is_realtime); }
245
246ScopedRealtimeRestorer::ScopedRealtimeRestorer() : prior_(is_realtime) {}
247
Austin Schuh62288252020-11-18 23:26:04 -0800248void NewHook(const void *ptr, size_t size) {
249 if (is_realtime) {
250 is_realtime = false;
251 RAW_LOG(FATAL, "Malloced %p -> %zu bytes", ptr, size);
252 }
253}
254
255void DeleteHook(const void *ptr) {
Austin Schuhf239e332021-07-30 15:27:26 -0700256 // It is legal to call free(nullptr) unconditionally and assume that it won't
257 // do anything. Eigen does this. So, if we are RT, ignore any of these
258 // calls.
259 if (is_realtime && ptr != nullptr) {
Austin Schuh62288252020-11-18 23:26:04 -0800260 is_realtime = false;
261 RAW_LOG(FATAL, "Delete Hook %p", ptr);
262 }
263}
264
Tyler Chatow582c6c72021-07-16 13:45:07 -0700265extern "C" {
266
267// malloc hooks for libc. Tcmalloc will replace everything it finds (malloc,
268// __libc_malloc, etc.), so we need its specific hook above as well.
269void *aos_malloc_hook(size_t size) {
270 if (FLAGS_die_on_malloc && aos::is_realtime) {
271 aos::is_realtime = false;
272 RAW_LOG(FATAL, "Malloced %zu bytes", size);
273 return nullptr;
274 } else {
275 return __libc_malloc(size);
276 }
277}
278
279void aos_free_hook(void *ptr) {
280 if (FLAGS_die_on_malloc && aos::is_realtime && ptr != nullptr) {
281 aos::is_realtime = false;
282 RAW_LOG(FATAL, "Deleted %p", ptr);
283 } else {
284 __libc_free(ptr);
285 }
286}
287
288void *malloc(size_t size) __attribute__((weak, alias("aos_malloc_hook")));
289void free(void *ptr) __attribute__((weak, alias("aos_free_hook")));
Tyler Chatow582c6c72021-07-16 13:45:07 -0700290}
291
Austin Schuh62288252020-11-18 23:26:04 -0800292void RegisterMallocHook() {
293 if (FLAGS_die_on_malloc) {
Tyler Chatow582c6c72021-07-16 13:45:07 -0700294 // tcmalloc redefines __libc_malloc, so use this as a feature test.
295 if (&__libc_malloc == &tc_malloc) {
296 RAW_LOG(INFO, "Hooking tcmalloc for die_on_malloc");
297 if (&MallocHook_AddNewHook != nullptr) {
298 CHECK(MallocHook_AddNewHook(&NewHook));
299 } else {
300 has_malloc_hook = false;
301 }
302 if (&MallocHook_AddDeleteHook != nullptr) {
303 CHECK(MallocHook_AddDeleteHook(&DeleteHook));
304 } else {
305 has_malloc_hook = false;
306 }
Austin Schuh62288252020-11-18 23:26:04 -0800307 } else {
Tyler Chatow582c6c72021-07-16 13:45:07 -0700308 RAW_LOG(INFO, "Replacing glibc malloc");
309 if (&malloc != &aos_malloc_hook) {
310 has_malloc_hook = false;
311 }
312 if (&free != &aos_free_hook) {
313 has_malloc_hook = false;
314 }
Austin Schuh62288252020-11-18 23:26:04 -0800315 }
316 }
317}
318
Alex Perrycb7da4b2019-08-28 19:35:56 -0700319} // namespace aos