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