blob: e277803b7c107dcf3341571ddbd2f96523f80e6b [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 <errno.h>
4#include <malloc.h>
5#include <sched.h>
6#include <stdint.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include <stdio.h>
Austin Schuhcc6070c2020-10-10 20:25:56 -07008#include <stdlib.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include <string.h>
10#include <sys/mman.h>
Austin Schuhcc6070c2020-10-10 20:25:56 -070011#include <sys/prctl.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -070012#include <sys/resource.h>
13#include <sys/types.h>
14#include <unistd.h>
Alex Perrycb7da4b2019-08-28 19:35:56 -070015
Austin Schuhcc6070c2020-10-10 20:25:56 -070016#include "aos/thread_local.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070017#include "glog/logging.h"
Austin Schuh62288252020-11-18 23:26:04 -080018#include "glog/raw_logging.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070019
Austin Schuh62288252020-11-18 23:26:04 -080020DEFINE_bool(
21 die_on_malloc, false,
22 "If true, die when the application allocates memory in a RT section.");
Austin Schuh27553152020-11-18 21:26:37 -080023DEFINE_bool(skip_realtime_scheduler, false,
24 "If true, skip changing the scheduler. Pretend that we changed "
25 "the scheduler instead.");
26DEFINE_bool(skip_locking_memory, false,
27 "If true, skip locking memory. Pretend that we did it instead.");
28
Austin Schuh62288252020-11-18 23:26:04 -080029extern "C" {
30typedef void (*MallocHook_NewHook)(const void* ptr, size_t size);
31int MallocHook_AddNewHook(MallocHook_NewHook hook) __attribute__((weak));
32int MallocHook_RemoveNewHook(MallocHook_NewHook hook) __attribute__((weak));
33
34typedef void (*MallocHook_DeleteHook)(const void* ptr);
35int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) __attribute__((weak));
36int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook) __attribute__((weak));
37} // extern "C"
38
Alex Perrycb7da4b2019-08-28 19:35:56 -070039namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
40extern double FLAGS_tcmalloc_release_rate __attribute__((weak));
41}
42using FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead::
43 FLAGS_tcmalloc_release_rate;
44
45namespace aos {
46namespace logging {
47namespace internal {
48
49// Implemented in aos/logging/context.cc.
50void ReloadThreadName() __attribute__((weak));
51
52} // namespace internal
53} // namespace logging
54
55namespace {
56
Austin Schuh27553152020-11-18 21:26:37 -080057enum class SetLimitForRoot { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080058
Austin Schuh27553152020-11-18 21:26:37 -080059enum class AllowSoftLimitDecrease { kYes, kNo };
James Kuszmaulb4874eb2020-01-18 17:50:35 -080060
61void SetSoftRLimit(
62 int resource, rlim64_t soft, SetLimitForRoot set_for_root,
Austin Schuh27553152020-11-18 21:26:37 -080063 std::string_view help_string,
James Kuszmaulb4874eb2020-01-18 17:50:35 -080064 AllowSoftLimitDecrease allow_decrease = AllowSoftLimitDecrease::kYes) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070065 bool am_root = getuid() == 0;
James Kuszmaulb4874eb2020-01-18 17:50:35 -080066 if (set_for_root == SetLimitForRoot::kYes || !am_root) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070067 struct rlimit64 rlim;
68 PCHECK(getrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070069 << ": getting limit for " << resource;
Alex Perrycb7da4b2019-08-28 19:35:56 -070070
James Kuszmaulb4874eb2020-01-18 17:50:35 -080071 if (allow_decrease == AllowSoftLimitDecrease::kYes) {
72 rlim.rlim_cur = soft;
73 } else {
74 rlim.rlim_cur = std::max(rlim.rlim_cur, soft);
75 }
Alex Perrycb7da4b2019-08-28 19:35:56 -070076 rlim.rlim_max = ::std::max(rlim.rlim_max, soft);
77
78 PCHECK(setrlimit64(resource, &rlim) == 0)
Brian Silverman6a54ff32020-04-28 16:41:39 -070079 << ": changing limit for " << resource << " to " << rlim.rlim_cur
Austin Schuh27553152020-11-18 21:26:37 -080080 << " with max of " << rlim.rlim_max << help_string;
Alex Perrycb7da4b2019-08-28 19:35:56 -070081 }
82}
83
84} // namespace
85
86void LockAllMemory() {
Austin Schuhcc6070c2020-10-10 20:25:56 -070087 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -070088 // Allow locking as much as we want into RAM.
Austin Schuh27553152020-11-18 21:26:37 -080089 SetSoftRLimit(RLIMIT_MEMLOCK, RLIM_INFINITY, SetLimitForRoot::kNo,
90 "use --skip_locking_memory to not lock memory.");
Alex Perrycb7da4b2019-08-28 19:35:56 -070091
92 WriteCoreDumps();
Austin Schuh27553152020-11-18 21:26:37 -080093 PCHECK(mlockall(MCL_CURRENT | MCL_FUTURE) == 0)
94 << ": Failed to lock memory, use --skip_locking_memory to bypass this. "
95 "Bypassing will impact RT performance.";
Alex Perrycb7da4b2019-08-28 19:35:56 -070096
Brian Silverman4dbbcce2020-09-18 15:27:38 -070097#if !__has_feature(address_sanitizer) && !__has_feature(memory_sanitizer)
Alex Perrycb7da4b2019-08-28 19:35:56 -070098 // Don't give freed memory back to the OS.
99 CHECK_EQ(1, mallopt(M_TRIM_THRESHOLD, -1));
100 // Don't use mmap for large malloc chunks.
101 CHECK_EQ(1, mallopt(M_MMAP_MAX, 0));
Austin Schuh85faf672020-09-10 22:58:46 -0700102#endif
Alex Perrycb7da4b2019-08-28 19:35:56 -0700103
104 if (&FLAGS_tcmalloc_release_rate) {
105 // Tell tcmalloc not to return memory.
106 FLAGS_tcmalloc_release_rate = 0.0;
107 }
108
109 // Forces the memory pages for all the stack space that we're ever going to
110 // use to be loaded into memory (so it can be locked there).
111 uint8_t data[4096 * 8];
112 // Not 0 because linux might optimize that to a 0-filled page.
113 memset(data, 1, sizeof(data));
Austin Schuh27553152020-11-18 21:26:37 -0800114 __asm__ __volatile__("" ::"m"(data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700115
116 static const size_t kHeapPreallocSize = 512 * 1024;
117 char *const heap_data = static_cast<char *>(malloc(kHeapPreallocSize));
118 memset(heap_data, 1, kHeapPreallocSize);
Austin Schuh27553152020-11-18 21:26:37 -0800119 __asm__ __volatile__("" ::"m"(heap_data));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700120 free(heap_data);
121}
122
123void InitRT() {
Austin Schuh27553152020-11-18 21:26:37 -0800124 if (FLAGS_skip_locking_memory) {
125 LOG(WARNING) << "Ignoring request to lock all memory due to "
126 "--skip_locking_memory.";
127 return;
128 }
129
Austin Schuhcc6070c2020-10-10 20:25:56 -0700130 CheckNotRealtime();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700131 LockAllMemory();
132
Austin Schuh27553152020-11-18 21:26:37 -0800133 if (FLAGS_skip_realtime_scheduler) {
134 return;
135 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700136 // Only let rt processes run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800137 SetSoftRLimit(
138 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
139 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
140 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700141
142 // Allow rt processes up to priority 40.
Austin Schuh27553152020-11-18 21:26:37 -0800143 SetSoftRLimit(
144 RLIMIT_RTPRIO, 40, SetLimitForRoot::kNo,
145 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
146 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700147}
148
149void UnsetCurrentThreadRealtimePriority() {
150 struct sched_param param;
151 param.sched_priority = 0;
Brian Silverman6a54ff32020-04-28 16:41:39 -0700152 PCHECK(sched_setscheduler(0, SCHED_OTHER, &param) == 0);
Austin Schuhcc6070c2020-10-10 20:25:56 -0700153 MarkRealtime(false);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700154}
155
156void SetCurrentThreadAffinity(const cpu_set_t &cpuset) {
157 PCHECK(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700158}
159
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800160void SetCurrentThreadName(const std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700161 CHECK_LE(name.size(), 16u) << ": thread name '" << name << "' too long";
162 VLOG(1) << "This thread is changing to '" << name << "'";
James Kuszmaul57c2baa2020-01-19 14:52:52 -0800163 std::string string_name(name);
Brian Silverman6a54ff32020-04-28 16:41:39 -0700164 PCHECK(prctl(PR_SET_NAME, string_name.c_str()) == 0)
165 << ": changing name to " << string_name;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700166 if (&logging::internal::ReloadThreadName != nullptr) {
167 logging::internal::ReloadThreadName();
168 }
169}
170
171void SetCurrentThreadRealtimePriority(int priority) {
Austin Schuh27553152020-11-18 21:26:37 -0800172 if (FLAGS_skip_realtime_scheduler) {
173 LOG(WARNING) << "Ignoring request to switch to the RT scheduler due to "
174 "--skip_realtime_scheduler.";
175 return;
176 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700177 // Make sure we will only be allowed to run for 3 seconds straight.
Austin Schuh27553152020-11-18 21:26:37 -0800178 SetSoftRLimit(
179 RLIMIT_RTTIME, 3000000, SetLimitForRoot::kYes,
180 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
181 "warning.");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700182
Brian Silvermanb3826f52020-07-02 19:41:18 -0700183 // Raise our soft rlimit if necessary.
Austin Schuh27553152020-11-18 21:26:37 -0800184 SetSoftRLimit(
185 RLIMIT_RTPRIO, priority, SetLimitForRoot::kNo,
186 ", use --skip_realtime_scheduler to stay non-rt and bypass this "
187 "warning.",
188 AllowSoftLimitDecrease::kNo);
Brian Silvermanb3826f52020-07-02 19:41:18 -0700189
Alex Perrycb7da4b2019-08-28 19:35:56 -0700190 struct sched_param param;
191 param.sched_priority = priority;
Austin Schuhcc6070c2020-10-10 20:25:56 -0700192 MarkRealtime(true);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700193 PCHECK(sched_setscheduler(0, SCHED_FIFO, &param) == 0)
Austin Schuh27553152020-11-18 21:26:37 -0800194 << ": changing to SCHED_FIFO with " << priority
195 << ", if you want to bypass this check for testing, use "
196 "--skip_realtime_scheduler";
Alex Perrycb7da4b2019-08-28 19:35:56 -0700197}
198
199void WriteCoreDumps() {
200 // Do create core files of unlimited size.
Austin Schuh27553152020-11-18 21:26:37 -0800201 SetSoftRLimit(RLIMIT_CORE, RLIM_INFINITY, SetLimitForRoot::kYes, "");
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800202}
203
204void ExpandStackSize() {
Austin Schuh27553152020-11-18 21:26:37 -0800205 SetSoftRLimit(RLIMIT_STACK, 1000000, SetLimitForRoot::kYes, "",
James Kuszmaulb4874eb2020-01-18 17:50:35 -0800206 AllowSoftLimitDecrease::kNo);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700207}
208
Austin Schuhcc6070c2020-10-10 20:25:56 -0700209namespace {
Austin Schuhf239e332021-07-30 15:27:26 -0700210// Bool to track if malloc hooks have failed to be configured.
211bool has_malloc_hook = true;
Austin Schuhcc6070c2020-10-10 20:25:56 -0700212AOS_THREAD_LOCAL bool is_realtime = false;
213}
214
215bool MarkRealtime(bool realtime) {
Austin Schuhf239e332021-07-30 15:27:26 -0700216 if (realtime) {
217 // For some applications (generally tools built for the host in Bazel), we
218 // don't have malloc hooks available, but we also don't go realtime. Delay
219 // complaining in that case until we try to go RT and it matters.
220 CHECK(has_malloc_hook)
221 << ": Failed to register required malloc hooks before going realtime. "
222 "Disable --die_on_malloc to continue.";
223 }
Austin Schuhcc6070c2020-10-10 20:25:56 -0700224 const bool prior = is_realtime;
225 is_realtime = realtime;
226 return prior;
227}
228
229void CheckRealtime() { CHECK(is_realtime); }
230
231void CheckNotRealtime() { CHECK(!is_realtime); }
232
233ScopedRealtimeRestorer::ScopedRealtimeRestorer() : prior_(is_realtime) {}
234
Austin Schuh62288252020-11-18 23:26:04 -0800235void NewHook(const void *ptr, size_t size) {
236 if (is_realtime) {
237 is_realtime = false;
238 RAW_LOG(FATAL, "Malloced %p -> %zu bytes", ptr, size);
239 }
240}
241
242void DeleteHook(const void *ptr) {
Austin Schuhf239e332021-07-30 15:27:26 -0700243 // It is legal to call free(nullptr) unconditionally and assume that it won't
244 // do anything. Eigen does this. So, if we are RT, ignore any of these
245 // calls.
246 if (is_realtime && ptr != nullptr) {
Austin Schuh62288252020-11-18 23:26:04 -0800247 is_realtime = false;
248 RAW_LOG(FATAL, "Delete Hook %p", ptr);
249 }
250}
251
252void RegisterMallocHook() {
253 if (FLAGS_die_on_malloc) {
254 if (&MallocHook_AddNewHook != nullptr) {
255 CHECK(MallocHook_AddNewHook(&NewHook));
256 } else {
Austin Schuhf239e332021-07-30 15:27:26 -0700257 has_malloc_hook = false;
Austin Schuh62288252020-11-18 23:26:04 -0800258 }
259 if (&MallocHook_AddDeleteHook != nullptr) {
260 CHECK(MallocHook_AddDeleteHook(&DeleteHook));
261 } else {
Austin Schuhf239e332021-07-30 15:27:26 -0700262 has_malloc_hook = false;
Austin Schuh62288252020-11-18 23:26:04 -0800263 }
264 }
265}
266
Alex Perrycb7da4b2019-08-28 19:35:56 -0700267} // namespace aos