1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "src/profiling/memory/bookkeeping.h" 18 19 #include "gmock/gmock.h" 20 #include "gtest/gtest.h" 21 22 namespace perfetto { 23 namespace profiling { 24 namespace { 25 26 using ::testing::AnyOf; 27 using ::testing::Eq; 28 29 std::vector<FrameData> stack() { 30 std::vector<FrameData> res; 31 32 unwindstack::FrameData data{}; 33 data.function_name = "fun1"; 34 data.map_name = "map1"; 35 res.emplace_back(std::move(data), "dummy_buildid"); 36 data = {}; 37 data.function_name = "fun2"; 38 data.map_name = "map2"; 39 res.emplace_back(std::move(data), "dummy_buildid"); 40 return res; 41 } 42 43 std::vector<FrameData> stack2() { 44 std::vector<FrameData> res; 45 unwindstack::FrameData data{}; 46 data.function_name = "fun1"; 47 data.map_name = "map1"; 48 res.emplace_back(std::move(data), "dummy_buildid"); 49 data = {}; 50 data.function_name = "fun3"; 51 data.map_name = "map3"; 52 res.emplace_back(std::move(data), "dummy_buildid"); 53 return res; 54 } 55 56 TEST(BookkeepingTest, Basic) { 57 uint64_t sequence_number = 1; 58 GlobalCallstackTrie c; 59 HeapTracker hd(&c); 60 61 hd.RecordMalloc(stack(), 1, 5, sequence_number, 100 * sequence_number); 62 sequence_number++; 63 hd.RecordMalloc(stack2(), 2, 2, sequence_number, 100 * sequence_number); 64 sequence_number++; 65 ASSERT_EQ(hd.GetSizeForTesting(stack()), 5); 66 ASSERT_EQ(hd.GetSizeForTesting(stack2()), 2); 67 ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1)); 68 hd.RecordFree(2, sequence_number, 100 * sequence_number); 69 sequence_number++; 70 ASSERT_EQ(hd.GetSizeForTesting(stack()), 5); 71 ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0); 72 ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1)); 73 hd.RecordFree(1, sequence_number, 100 * sequence_number); 74 sequence_number++; 75 ASSERT_EQ(hd.GetSizeForTesting(stack()), 0); 76 ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0); 77 ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1)); 78 } 79 80 TEST(BookkeepingTest, TwoHeapTrackers) { 81 uint64_t sequence_number = 1; 82 GlobalCallstackTrie c; 83 HeapTracker hd(&c); 84 { 85 HeapTracker hd2(&c); 86 87 hd.RecordMalloc(stack(), 1, 5, sequence_number, 100 * sequence_number); 88 hd2.RecordMalloc(stack(), 2, 2, sequence_number, 100 * sequence_number); 89 sequence_number++; 90 ASSERT_EQ(hd2.GetSizeForTesting(stack()), 2); 91 ASSERT_EQ(hd.GetSizeForTesting(stack()), 5); 92 ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1)); 93 } 94 ASSERT_EQ(hd.GetSizeForTesting(stack()), 5); 95 } 96 97 TEST(BookkeepingTest, ReplaceAlloc) { 98 uint64_t sequence_number = 1; 99 GlobalCallstackTrie c; 100 HeapTracker hd(&c); 101 102 hd.RecordMalloc(stack(), 1, 5, sequence_number, 100 * sequence_number); 103 sequence_number++; 104 hd.RecordMalloc(stack2(), 1, 2, sequence_number, 100 * sequence_number); 105 sequence_number++; 106 EXPECT_EQ(hd.GetSizeForTesting(stack()), 0); 107 EXPECT_EQ(hd.GetSizeForTesting(stack2()), 2); 108 ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1)); 109 } 110 111 TEST(BookkeepingTest, OutOfOrder) { 112 GlobalCallstackTrie c; 113 HeapTracker hd(&c); 114 115 hd.RecordMalloc(stack(), 1, 5, 2, 2); 116 hd.RecordMalloc(stack2(), 1, 2, 1, 1); 117 EXPECT_EQ(hd.GetSizeForTesting(stack()), 5); 118 EXPECT_EQ(hd.GetSizeForTesting(stack2()), 0); 119 } 120 121 TEST(BookkeepingTest, ManyAllocations) { 122 GlobalCallstackTrie c; 123 HeapTracker hd(&c); 124 125 std::vector<std::pair<uint64_t, uint64_t>> batch_frees; 126 127 for (uint64_t sequence_number = 1; sequence_number < 1000;) { 128 if (batch_frees.size() > 10) { 129 for (const auto& p : batch_frees) 130 hd.RecordFree(p.first, p.second, 100 * p.second); 131 batch_frees.clear(); 132 } 133 134 uint64_t addr = sequence_number; 135 hd.RecordMalloc(stack(), addr, 5, sequence_number, sequence_number); 136 sequence_number++; 137 batch_frees.emplace_back(addr, sequence_number++); 138 ASSERT_THAT(hd.GetSizeForTesting(stack()), AnyOf(Eq(0), Eq(5))); 139 } 140 } 141 142 TEST(BookkeepingTest, ArbitraryOrder) { 143 std::vector<FrameData> s = stack(); 144 std::vector<FrameData> s2 = stack2(); 145 146 struct Operation { 147 uint64_t sequence_number; 148 uint64_t address; 149 uint64_t bytes; // 0 for free 150 const std::vector<FrameData>* stack; // nullptr for free 151 152 // For std::next_permutation. 153 bool operator<(const Operation& other) const { 154 return sequence_number < other.sequence_number; 155 } 156 } operations[] = { 157 {1, 1, 5, &s}, // 158 {2, 1, 10, &s2}, // 159 {3, 1, 0, nullptr}, // 160 {4, 2, 0, nullptr}, // 161 {5, 3, 0, nullptr}, // 162 {6, 3, 2, &s}, // 163 {7, 4, 3, &s2}, // 164 }; 165 166 uint64_t s_size = 2; 167 uint64_t s2_size = 3; 168 169 do { 170 GlobalCallstackTrie c; 171 HeapTracker hd(&c); 172 173 for (auto it = std::begin(operations); it != std::end(operations); ++it) { 174 const Operation& operation = *it; 175 176 if (operation.bytes == 0) { 177 hd.RecordFree(operation.address, operation.sequence_number, 178 100 * operation.sequence_number); 179 } else { 180 hd.RecordMalloc(*operation.stack, operation.address, operation.bytes, 181 operation.sequence_number, 182 100 * operation.sequence_number); 183 } 184 } 185 ASSERT_EQ(hd.GetSizeForTesting(s), s_size); 186 ASSERT_EQ(hd.GetSizeForTesting(s2), s2_size); 187 } while (std::next_permutation(std::begin(operations), std::end(operations))); 188 } 189 190 } // namespace 191 } // namespace profiling 192 } // namespace perfetto 193