1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://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,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef LIBPANDABASE_MEM_POOL_MAP_H
17#define LIBPANDABASE_MEM_POOL_MAP_H
18
19#include <array>
20#include <cstddef>
21#include <cstdint>
22
23#include "macros.h"
24#include "mem.h"
25#include "space.h"
26
27WEAK_FOR_LTO_START
28
29namespace panda {
30
31enum class AllocatorType {
32    UNDEFINED,
33    RUNSLOTS_ALLOCATOR,
34    FREELIST_ALLOCATOR,
35    HUMONGOUS_ALLOCATOR,
36    ARENA_ALLOCATOR,
37    TLAB_ALLOCATOR,
38    BUMP_ALLOCATOR,
39    REGION_ALLOCATOR,
40    FRAME_ALLOCATOR,
41    BUMP_ALLOCATOR_WITH_TLABS,
42};
43
44class AllocatorInfo {
45public:
46    explicit constexpr AllocatorInfo(AllocatorType type, const void *addr) : type_(type), header_addr_(addr)
47    {
48        // We can't create AllocatorInfo without correct pointer to the allocator header
49        ASSERT(header_addr_ != nullptr);
50    }
51
52    AllocatorType GetType() const
53    {
54        return type_;
55    }
56
57    const void *GetAllocatorHeaderAddr() const
58    {
59        return header_addr_;
60    }
61
62    virtual ~AllocatorInfo() = default;
63
64    DEFAULT_COPY_SEMANTIC(AllocatorInfo);
65    DEFAULT_MOVE_SEMANTIC(AllocatorInfo);
66
67private:
68    AllocatorType type_;
69    const void *header_addr_;
70};
71
72// PoolMap is used to manage all pools which has been given to the allocators.
73// It can be used to find which allocator has been used to allocate an object.
74class PoolMap {
75public:
76    void AddPoolToMap(const void *pool_addr, size_t pool_size, SpaceType space_type, AllocatorType allocator_type,
77                      const void *allocator_addr);
78    void RemovePoolFromMap(const void *pool_addr, size_t pool_size);
79    // Get Allocator info for the object allocated at this address.
80    AllocatorInfo GetAllocatorInfo(const void *addr) const;
81
82    void *GetFirstByteOfPoolForAddr(const void *addr) const;
83
84    SpaceType GetSpaceType(const void *addr) const;
85
86    bool IsEmpty() const;
87
88private:
89    static constexpr uint64_t POOL_MAP_COVERAGE = PANDA_MAX_HEAP_SIZE;
90    static constexpr size_t POOL_MAP_GRANULARITY = PANDA_POOL_ALIGNMENT_IN_BYTES;
91    static constexpr size_t POOL_MAP_SIZE = POOL_MAP_COVERAGE / POOL_MAP_GRANULARITY;
92
93    static constexpr bool FIRST_BYTE_IN_SEGMENT_VALUE = true;
94
95    class PoolInfo {
96    public:
97        void Initialize(bool first_byte_in_segment, SpaceType space_type, AllocatorType allocator_type,
98                        const void *allocator_addr)
99        {
100            ASSERT(first_byte_in_segment_ == FIRST_BYTE_IN_SEGMENT_VALUE);
101            ASSERT(allocator_type_ == AllocatorType::UNDEFINED);
102            // Added a TSAN ignore here because TSAN thinks
103            // that we can have a data race here - concurrent
104            // initialization and reading.
105            // However, we can't get an access for this fields
106            // without initialization in the correct flow.
107            TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
108            first_byte_in_segment_ = first_byte_in_segment;
109            allocator_addr_ = allocator_addr;
110            space_type_ = space_type;
111            allocator_type_ = allocator_type;
112            TSAN_ANNOTATE_IGNORE_WRITES_END();
113        }
114
115        inline bool IsEmpty() const
116        {
117            return space_type_ == SpaceType::SPACE_TYPE_UNDEFINED;
118        }
119
120        void Destroy()
121        {
122            first_byte_in_segment_ = FIRST_BYTE_IN_SEGMENT_VALUE;
123            allocator_addr_ = nullptr;
124            allocator_type_ = AllocatorType::UNDEFINED;
125            space_type_ = SpaceType::SPACE_TYPE_UNDEFINED;
126        }
127
128        bool IsFirstByteInSegment() const
129        {
130            return first_byte_in_segment_ == FIRST_BYTE_IN_SEGMENT_VALUE;
131        }
132
133        AllocatorType GetAllocatorType() const
134        {
135            return allocator_type_;
136        }
137
138        const void *GetAllocatorAddr() const
139        {
140            return allocator_addr_;
141        }
142
143        SpaceType GetSpaceType() const
144        {
145            return space_type_;
146        }
147
148    private:
149        bool first_byte_in_segment_ {FIRST_BYTE_IN_SEGMENT_VALUE};
150        AllocatorType allocator_type_ {AllocatorType::UNDEFINED};
151        SpaceType space_type_ {SpaceType::SPACE_TYPE_UNDEFINED};
152        const void *allocator_addr_ = nullptr;
153    };
154
155    static size_t AddrToMapNum(const void *addr)
156    {
157        size_t map_num = ToUintPtr(addr) / POOL_MAP_GRANULARITY;
158        ASSERT(map_num < POOL_MAP_SIZE);
159        return map_num;
160    }
161
162    static void *MapNumToAddr(size_t map_num)
163    {
164        // Checking overflow
165        ASSERT(static_cast<uint64_t>(map_num) * POOL_MAP_GRANULARITY == map_num * POOL_MAP_GRANULARITY);
166        return ToVoidPtr(map_num * POOL_MAP_GRANULARITY);
167    }
168
169    void *GetFirstByteInSegment(const void *addr) const;
170
171    std::array<PoolInfo, POOL_MAP_SIZE> pool_map_;
172
173    friend class PoolMapTest;
174};
175
176}  // namespace panda
177
178WEAK_FOR_LTO_END
179
180#endif  // LIBPANDABASE_MEM_POOL_MAP_H
181