• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/profiler/stack_buffer.h"
11 
12 #include <bit>
13 
14 #if BUILDFLAG(IS_CHROMEOS)
15 #include <sys/mman.h>
16 
17 #include <ostream>
18 
19 #include "base/bits.h"
20 #include "base/check.h"
21 #include "base/check_op.h"
22 #include "base/memory/page_size.h"
23 #endif  // #if BUILDFLAG(IS_CHROMEOS)
24 
25 namespace base {
26 
27 constexpr size_t StackBuffer::kPlatformStackAlignment;
28 
29 #if BUILDFLAG(IS_CHROMEOS)
30 
MarkUpperBufferContentsAsUnneeded(size_t retained_bytes)31 void StackBuffer::MarkUpperBufferContentsAsUnneeded(size_t retained_bytes) {
32   // Round up to the next multiple of the page size. madvise needs the
33   // starting address to be page aligned. Since buffer_.get() is
34   // already page aligned, we just need to round up the retained bytes.
35   size_t actual_retained_bytes = bits::AlignUp(retained_bytes, GetPageSize());
36 
37   // Avoid passing a negative discard_size to madvise(). Doing so would randomly
38   // discard large amounts of memory causing weird crashes.
39   CHECK_LE(actual_retained_bytes, size_);
40 
41   uint8_t* start_of_discard =
42       reinterpret_cast<uint8_t*>(buffer_.get()) + actual_retained_bytes;
43   size_t discard_size = size_ - actual_retained_bytes;
44   int result = madvise(start_of_discard, discard_size, MADV_DONTNEED);
45 
46   DPCHECK(result == 0) << "madvise failed: ";
47 }
48 
49 #endif  // #if BUILDFLAG(IS_CHROMEOS)
50 
StackBuffer(size_t buffer_size)51 StackBuffer::StackBuffer(size_t buffer_size)
52 #if BUILDFLAG(IS_CHROMEOS)
53     // On ChromeOS, we have 8MB of stack space per thread; however, we normally
54     // only use a small fraction of that. To avoid blowing our memory budget,
55     // we use madvise(MADV_DONTNEED) to let the kernel discard the memory in the
56     // 8MB buffer except when we are actively using it. For madvise() to work,
57     // we need |buffer_| to be aligned to a page boundary.
58     //
59     // We also need the |size_| to be a multiple of the page size so that we
60     // don't pass partial pages to madvise(). This isn't documented but the
61     // program will consistently crash otherwise.
62     : size_(bits::AlignUp(buffer_size, GetPageSize())),
63       buffer_(static_cast<uintptr_t*>(AlignedAlloc(size_, GetPageSize()))) {
64   // Our (very large) buffer may already have data written to it & thus have
65   // backing pages. Tell the kernel we don't need the current contents.
66   MarkUpperBufferContentsAsUnneeded(0);
67 }
68 #else   // #if BUILDFLAG(IS_CHROMEOS)
69     : size_(buffer_size),
70       buffer_(static_cast<uintptr_t*>(
71           AlignedAlloc(size_, kPlatformStackAlignment))) {
72   static_assert(std::has_single_bit(kPlatformStackAlignment));
73 }
74 #endif  // !#if BUILDFLAG(IS_CHROMEOS)
75 
76 StackBuffer::~StackBuffer() = default;
77 
78 }  // namespace base
79