1b1994897Sopenharmony_ci/**
2b1994897Sopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3b1994897Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4b1994897Sopenharmony_ci * you may not use this file except in compliance with the License.
5b1994897Sopenharmony_ci * You may obtain a copy of the License at
6b1994897Sopenharmony_ci *
7b1994897Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8b1994897Sopenharmony_ci *
9b1994897Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10b1994897Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11b1994897Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12b1994897Sopenharmony_ci * See the License for the specific language governing permissions and
13b1994897Sopenharmony_ci * limitations under the License.
14b1994897Sopenharmony_ci */
15b1994897Sopenharmony_ci
16b1994897Sopenharmony_ci#include <cstring>
17b1994897Sopenharmony_ci#include <algorithm>
18b1994897Sopenharmony_ci#include <iterator>
19b1994897Sopenharmony_ci#include <sstream>
20b1994897Sopenharmony_ci#include <fstream>
21b1994897Sopenharmony_ci#include <functional>
22b1994897Sopenharmony_ci#include <unordered_map>
23b1994897Sopenharmony_ci#include "utils/logger.h"
24b1994897Sopenharmony_ci#include "os/stacktrace.h"
25b1994897Sopenharmony_ci#include "mem/alloc_tracker.h"
26b1994897Sopenharmony_ci
27b1994897Sopenharmony_cinamespace panda {
28b1994897Sopenharmony_ci
29b1994897Sopenharmony_cistatic constexpr size_t NUM_SKIP_FRAMES = 1;
30b1994897Sopenharmony_cistatic constexpr size_t ARENA_SIZE = 4096;
31b1994897Sopenharmony_cistatic constexpr size_t ENTRY_HDR_SIZE = sizeof(int32_t);
32b1994897Sopenharmony_ci
33b1994897Sopenharmony_cistatic const char *GetDumpFilePath()
34b1994897Sopenharmony_ci{
35b1994897Sopenharmony_ci#if defined(PANDA_TARGET_MOBILE)
36b1994897Sopenharmony_ci    return "/data/local/tmp/memdump.bin";
37b1994897Sopenharmony_ci#else
38b1994897Sopenharmony_ci    return "memdump.bin";
39b1994897Sopenharmony_ci#endif
40b1994897Sopenharmony_ci}
41b1994897Sopenharmony_ci
42b1994897Sopenharmony_cistatic void Write(uint32_t val, std::ostream &out)
43b1994897Sopenharmony_ci{
44b1994897Sopenharmony_ci    out.write(reinterpret_cast<char *>(&val), sizeof(val));
45b1994897Sopenharmony_ci}
46b1994897Sopenharmony_ci
47b1994897Sopenharmony_cistatic void Write(const std::string &str, std::ostream &out)
48b1994897Sopenharmony_ci{
49b1994897Sopenharmony_ci    Write(static_cast<uint32_t>(str.size()), out);
50b1994897Sopenharmony_ci    out.write(str.c_str(), str.length());
51b1994897Sopenharmony_ci}
52b1994897Sopenharmony_ci
53b1994897Sopenharmony_cistatic size_t CalcHash(const std::vector<uintptr_t> &st)
54b1994897Sopenharmony_ci{
55b1994897Sopenharmony_ci    size_t hash = 0;
56b1994897Sopenharmony_ci    std::hash<uintptr_t> addr_hash;
57b1994897Sopenharmony_ci    for (uintptr_t addr : st) {
58b1994897Sopenharmony_ci        hash |= addr_hash(addr);
59b1994897Sopenharmony_ci    }
60b1994897Sopenharmony_ci    return hash;
61b1994897Sopenharmony_ci}
62b1994897Sopenharmony_ci
63b1994897Sopenharmony_ci// On a phone getting a stacktrace is expensive operation.
64b1994897Sopenharmony_ci// An application doesn't launch in timeout and gets killed.
65b1994897Sopenharmony_ci// This function is aimed to skip getting stacktraces for some allocations.
66b1994897Sopenharmony_ci#if defined(PANDA_TARGET_MOBILE)
67b1994897Sopenharmony_cistatic bool SkipStacktrace(size_t num)
68b1994897Sopenharmony_ci{
69b1994897Sopenharmony_ci    static constexpr size_t FREQUENCY = 5;
70b1994897Sopenharmony_ci    return num % FREQUENCY != 0;
71b1994897Sopenharmony_ci}
72b1994897Sopenharmony_ci#else
73b1994897Sopenharmony_cistatic bool SkipStacktrace([[maybe_unused]] size_t num)
74b1994897Sopenharmony_ci{
75b1994897Sopenharmony_ci    return false;
76b1994897Sopenharmony_ci}
77b1994897Sopenharmony_ci#endif
78b1994897Sopenharmony_ci
79b1994897Sopenharmony_civoid DetailAllocTracker::TrackAlloc(void *addr, size_t size, SpaceType space)
80b1994897Sopenharmony_ci{
81b1994897Sopenharmony_ci    if (addr == nullptr) {
82b1994897Sopenharmony_ci        return;
83b1994897Sopenharmony_ci    }
84b1994897Sopenharmony_ci    Stacktrace stacktrace = SkipStacktrace(++alloc_counter_) ? Stacktrace() : GetStacktrace();
85b1994897Sopenharmony_ci    os::memory::LockHolder lock(mutex_);
86b1994897Sopenharmony_ci
87b1994897Sopenharmony_ci    uint32_t stacktrace_id = stacktraces_.size();
88b1994897Sopenharmony_ci    if (stacktrace.size() > NUM_SKIP_FRAMES) {
89b1994897Sopenharmony_ci        stacktraces_.emplace_back(stacktrace.begin() + NUM_SKIP_FRAMES, stacktrace.end());
90b1994897Sopenharmony_ci    } else {
91b1994897Sopenharmony_ci        stacktraces_.emplace_back(stacktrace);
92b1994897Sopenharmony_ci    }
93b1994897Sopenharmony_ci    if (cur_arena_.size() < sizeof(AllocInfo)) {
94b1994897Sopenharmony_ci        AllocArena();
95b1994897Sopenharmony_ci    }
96b1994897Sopenharmony_ci    auto info = new (cur_arena_.data()) AllocInfo(cur_id_++, size, static_cast<uint32_t>(space), stacktrace_id);
97b1994897Sopenharmony_ci    cur_arena_ = cur_arena_.SubSpan(sizeof(AllocInfo));
98b1994897Sopenharmony_ci    cur_allocs_.insert({addr, info});
99b1994897Sopenharmony_ci}
100b1994897Sopenharmony_ci
101b1994897Sopenharmony_civoid DetailAllocTracker::TrackFree(void *addr)
102b1994897Sopenharmony_ci{
103b1994897Sopenharmony_ci    if (addr == nullptr) {
104b1994897Sopenharmony_ci        return;
105b1994897Sopenharmony_ci    }
106b1994897Sopenharmony_ci    os::memory::LockHolder lock(mutex_);
107b1994897Sopenharmony_ci    auto it = cur_allocs_.find(addr);
108b1994897Sopenharmony_ci    ASSERT(it != cur_allocs_.end());
109b1994897Sopenharmony_ci    AllocInfo *alloc = it->second;
110b1994897Sopenharmony_ci    cur_allocs_.erase(it);
111b1994897Sopenharmony_ci    if (cur_arena_.size() < sizeof(FreeInfo)) {
112b1994897Sopenharmony_ci        AllocArena();
113b1994897Sopenharmony_ci    }
114b1994897Sopenharmony_ci    new (cur_arena_.data()) FreeInfo(alloc->GetId());
115b1994897Sopenharmony_ci    cur_arena_ = cur_arena_.SubSpan(sizeof(FreeInfo));
116b1994897Sopenharmony_ci}
117b1994897Sopenharmony_ci
118b1994897Sopenharmony_civoid DetailAllocTracker::AllocArena()
119b1994897Sopenharmony_ci{
120b1994897Sopenharmony_ci    if (cur_arena_.size() >= ENTRY_HDR_SIZE) {
121b1994897Sopenharmony_ci        *reinterpret_cast<uint32_t *>(cur_arena_.data()) = 0;
122b1994897Sopenharmony_ci    }
123b1994897Sopenharmony_ci    arenas_.emplace_back(new uint8_t[ARENA_SIZE]);
124b1994897Sopenharmony_ci    cur_arena_ = Span<uint8_t>(arenas_.back().get(), arenas_.back().get() + ARENA_SIZE);
125b1994897Sopenharmony_ci}
126b1994897Sopenharmony_ci
127b1994897Sopenharmony_civoid DetailAllocTracker::Dump()
128b1994897Sopenharmony_ci{
129b1994897Sopenharmony_ci    LOG(ERROR, RUNTIME) << "DetailAllocTracker::Dump to " << GetDumpFilePath();
130b1994897Sopenharmony_ci    std::ofstream out(GetDumpFilePath(), std::ios::out | std::ios::binary | std::ios::trunc);
131b1994897Sopenharmony_ci    if (!out) {
132b1994897Sopenharmony_ci        LOG(ERROR, RUNTIME) << "DetailAllocTracker: Cannot open " << GetDumpFilePath()
133b1994897Sopenharmony_ci                            << " for writing: " << strerror(errno) << "."
134b1994897Sopenharmony_ci                            << "\nCheck the directory has write permissions or"
135b1994897Sopenharmony_ci                            << " selinux is disabled.";
136b1994897Sopenharmony_ci    }
137b1994897Sopenharmony_ci    Dump(out);
138b1994897Sopenharmony_ci    LOG(ERROR, RUNTIME) << "DetailAllocTracker: dump file has been written";
139b1994897Sopenharmony_ci}
140b1994897Sopenharmony_ci
141b1994897Sopenharmony_civoid DetailAllocTracker::Dump(std::ostream &out)
142b1994897Sopenharmony_ci{
143b1994897Sopenharmony_ci    os::memory::LockHolder lock(mutex_);
144b1994897Sopenharmony_ci
145b1994897Sopenharmony_ci    Write(0, out);  // number of items, will be updated later
146b1994897Sopenharmony_ci    Write(0, out);  // number of stacktraces, will be updated later
147b1994897Sopenharmony_ci
148b1994897Sopenharmony_ci    std::map<uint32_t, uint32_t> id_map;
149b1994897Sopenharmony_ci    uint32_t num_stacks = WriteStacks(out, &id_map);
150b1994897Sopenharmony_ci
151b1994897Sopenharmony_ci    // Write end marker to the current arena
152b1994897Sopenharmony_ci    if (cur_arena_.size() >= ENTRY_HDR_SIZE) {
153b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
154b1994897Sopenharmony_ci        *reinterpret_cast<uint32_t *>(cur_arena_.data()) = 0;
155b1994897Sopenharmony_ci    }
156b1994897Sopenharmony_ci    uint32_t num_items = 0;
157b1994897Sopenharmony_ci    for (auto &arena : arenas_) {
158b1994897Sopenharmony_ci        uint8_t *ptr = arena.get();
159b1994897Sopenharmony_ci        size_t pos = 0;
160b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
161b1994897Sopenharmony_ci        while (pos < ARENA_SIZE && *reinterpret_cast<uint32_t *>(ptr + pos) != 0) {
162b1994897Sopenharmony_ci            // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
163b1994897Sopenharmony_ci            uint32_t tag = *reinterpret_cast<uint32_t *>(ptr + pos);
164b1994897Sopenharmony_ci            if (tag == ALLOC_TAG) {
165b1994897Sopenharmony_ci                // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
166b1994897Sopenharmony_ci                auto alloc = reinterpret_cast<AllocInfo *>(ptr + pos);
167b1994897Sopenharmony_ci                Write(alloc->GetTag(), out);
168b1994897Sopenharmony_ci                Write(alloc->GetId(), out);
169b1994897Sopenharmony_ci                Write(alloc->GetSize(), out);
170b1994897Sopenharmony_ci                Write(alloc->GetSpace(), out);
171b1994897Sopenharmony_ci                Write(id_map[alloc->GetStacktraceId()], out);
172b1994897Sopenharmony_ci                pos += sizeof(AllocInfo);
173b1994897Sopenharmony_ci            } else if (tag == FREE_TAG) {
174b1994897Sopenharmony_ci                // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
175b1994897Sopenharmony_ci                auto info = reinterpret_cast<FreeInfo *>(ptr + pos);
176b1994897Sopenharmony_ci                Write(info->GetTag(), out);
177b1994897Sopenharmony_ci                Write(info->GetAllocId(), out);
178b1994897Sopenharmony_ci                pos += sizeof(FreeInfo);
179b1994897Sopenharmony_ci            } else {
180b1994897Sopenharmony_ci                UNREACHABLE();
181b1994897Sopenharmony_ci            }
182b1994897Sopenharmony_ci            ++num_items;
183b1994897Sopenharmony_ci        }
184b1994897Sopenharmony_ci    }
185b1994897Sopenharmony_ci
186b1994897Sopenharmony_ci    out.seekp(0);
187b1994897Sopenharmony_ci    Write(num_items, out);
188b1994897Sopenharmony_ci    Write(num_stacks, out);
189b1994897Sopenharmony_ci}
190b1994897Sopenharmony_ci
191b1994897Sopenharmony_civoid DetailAllocTracker::DumpMemLeaks(std::ostream &out)
192b1994897Sopenharmony_ci{
193b1994897Sopenharmony_ci    static constexpr size_t MAX_ENTRIES_TO_REPORT = 10;
194b1994897Sopenharmony_ci
195b1994897Sopenharmony_ci    os::memory::LockHolder lock(mutex_);
196b1994897Sopenharmony_ci    size_t num = 0;
197b1994897Sopenharmony_ci    out << "found " << cur_allocs_.size() << " leaks\n";
198b1994897Sopenharmony_ci    for (auto &entry : cur_allocs_) {
199b1994897Sopenharmony_ci        out << "Allocation of " << entry.second->GetSize() << " is allocated at\n";
200b1994897Sopenharmony_ci        uint32_t stacktrace_id = entry.second->GetStacktraceId();
201b1994897Sopenharmony_ci        auto it = stacktraces_.begin();
202b1994897Sopenharmony_ci        std::advance(it, stacktrace_id);
203b1994897Sopenharmony_ci        PrintStack(*it, out);
204b1994897Sopenharmony_ci        if (++num > MAX_ENTRIES_TO_REPORT) {
205b1994897Sopenharmony_ci            break;
206b1994897Sopenharmony_ci        }
207b1994897Sopenharmony_ci    }
208b1994897Sopenharmony_ci}
209b1994897Sopenharmony_ci
210b1994897Sopenharmony_ciuint32_t DetailAllocTracker::WriteStacks(std::ostream &out, std::map<uint32_t, uint32_t> *id_map)
211b1994897Sopenharmony_ci{
212b1994897Sopenharmony_ci    class Key {
213b1994897Sopenharmony_ci    public:
214b1994897Sopenharmony_ci        explicit Key(const Stacktrace *stacktrace) : stacktrace_(stacktrace), hash_(CalcHash(*stacktrace)) {}
215b1994897Sopenharmony_ci
216b1994897Sopenharmony_ci        DEFAULT_COPY_SEMANTIC(Key);
217b1994897Sopenharmony_ci        DEFAULT_NOEXCEPT_MOVE_SEMANTIC(Key);
218b1994897Sopenharmony_ci
219b1994897Sopenharmony_ci        ~Key() = default;
220b1994897Sopenharmony_ci
221b1994897Sopenharmony_ci        bool operator==(const Key &k) const
222b1994897Sopenharmony_ci        {
223b1994897Sopenharmony_ci            return *stacktrace_ == *k.stacktrace_;
224b1994897Sopenharmony_ci        }
225b1994897Sopenharmony_ci
226b1994897Sopenharmony_ci        size_t GetHash() const
227b1994897Sopenharmony_ci        {
228b1994897Sopenharmony_ci            return hash_;
229b1994897Sopenharmony_ci        }
230b1994897Sopenharmony_ci
231b1994897Sopenharmony_ci    private:
232b1994897Sopenharmony_ci        const Stacktrace *stacktrace_ = nullptr;
233b1994897Sopenharmony_ci        size_t hash_ = 0U;
234b1994897Sopenharmony_ci    };
235b1994897Sopenharmony_ci
236b1994897Sopenharmony_ci    struct KeyHash {
237b1994897Sopenharmony_ci        size_t operator()(const Key &k) const
238b1994897Sopenharmony_ci        {
239b1994897Sopenharmony_ci            return k.GetHash();
240b1994897Sopenharmony_ci        }
241b1994897Sopenharmony_ci    };
242b1994897Sopenharmony_ci
243b1994897Sopenharmony_ci    std::unordered_map<Key, uint32_t, KeyHash> alloc_stacks;
244b1994897Sopenharmony_ci    uint32_t stacktrace_id = 0;
245b1994897Sopenharmony_ci    uint32_t deduplicated_id = 0;
246b1994897Sopenharmony_ci    for (Stacktrace &stacktrace : stacktraces_) {
247b1994897Sopenharmony_ci        Key akey(&stacktrace);
248b1994897Sopenharmony_ci        auto res = alloc_stacks.insert({akey, deduplicated_id});
249b1994897Sopenharmony_ci        if (res.second) {
250b1994897Sopenharmony_ci            std::stringstream str;
251b1994897Sopenharmony_ci            PrintStack(stacktrace, str);
252b1994897Sopenharmony_ci            Write(str.str(), out);
253b1994897Sopenharmony_ci            id_map->insert({stacktrace_id, deduplicated_id});
254b1994897Sopenharmony_ci            ++deduplicated_id;
255b1994897Sopenharmony_ci        } else {
256b1994897Sopenharmony_ci            uint32_t id = res.first->second;
257b1994897Sopenharmony_ci            id_map->insert({stacktrace_id, id});
258b1994897Sopenharmony_ci        }
259b1994897Sopenharmony_ci        ++stacktrace_id;
260b1994897Sopenharmony_ci    }
261b1994897Sopenharmony_ci    return deduplicated_id;
262b1994897Sopenharmony_ci}
263b1994897Sopenharmony_ci
264b1994897Sopenharmony_ci}  // namespace panda
265