summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/memprof
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/memprof')
-rw-r--r--protocols/Telegram/tdlib/td/memprof/memprof.cpp61
-rw-r--r--protocols/Telegram/tdlib/td/memprof/memprof.h2
-rw-r--r--protocols/Telegram/tdlib/td/memprof/memprof_stat.cpp166
-rw-r--r--protocols/Telegram/tdlib/td/memprof/memprof_stat.h13
4 files changed, 216 insertions, 26 deletions
diff --git a/protocols/Telegram/tdlib/td/memprof/memprof.cpp b/protocols/Telegram/tdlib/td/memprof/memprof.cpp
index 11822ee00a..c1ef1d3d1b 100644
--- a/protocols/Telegram/tdlib/td/memprof/memprof.cpp
+++ b/protocols/Telegram/tdlib/td/memprof/memprof.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,7 +11,6 @@
#if (TD_DARWIN || TD_LINUX) && defined(USE_MEMPROF)
#include <algorithm>
#include <atomic>
-#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
@@ -28,6 +27,11 @@ bool is_memprof_on() {
return true;
}
+#define my_assert(f) \
+ if (!(f)) { \
+ std::abort(); \
+ }
+
#if USE_MEMPROF_SAFE
double get_fast_backtrace_success_rate() {
return 0;
@@ -54,7 +58,7 @@ static int fast_backtrace(void **buffer, int size) {
void *ip;
};
- stack_frame *bp = reinterpret_cast<stack_frame *>(get_bp());
+ auto *bp = reinterpret_cast<stack_frame *>(get_bp());
int i = 0;
while (i < size &&
#if TD_LINUX
@@ -72,7 +76,8 @@ static int fast_backtrace(void **buffer, int size) {
return i;
}
-static std::atomic<std::size_t> fast_backtrace_failed_cnt, backtrace_total_cnt;
+static std::atomic<std::size_t> fast_backtrace_failed_cnt;
+static std::atomic<std::size_t> backtrace_total_cnt;
double get_fast_backtrace_success_rate() {
return 1 - static_cast<double>(fast_backtrace_failed_cnt.load(std::memory_order_relaxed)) /
static_cast<double>(std::max(std::size_t(1), backtrace_total_cnt.load(std::memory_order_relaxed)));
@@ -116,8 +121,8 @@ static Backtrace get_backtrace() {
return res;
}
-static constexpr std::size_t reserved = 16;
-static constexpr std::int32_t malloc_info_magic = 0x27138373;
+static constexpr std::size_t RESERVED_SIZE = 16;
+static constexpr std::int32_t MALLOC_INFO_MAGIC = 0x27138373;
struct malloc_info {
std::int32_t magic;
std::int32_t size;
@@ -138,9 +143,9 @@ struct HashtableNode {
std::atomic<std::size_t> size;
};
-static constexpr std::size_t ht_max_size = 1000000;
+static constexpr std::size_t HT_MAX_SIZE = 1000000;
static std::atomic<std::size_t> ht_size{0};
-static std::array<HashtableNode, ht_max_size> ht;
+static std::array<HashtableNode, HT_MAX_SIZE> ht;
std::size_t get_ht_size() {
return ht_size.load();
@@ -148,14 +153,14 @@ std::size_t get_ht_size() {
std::int32_t get_ht_pos(const Backtrace &bt, bool force = false) {
auto hash = get_hash(bt);
- std::int32_t pos = static_cast<std::int32_t>(hash % ht.size());
+ auto pos = static_cast<std::int32_t>(hash % ht.size());
bool was_overflow = false;
while (true) {
auto pos_hash = ht[pos].hash.load();
if (pos_hash == 0) {
- if (ht_size > ht_max_size / 2) {
+ if (ht_size > HT_MAX_SIZE / 2) {
if (force) {
- assert(ht_size * 10 < ht_max_size * 7);
+ my_assert(ht_size * 10 < HT_MAX_SIZE * 7);
} else {
Backtrace unknown_bt{{nullptr}};
unknown_bt[0] = reinterpret_cast<void *>(1);
@@ -187,26 +192,29 @@ std::int32_t get_ht_pos(const Backtrace &bt, bool force = false) {
void dump_alloc(const std::function<void(const AllocInfo &)> &func) {
for (auto &node : ht) {
- if (node.size == 0) {
+ auto size = node.size.load(std::memory_order_relaxed);
+ if (size == 0) {
continue;
}
- func(AllocInfo{node.backtrace, node.size.load()});
+ func(AllocInfo{node.backtrace, size});
}
}
void register_xalloc(malloc_info *info, std::int32_t diff) {
+ my_assert(info->size >= 0);
if (diff > 0) {
- ht[info->ht_pos].size += info->size;
+ ht[info->ht_pos].size.fetch_add(info->size, std::memory_order_relaxed);
} else {
- ht[info->ht_pos].size -= info->size;
+ auto old_value = ht[info->ht_pos].size.fetch_sub(info->size, std::memory_order_relaxed);
+ my_assert(old_value >= static_cast<std::size_t>(info->size));
}
}
extern "C" {
static void *malloc_with_frame(std::size_t size, const Backtrace &frame) {
- static_assert(reserved % alignof(std::max_align_t) == 0, "fail");
- static_assert(reserved >= sizeof(malloc_info), "fail");
+ static_assert(RESERVED_SIZE % alignof(std::max_align_t) == 0, "fail");
+ static_assert(RESERVED_SIZE >= sizeof(malloc_info), "fail");
#if TD_DARWIN
static void *malloc_void = dlsym(RTLD_NEXT, "malloc");
static auto malloc_old = *reinterpret_cast<decltype(malloc) **>(&malloc_void);
@@ -214,26 +222,26 @@ static void *malloc_with_frame(std::size_t size, const Backtrace &frame) {
extern decltype(malloc) __libc_malloc;
static auto malloc_old = __libc_malloc;
#endif
- auto *info = static_cast<malloc_info *>(malloc_old(size + reserved));
+ auto *info = static_cast<malloc_info *>(malloc_old(size + RESERVED_SIZE));
auto *buf = reinterpret_cast<char *>(info);
- info->magic = malloc_info_magic;
+ info->magic = MALLOC_INFO_MAGIC;
info->size = static_cast<std::int32_t>(size);
info->ht_pos = get_ht_pos(frame);
register_xalloc(info, +1);
- void *data = buf + reserved;
+ void *data = buf + RESERVED_SIZE;
return data;
}
static malloc_info *get_info(void *data_void) {
- char *data = static_cast<char *>(data_void);
- auto *buf = data - reserved;
+ auto *data = static_cast<char *>(data_void);
+ auto *buf = data - RESERVED_SIZE;
auto *info = reinterpret_cast<malloc_info *>(buf);
- assert(info->magic == malloc_info_magic);
+ my_assert(info->magic == MALLOC_INFO_MAGIC);
return info;
}
@@ -257,12 +265,14 @@ void free(void *data_void) {
#endif
return free_old(info);
}
+
void *calloc(std::size_t size_a, std::size_t size_b) {
auto size = size_a * size_b;
void *res = malloc_with_frame(size, get_backtrace());
std::memset(res, 0, size);
return res;
}
+
void *realloc(void *ptr, std::size_t size) {
if (ptr == nullptr) {
return malloc_with_frame(size, get_backtrace());
@@ -274,13 +284,14 @@ void *realloc(void *ptr, std::size_t size) {
free(ptr);
return new_ptr;
}
+
void *memalign(std::size_t aligment, std::size_t size) {
- assert(false && "Memalign is unsupported");
+ my_assert(false && "Memalign is unsupported");
return nullptr;
}
}
-// c++14 guarantees than it is enough to override this two operators.
+// c++14 guarantees that it is enough to override these two operators.
void *operator new(std::size_t count) {
return malloc_with_frame(count, get_backtrace());
}
diff --git a/protocols/Telegram/tdlib/td/memprof/memprof.h b/protocols/Telegram/tdlib/td/memprof/memprof.h
index 00e33f7380..be3823f82e 100644
--- a/protocols/Telegram/tdlib/td/memprof/memprof.h
+++ b/protocols/Telegram/tdlib/td/memprof/memprof.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/memprof/memprof_stat.cpp b/protocols/Telegram/tdlib/td/memprof/memprof_stat.cpp
new file mode 100644
index 0000000000..00d371431e
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/memprof/memprof_stat.cpp
@@ -0,0 +1,166 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "memprof/memprof_stat.h"
+
+#include "td/utils/port/platform.h"
+
+#if (TD_DARWIN || TD_LINUX)
+#include <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <new>
+#include <utility>
+#include <vector>
+
+#include <dlfcn.h>
+#include <execinfo.h>
+
+bool is_memprof_on() {
+ return true;
+}
+
+#define my_assert(f) \
+ if (!(f)) { \
+ std::abort(); \
+ }
+
+struct malloc_info {
+ std::int32_t magic;
+ std::int32_t size;
+};
+
+static std::atomic<std::size_t> total_memory_used;
+
+void register_xalloc(malloc_info *info, std::int32_t diff) {
+ my_assert(info->size >= 0);
+ // TODO: this is very slow in case of several threads.
+ // Currently this statistics is intended only for memory benchmarks.
+ total_memory_used.fetch_add(diff * info->size, std::memory_order_relaxed);
+}
+
+std::size_t get_used_memory_size() {
+ return total_memory_used.load();
+}
+
+extern "C" {
+
+static constexpr std::size_t RESERVED_SIZE = 16;
+static constexpr std::int32_t MALLOC_INFO_MAGIC = 0x27138373;
+
+static void *do_malloc(std::size_t size) {
+ static_assert(RESERVED_SIZE % alignof(std::max_align_t) == 0, "fail");
+ static_assert(RESERVED_SIZE >= sizeof(malloc_info), "fail");
+#if TD_DARWIN
+ static void *malloc_void = dlsym(RTLD_NEXT, "malloc");
+ static auto malloc_old = *reinterpret_cast<decltype(malloc) **>(&malloc_void);
+#else
+ extern decltype(malloc) __libc_malloc;
+ static auto malloc_old = __libc_malloc;
+#endif
+ auto *info = static_cast<malloc_info *>(malloc_old(size + RESERVED_SIZE));
+ auto *buf = reinterpret_cast<char *>(info);
+
+ info->magic = MALLOC_INFO_MAGIC;
+ info->size = static_cast<std::int32_t>(size);
+
+ register_xalloc(info, +1);
+
+ void *data = buf + RESERVED_SIZE;
+
+ return data;
+}
+
+static malloc_info *get_info(void *data_void) {
+ auto *data = static_cast<char *>(data_void);
+ auto *buf = data - RESERVED_SIZE;
+
+ auto *info = reinterpret_cast<malloc_info *>(buf);
+ my_assert(info->magic == MALLOC_INFO_MAGIC);
+ return info;
+}
+
+void *malloc(std::size_t size) {
+ return do_malloc(size);
+}
+
+void free(void *data_void) {
+ if (data_void == nullptr) {
+ return;
+ }
+ auto *info = get_info(data_void);
+ register_xalloc(info, -1);
+
+#if TD_DARWIN
+ static void *free_void = dlsym(RTLD_NEXT, "free");
+ static auto free_old = *reinterpret_cast<decltype(free) **>(&free_void);
+#else
+ extern decltype(free) __libc_free;
+ static auto free_old = __libc_free;
+#endif
+ return free_old(info);
+}
+
+void *calloc(std::size_t size_a, std::size_t size_b) {
+ auto size = size_a * size_b;
+ void *res = do_malloc(size);
+ std::memset(res, 0, size);
+ return res;
+}
+
+void *realloc(void *ptr, std::size_t size) {
+ if (ptr == nullptr) {
+ return do_malloc(size);
+ }
+ auto *info = get_info(ptr);
+ auto *new_ptr = do_malloc(size);
+ auto to_copy = std::min(static_cast<std::int32_t>(size), info->size);
+ std::memcpy(new_ptr, ptr, to_copy);
+ free(ptr);
+ return new_ptr;
+}
+
+void *memalign(std::size_t alignment, std::size_t size) {
+ auto res = malloc(size);
+ my_assert(reinterpret_cast<std::uintptr_t>(res) % alignment == 0);
+ return res;
+}
+
+int posix_memalign(void **memptr, size_t alignment, size_t size) {
+ auto res = malloc(size);
+ my_assert(reinterpret_cast<std::uintptr_t>(res) % alignment == 0);
+ *memptr = res;
+ return 0;
+}
+}
+
+// c++14 guarantees that it is enough to override these two operators.
+void *operator new(std::size_t count) {
+ return do_malloc(count);
+}
+void operator delete(void *ptr) noexcept(true) {
+ free(ptr);
+}
+// because of gcc warning: the program should also define 'void operator delete(void*, std::size_t)'
+void operator delete(void *ptr, std::size_t) noexcept(true) {
+ free(ptr);
+}
+
+// c++17
+// void *operator new(std::size_t count, std::align_val_t al);
+// void operator delete(void *ptr, std::align_val_t al);
+
+#else
+bool is_memprof_on() {
+ return false;
+}
+std::size_t get_used_memory_size() {
+ return 0;
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/memprof/memprof_stat.h b/protocols/Telegram/tdlib/td/memprof/memprof_stat.h
new file mode 100644
index 0000000000..5bc045e25c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/memprof/memprof_stat.h
@@ -0,0 +1,13 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include <cstddef>
+
+bool is_memprof_on();
+
+std::size_t get_used_memory_size();