1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_malloc/malloc.h"
16
17 #include <cstdint>
18
19 #include "pw_tokenizer/detokenize.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace {
23
24 using namespace std::literals::string_view_literals;
25
26 std::array<std::byte, 8192> system_heap;
27
28 class MallocTest : public ::pw::unit_test::internal::Test {
29 public:
30 // NOTE! This tests should only be run WITHOUT a pw_malloc backend set.
31 // Otherwise, `InitSystemAllocator` may have already been called and may
32 // crash.
SetUpTestSuite()33 static void SetUpTestSuite() { pw::malloc::InitSystemAllocator(system_heap); }
34
35 protected:
36 // NOTE!! This tests ONLY run on host with the "light" framework. Other
37 // test frameworks may attempt and fail to de/allocate outside the test method
38 // outside the test body.
SetUp()39 void SetUp() override {
40 auto& system_metrics = pw::malloc::GetSystemMetrics();
41 ASSERT_EQ(system_metrics.allocated_bytes.value(), 0U);
42 }
43
TearDown()44 void TearDown() override {
45 auto& system_metrics = pw::malloc::GetSystemMetrics();
46 ASSERT_EQ(system_metrics.allocated_bytes.value(), 0U);
47 }
48 };
49
TEST_F(MallocTest,MallocFree)50 TEST_F(MallocTest, MallocFree) {
51 constexpr size_t kSize = 256;
52 auto& system_metrics = pw::malloc::GetSystemMetrics();
53
54 void* ptr = malloc(kSize);
55 ASSERT_NE(ptr, nullptr);
56 EXPECT_EQ(system_metrics.requested_bytes.value(), kSize);
57
58 free(ptr);
59 EXPECT_EQ(system_metrics.allocated_bytes.value(), 0U);
60 }
61
TEST_F(MallocTest,NewDelete)62 TEST_F(MallocTest, NewDelete) {
63 constexpr size_t kSize = 256;
64 auto& system_metrics = pw::malloc::GetSystemMetrics();
65
66 auto* ptr = new std::array<std::byte, kSize>();
67 // Prevent elision of the allocation.
68 pw::test::DoNotOptimize(ptr);
69 ASSERT_NE(ptr, nullptr);
70 EXPECT_GE(system_metrics.allocated_bytes.value(), kSize);
71
72 delete (ptr);
73 EXPECT_EQ(system_metrics.allocated_bytes.value(), 0U);
74 }
75
TEST_F(MallocTest,CallocFree)76 TEST_F(MallocTest, CallocFree) {
77 constexpr size_t kNum = 4;
78 constexpr size_t kSize = 64;
79 auto& system_metrics = pw::malloc::GetSystemMetrics();
80
81 void* ptr = calloc(kNum, kSize);
82 ASSERT_NE(ptr, nullptr);
83 EXPECT_EQ(system_metrics.requested_bytes.value(), kNum * kSize);
84
85 free(ptr);
86 EXPECT_EQ(system_metrics.allocated_bytes.value(), 0U);
87 }
88
TEST_F(MallocTest,ReallocFree)89 TEST_F(MallocTest, ReallocFree) {
90 constexpr size_t kSize = 256;
91 auto& system_metrics = pw::malloc::GetSystemMetrics();
92
93 void* ptr = realloc(nullptr, kSize);
94 ASSERT_NE(ptr, nullptr);
95 EXPECT_EQ(system_metrics.requested_bytes.value(), kSize);
96
97 free(ptr);
98 EXPECT_EQ(system_metrics.allocated_bytes.value(), 0U);
99 }
100
TEST_F(MallocTest,MallocReallocFree)101 TEST_F(MallocTest, MallocReallocFree) {
102 constexpr size_t kSize1 = 256;
103 constexpr size_t kSize2 = 512;
104 auto& system_metrics = pw::malloc::GetSystemMetrics();
105
106 void* ptr = malloc(kSize1);
107 ASSERT_NE(ptr, nullptr);
108 EXPECT_EQ(system_metrics.requested_bytes.value(), kSize1);
109 std::memset(ptr, 1, kSize1);
110
111 void* new_ptr = realloc(ptr, kSize2);
112 ASSERT_NE(ptr, nullptr);
113 EXPECT_EQ(system_metrics.requested_bytes.value(), kSize2);
114
115 // Using `new_ptr` prevents the call to `realloc from being optimized away.
116 auto* bytes = std::launder(reinterpret_cast<uint8_t*>(new_ptr));
117 for (size_t i = 0; i < kSize1; ++i) {
118 EXPECT_EQ(bytes[i], 1U);
119 }
120
121 free(new_ptr);
122 EXPECT_EQ(system_metrics.allocated_bytes.value(), 0U);
123 }
124
125 // This test mimics pw_tokenizer//detokenize_test.cc in order to perform memory
126 // allocations as a result of manipulating a std::unordered_map.
127 // See also b/345526413 for the failure that motivated this test.
TEST_F(MallocTest,Detokenize)128 TEST_F(MallocTest, Detokenize) {
129 static constexpr char kTestDatabase[] =
130 "TOKENS\0\0"
131 "\x06\x00\x00\x00" // Number of tokens in this database.
132 "\0\0\0\0"
133 "\x01\x00\x00\x00----"
134 "\x05\x00\x00\x00----"
135 "\xFF\x00\x00\x00----"
136 "\xFF\xEE\xEE\xDD----"
137 "\xEE\xEE\xEE\xEE----"
138 "\x9D\xA7\x97\xF8----"
139 "One\0"
140 "TWO\0"
141 "333\0"
142 "FOUR\0"
143 "$AQAAAA==\0"
144 "■msg♦This is $AQAAAA== message■module♦■file♦file.txt";
145 pw::tokenizer::Detokenizer detok(
146 pw::tokenizer::TokenDatabase::Create<kTestDatabase>());
147 EXPECT_EQ(detok.Detokenize("\1\0\0\0"sv).BestString(), "One");
148 EXPECT_EQ(detok.Detokenize("\5\0\0\0"sv).BestString(), "TWO");
149 EXPECT_EQ(detok.Detokenize("\xff\x00\x00\x00"sv).BestString(), "333");
150 EXPECT_EQ(detok.Detokenize("\xff\xee\xee\xdd"sv).BestString(), "FOUR");
151 }
152
153 } // namespace
154