1353944Sdim//===-- sanitizer_common_libcdep.cpp --------------------------------------===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// This file is shared between AddressSanitizer and ThreadSanitizer
10353944Sdim// run-time libraries.
11353944Sdim//===----------------------------------------------------------------------===//
12353944Sdim
13353944Sdim#include "sanitizer_allocator_interface.h"
14353944Sdim#include "sanitizer_common.h"
15353944Sdim#include "sanitizer_flags.h"
16353944Sdim#include "sanitizer_procmaps.h"
17353944Sdim
18353944Sdim
19353944Sdimnamespace __sanitizer {
20353944Sdim
21353944Sdimstatic void (*SoftRssLimitExceededCallback)(bool exceeded);
22353944Sdimvoid SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
23353944Sdim  CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
24353944Sdim  SoftRssLimitExceededCallback = Callback;
25353944Sdim}
26353944Sdim
27353944Sdim#if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO
28353944Sdim// Weak default implementation for when sanitizer_stackdepot is not linked in.
29353944SdimSANITIZER_WEAK_ATTRIBUTE StackDepotStats *StackDepotGetStats() {
30353944Sdim  return nullptr;
31353944Sdim}
32353944Sdim
33353944Sdimvoid BackgroundThread(void *arg) {
34353944Sdim  const uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
35353944Sdim  const uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
36353944Sdim  const bool heap_profile = common_flags()->heap_profile;
37353944Sdim  uptr prev_reported_rss = 0;
38353944Sdim  uptr prev_reported_stack_depot_size = 0;
39353944Sdim  bool reached_soft_rss_limit = false;
40353944Sdim  uptr rss_during_last_reported_profile = 0;
41353944Sdim  while (true) {
42353944Sdim    SleepForMillis(100);
43353944Sdim    const uptr current_rss_mb = GetRSS() >> 20;
44353944Sdim    if (Verbosity()) {
45353944Sdim      // If RSS has grown 10% since last time, print some information.
46353944Sdim      if (prev_reported_rss * 11 / 10 < current_rss_mb) {
47353944Sdim        Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
48353944Sdim        prev_reported_rss = current_rss_mb;
49353944Sdim      }
50353944Sdim      // If stack depot has grown 10% since last time, print it too.
51353944Sdim      StackDepotStats *stack_depot_stats = StackDepotGetStats();
52353944Sdim      if (stack_depot_stats) {
53353944Sdim        if (prev_reported_stack_depot_size * 11 / 10 <
54353944Sdim            stack_depot_stats->allocated) {
55353944Sdim          Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
56353944Sdim                 SanitizerToolName,
57353944Sdim                 stack_depot_stats->n_uniq_ids,
58353944Sdim                 stack_depot_stats->allocated >> 20);
59353944Sdim          prev_reported_stack_depot_size = stack_depot_stats->allocated;
60353944Sdim        }
61353944Sdim      }
62353944Sdim    }
63353944Sdim    // Check RSS against the limit.
64353944Sdim    if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
65353944Sdim      Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
66353944Sdim             SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
67353944Sdim      DumpProcessMap();
68353944Sdim      Die();
69353944Sdim    }
70353944Sdim    if (soft_rss_limit_mb) {
71353944Sdim      if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
72353944Sdim        reached_soft_rss_limit = true;
73353944Sdim        Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
74353944Sdim               SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
75353944Sdim        if (SoftRssLimitExceededCallback)
76353944Sdim          SoftRssLimitExceededCallback(true);
77353944Sdim      } else if (soft_rss_limit_mb >= current_rss_mb &&
78353944Sdim                 reached_soft_rss_limit) {
79353944Sdim        reached_soft_rss_limit = false;
80353944Sdim        if (SoftRssLimitExceededCallback)
81353944Sdim          SoftRssLimitExceededCallback(false);
82353944Sdim      }
83353944Sdim    }
84353944Sdim    if (heap_profile &&
85353944Sdim        current_rss_mb > rss_during_last_reported_profile * 1.1) {
86353944Sdim      Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb);
87353944Sdim      __sanitizer_print_memory_profile(90, 20);
88353944Sdim      rss_during_last_reported_profile = current_rss_mb;
89353944Sdim    }
90353944Sdim  }
91353944Sdim}
92353944Sdim#endif
93353944Sdim
94353944Sdimvoid WriteToSyslog(const char *msg) {
95353944Sdim  InternalScopedString msg_copy(kErrorMessageBufferSize);
96353944Sdim  msg_copy.append("%s", msg);
97353944Sdim  char *p = msg_copy.data();
98353944Sdim  char *q;
99353944Sdim
100353944Sdim  // Print one line at a time.
101353944Sdim  // syslog, at least on Android, has an implicit message length limit.
102353944Sdim  while ((q = internal_strchr(p, '\n'))) {
103353944Sdim    *q = '\0';
104353944Sdim    WriteOneLineToSyslog(p);
105353944Sdim    p = q + 1;
106353944Sdim  }
107353944Sdim  // Print remaining characters, if there are any.
108353944Sdim  // Note that this will add an extra newline at the end.
109353944Sdim  // FIXME: buffer extra output. This would need a thread-local buffer, which
110353944Sdim  // on Android requires plugging into the tools (ex. ASan's) Thread class.
111353944Sdim  if (*p)
112353944Sdim    WriteOneLineToSyslog(p);
113353944Sdim}
114353944Sdim
115353944Sdimvoid MaybeStartBackgroudThread() {
116353944Sdim#if (SANITIZER_LINUX || SANITIZER_NETBSD) && \
117353944Sdim    !SANITIZER_GO  // Need to implement/test on other platforms.
118353944Sdim  // Start the background thread if one of the rss limits is given.
119353944Sdim  if (!common_flags()->hard_rss_limit_mb &&
120353944Sdim      !common_flags()->soft_rss_limit_mb &&
121353944Sdim      !common_flags()->heap_profile) return;
122353944Sdim  if (!&real_pthread_create) return;  // Can't spawn the thread anyway.
123353944Sdim  internal_start_thread(BackgroundThread, nullptr);
124353944Sdim#endif
125353944Sdim}
126353944Sdim
127353944Sdimstatic void (*sandboxing_callback)();
128353944Sdimvoid SetSandboxingCallback(void (*f)()) {
129353944Sdim  sandboxing_callback = f;
130353944Sdim}
131353944Sdim
132353944Sdim}  // namespace __sanitizer
133353944Sdim
134353944SdimSANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
135353944Sdim                             __sanitizer_sandbox_arguments *args) {
136353944Sdim  __sanitizer::PlatformPrepareForSandboxing(args);
137353944Sdim  if (__sanitizer::sandboxing_callback)
138353944Sdim    __sanitizer::sandboxing_callback();
139353944Sdim}
140