Home | History | Annotate | Download | only in memory
      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