Add simple allocation tracer on Linux.
This commit is contained in:
parent
538b39ec9d
commit
42d3840cde
7 changed files with 462 additions and 0 deletions
|
|
@ -13,3 +13,6 @@ if (LINUX
|
|||
AND Qt5WaylandClient_FOUND)
|
||||
add_subdirectory(linux_wayland_helper)
|
||||
endif()
|
||||
if (DESKTOP_APP_USE_ALLOCATION_TRACER)
|
||||
add_subdirectory(linux_allocation_tracer)
|
||||
endif()
|
||||
|
|
|
|||
24
external/qt/CMakeLists.txt
vendored
24
external/qt/CMakeLists.txt
vendored
|
|
@ -325,6 +325,23 @@ else()
|
|||
-pthread
|
||||
-rdynamic
|
||||
)
|
||||
if (DESKTOP_APP_USE_ALLOCATION_TRACER)
|
||||
target_link_options(external_qt
|
||||
INTERFACE
|
||||
# -Wl,-wrap,__malloc
|
||||
-Wl,-wrap,__libc_malloc
|
||||
-Wl,-wrap,malloc
|
||||
-Wl,-wrap,valloc
|
||||
-Wl,-wrap,pvalloc
|
||||
-Wl,-wrap,calloc
|
||||
-Wl,-wrap,realloc
|
||||
-Wl,-wrap,memalign
|
||||
-Wl,-wrap,aligned_alloc
|
||||
-Wl,-wrap,posix_memalign
|
||||
-Wl,-wrap,free
|
||||
-Wl,--no-as-needed,-lrt
|
||||
)
|
||||
endif()
|
||||
if (DESKTOP_APP_USE_GLIBC_WRAPS)
|
||||
target_link_options(external_qt
|
||||
INTERFACE
|
||||
|
|
@ -366,6 +383,13 @@ else()
|
|||
xcb-render-util
|
||||
xcb-keysyms
|
||||
)
|
||||
if (DESKTOP_APP_USE_ALLOCATION_TRACER)
|
||||
target_link_libraries(external_qt
|
||||
INTERFACE
|
||||
desktop-app::linux_allocation_tracer
|
||||
$<TARGET_FILE:desktop-app::linux_allocation_tracer>
|
||||
)
|
||||
endif()
|
||||
if (DESKTOP_APP_USE_GLIBC_WRAPS)
|
||||
target_link_libraries(external_qt
|
||||
INTERFACE
|
||||
|
|
|
|||
26
linux_allocation_tracer/CMakeLists.txt
Normal file
26
linux_allocation_tracer/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# This file is part of Desktop App Toolkit,
|
||||
# a set of libraries for developing nice desktop applications.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
|
||||
add_library(linux_allocation_tracer STATIC)
|
||||
add_library(desktop-app::linux_allocation_tracer ALIAS linux_allocation_tracer)
|
||||
|
||||
nice_target_sources(linux_allocation_tracer ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE
|
||||
linux_allocation_tracer.cpp
|
||||
linux_allocation_tracer.h
|
||||
)
|
||||
|
||||
add_executable(allocation_trace_reader WIN32)
|
||||
init_target(allocation_trace_reader)
|
||||
|
||||
target_sources(allocation_trace_reader
|
||||
PRIVATE
|
||||
linux_allocation_trace_reader.cpp
|
||||
)
|
||||
|
||||
target_link_options(allocation_trace_reader PRIVATE -static-libstdc++)
|
||||
|
||||
set_target_properties(allocation_trace_reader PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
216
linux_allocation_tracer/linux_allocation_trace_reader.cpp
Normal file
216
linux_allocation_tracer/linux_allocation_trace_reader.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
#include <unordered_map>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
|
||||
constexpr auto kBufferSize = 1024 * 1024;
|
||||
char Buffer[kBufferSize];
|
||||
|
||||
struct Fields {
|
||||
std::uint32_t time = 0;
|
||||
std::size_t mallocs = 0;
|
||||
std::size_t reallocs = 0;
|
||||
std::size_t frees = 0;
|
||||
std::size_t unknownReallocs = 0;
|
||||
std::size_t unknownFrees = 0;
|
||||
};
|
||||
Fields State;
|
||||
std::unordered_map<std::uint64_t, std::size_t> Map;
|
||||
std::vector<Fields> Snapshots;
|
||||
|
||||
void WriteTime(std::uint32_t time) {
|
||||
std::time_t t = std::time_t(time);
|
||||
const auto parsed = std::localtime(&t);
|
||||
std::cout
|
||||
<< std::setw(2) << std::setfill('0') << parsed->tm_hour
|
||||
<< ":"
|
||||
<< std::setw(2) << std::setfill('0') << parsed->tm_min
|
||||
<< ":"
|
||||
<< std::setw(2) << std::setfill('0') << parsed->tm_sec;
|
||||
}
|
||||
|
||||
void PrintState() {
|
||||
if (!State.time) {
|
||||
return;
|
||||
}
|
||||
WriteTime(State.time);
|
||||
auto full = std::uint64_t(0);
|
||||
for (const auto &[address, amount] : Map) {
|
||||
full += amount;
|
||||
}
|
||||
|
||||
class NumPunct final : public std::numpunct<char> {
|
||||
protected:
|
||||
char do_thousands_sep() const override { return '\''; }
|
||||
std::string do_grouping() const override { return "\03"; }
|
||||
};
|
||||
|
||||
const auto &locale = std::cout.getloc();
|
||||
std::cout.imbue(std::locale(std::locale::classic(), new NumPunct()));
|
||||
std::cout
|
||||
<< ": "
|
||||
<< std::setw(13) << std::setfill(' ') << full
|
||||
<< " (unknown: "
|
||||
<< State.unknownFrees
|
||||
<< ")"
|
||||
<< std::endl;
|
||||
std::cout.imbue(locale);
|
||||
}
|
||||
|
||||
void Add(std::uint64_t address, std::uint64_t size) {
|
||||
const auto i = Map.find(address);
|
||||
if (i != end(Map)) {
|
||||
std::cout
|
||||
<< "WARNING: Repeated entry for "
|
||||
<< address
|
||||
<< " (size: "
|
||||
<< size
|
||||
<< ")."
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
Map.emplace(address, size);
|
||||
}
|
||||
|
||||
void ParseMalloc(const char *buffer) {
|
||||
const auto size = *reinterpret_cast<const std::uint64_t*>(buffer);
|
||||
const auto address = *reinterpret_cast<const std::uint64_t*>(buffer + 8);
|
||||
Add(address, size);
|
||||
++State.mallocs;
|
||||
}
|
||||
|
||||
void ParseRealloc(const char *buffer) {
|
||||
const auto old = *reinterpret_cast<const std::uint64_t*>(buffer);
|
||||
const auto size = *reinterpret_cast<const std::uint64_t*>(buffer + 8);
|
||||
const auto address = *reinterpret_cast<const std::uint64_t*>(buffer + 16);
|
||||
const auto i = Map.find(old);
|
||||
if (i == end(Map)) {
|
||||
++State.unknownReallocs;
|
||||
Add(address, size);
|
||||
} else if (old != address) {
|
||||
Map.erase(i);
|
||||
Add(address, size);
|
||||
++State.reallocs;
|
||||
} else {
|
||||
i->second = size;
|
||||
++State.reallocs;
|
||||
}
|
||||
}
|
||||
|
||||
void ParseFree(const char *buffer) {
|
||||
const auto address = *reinterpret_cast<const std::uint64_t*>(buffer);
|
||||
const auto i = Map.find(address);
|
||||
if (i == end(Map)) {
|
||||
++State.unknownFrees;
|
||||
} else {
|
||||
Map.erase(i);
|
||||
++State.frees;
|
||||
}
|
||||
}
|
||||
|
||||
long Parse(const char *buffer, const char *end) {
|
||||
auto result = 0;
|
||||
while (end > buffer) {
|
||||
const auto command = buffer[0];
|
||||
auto entry = 0;
|
||||
switch (command) {
|
||||
case 1: entry = 5 + 2 * sizeof(std::uint64_t); break;
|
||||
case 2: entry = 5 + 3 * sizeof(std::uint64_t); break;
|
||||
case 3: entry = 5 + sizeof(std::uint64_t); break;
|
||||
default:
|
||||
std::cout
|
||||
<< "WARNING: Garbage in trace file, command: "
|
||||
<< int(command)
|
||||
<< "."
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
if (end - buffer < entry) {
|
||||
break;
|
||||
}
|
||||
const auto time = *reinterpret_cast<const std::uint32_t*>(++buffer);
|
||||
buffer += 4;
|
||||
if (time > State.time) {
|
||||
PrintState();
|
||||
State.time = time;
|
||||
} else if (time < State.time) {
|
||||
std::cout
|
||||
<< "WARNING: Time "
|
||||
<< time
|
||||
<< " after "
|
||||
<< State.time
|
||||
<< "."
|
||||
<< std::endl;
|
||||
}
|
||||
switch (command) {
|
||||
case 1: ParseMalloc(buffer); break;
|
||||
case 2: ParseRealloc(buffer); break;
|
||||
case 3: ParseFree(buffer); break;
|
||||
}
|
||||
buffer += entry - 5;
|
||||
result += entry;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cout
|
||||
<< "Usage 'allocation_trace_reader [trace_file_path]'."
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
const auto file = fopen(argv[1], "rb");
|
||||
if (!file) {
|
||||
std::cout
|
||||
<< "ERROR: Could not open '"
|
||||
<< argv[1]
|
||||
<< "'."
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
char *data = Buffer;
|
||||
while (true) {
|
||||
const auto read = fread(data, 1, Buffer + kBufferSize - data, file);
|
||||
if (read == 0) {
|
||||
if (data != Buffer) {
|
||||
std::cout
|
||||
<< "WARNING: Trace file end is corrupt, could not parse: "
|
||||
<< std::size_t(data - Buffer)
|
||||
<< std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
data += read;
|
||||
const auto parsed = Parse(Buffer, data);
|
||||
if (parsed < 0) {
|
||||
break;
|
||||
}
|
||||
data -= parsed;
|
||||
if (data - Buffer > 0) {
|
||||
std::memmove(Buffer, Buffer + parsed, data - Buffer);
|
||||
}
|
||||
}
|
||||
PrintState();
|
||||
std::cout << "Mallocs: " << State.mallocs << "." << std::endl;
|
||||
std::cout << "Reallocs: " << State.reallocs << "." << std::endl;
|
||||
std::cout << "Frees: " << State.frees << "." << std::endl;
|
||||
if (State.unknownReallocs) {
|
||||
std::cout
|
||||
<< "Unknown realloc() calls: "
|
||||
<< State.unknownReallocs
|
||||
<< "."
|
||||
<< std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
172
linux_allocation_tracer/linux_allocation_tracer.cpp
Normal file
172
linux_allocation_tracer/linux_allocation_tracer.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "linux_allocation_tracer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::atomic<void(*)(size_t, void *)> MallocLogger;
|
||||
std::atomic<void(*)(size_t, void *)> VallocLogger;
|
||||
std::atomic<void(*)(size_t, void *)> PVallocLogger;
|
||||
std::atomic<void(*)(size_t, size_t, void *)> CallocLogger;
|
||||
std::atomic<void(*)(void *, size_t, void *)> ReallocLogger;
|
||||
std::atomic<void(*)(size_t, size_t, void *)> MemAlignLogger;
|
||||
std::atomic<void(*)(size_t, size_t, void *)> AlignedAllocLogger;
|
||||
std::atomic<void(*)(size_t, size_t, void *)> PosixMemAlignLogger;
|
||||
std::atomic<void(*)(void *)> FreeLogger;
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
//void *__real___malloc(size_t size);
|
||||
void *__real___libc_malloc(size_t size);
|
||||
|
||||
void *__real_malloc(size_t size);
|
||||
void *__real_valloc(size_t size);
|
||||
void *__real_pvalloc(size_t size);
|
||||
void *__real_calloc(size_t num, size_t size);
|
||||
void *__real_realloc(void *ptr, size_t size);
|
||||
void *__real_memalign(size_t alignment, size_t size);
|
||||
void *__real_aligned_alloc(size_t alignment, size_t size);
|
||||
int __real_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
void __real_free(void *ptr);
|
||||
|
||||
// void *__wrap___malloc(size_t size) {
|
||||
// const auto result = __real___malloc(size);
|
||||
// if (const auto logger = MallocLogger.load()) {
|
||||
// logger(size, result);
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
void *__wrap___libc_malloc(size_t size) {
|
||||
const auto result = __real___libc_malloc(size);
|
||||
if (const auto logger = MallocLogger.load()) {
|
||||
logger(size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_malloc(size_t size) {
|
||||
const auto result = __real_malloc(size);
|
||||
if (const auto logger = MallocLogger.load()) {
|
||||
logger(size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_valloc(size_t size) {
|
||||
const auto result = __real_valloc(size);
|
||||
if (const auto logger = VallocLogger.load()) {
|
||||
logger(size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_pvalloc(size_t size) {
|
||||
const auto result = __real_pvalloc(size);
|
||||
if (const auto logger = PVallocLogger.load()) {
|
||||
logger(size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_calloc(size_t num, size_t size) {
|
||||
const auto result = __real_calloc(num, size);
|
||||
if (const auto logger = CallocLogger.load()) {
|
||||
logger(num, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_realloc(void *ptr, size_t size) {
|
||||
const auto result = __real_realloc(ptr, size);
|
||||
if (const auto logger = ReallocLogger.load()) {
|
||||
logger(ptr, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_memalign(size_t alignment, size_t size) {
|
||||
const auto result = __real_memalign(alignment, size);
|
||||
if (const auto logger = MemAlignLogger.load()) {
|
||||
logger(alignment, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *__wrap_aligned_alloc(size_t alignment, size_t size) {
|
||||
const auto result = __real_aligned_alloc(alignment, size);
|
||||
if (const auto logger = AlignedAllocLogger.load()) {
|
||||
logger(alignment, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size) {
|
||||
const auto result = __real_posix_memalign(memptr, alignment, size);
|
||||
if (!result) {
|
||||
if (const auto logger = PosixMemAlignLogger.load()) {
|
||||
logger(alignment, size, *memptr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void __wrap_free(void *ptr) {
|
||||
if (const auto logger = FreeLogger.load()) {
|
||||
logger(ptr);
|
||||
}
|
||||
__real_free(ptr);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
void SetMallocLogger(void (*logger)(size_t, void *)) {
|
||||
MallocLogger = logger;
|
||||
}
|
||||
|
||||
void SetVallocLogger(void (*logger)(size_t, void *)) {
|
||||
VallocLogger = logger;
|
||||
}
|
||||
|
||||
void SetPVallocLogger(void (*logger)(size_t, void *)) {
|
||||
PVallocLogger = logger;
|
||||
}
|
||||
|
||||
void SetCallocLogger(void (*logger)(size_t, size_t, void *)) {
|
||||
CallocLogger = logger;
|
||||
}
|
||||
|
||||
void SetReallocLogger(void (*logger)(void *, size_t, void *)) {
|
||||
ReallocLogger = logger;
|
||||
}
|
||||
|
||||
void SetMemAlignLogger(void (*logger)(size_t, size_t, void *)) {
|
||||
MemAlignLogger = logger;
|
||||
}
|
||||
|
||||
void SetAlignedAllocLogger(void (*logger)(size_t, size_t, void *)) {
|
||||
AlignedAllocLogger = logger;
|
||||
}
|
||||
|
||||
void SetPosixMemAlignLogger(void (*logger)(size_t, size_t, void *)) {
|
||||
PosixMemAlignLogger = logger;
|
||||
}
|
||||
|
||||
void SetFreeLogger(void (*logger)(void *)) {
|
||||
FreeLogger = logger;
|
||||
}
|
||||
20
linux_allocation_tracer/linux_allocation_tracer.h
Normal file
20
linux_allocation_tracer/linux_allocation_tracer.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
void SetMallocLogger(void (*logger)(size_t, void *));
|
||||
void SetVallocLogger(void (*logger)(size_t, void *));
|
||||
void SetPVallocLogger(void (*logger)(size_t, void *));
|
||||
void SetCallocLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetReallocLogger(void (*logger)(void *, size_t, void *));
|
||||
void SetMemAlignLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetAlignedAllocLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetPosixMemAlignLogger(void (*logger)(size_t, size_t, void *));
|
||||
void SetFreeLogger(void (*logger)(void *));
|
||||
|
|
@ -33,6 +33,7 @@ option(DESKTOP_APP_DISABLE_X11_INTEGRATION "Disable all code for X11 integration
|
|||
option(DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION "Disable all code for Wayland integration (Linux only)." OFF)
|
||||
option(DESKTOP_APP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF)
|
||||
option(DESKTOP_APP_USE_GLIBC_WRAPS "Use wraps for new GLIBC features." OFF)
|
||||
option(DESKTOP_APP_USE_ALLOCATION_TRACER "Use simple allocation tracer (Linux only)." OFF)
|
||||
option(DESKTOP_APP_USE_PACKAGED "Find libraries using CMake instead of exact paths." ${no_special_target})
|
||||
option(DESKTOP_APP_USE_PACKAGED_LAZY "Bundle recommended Qt plugins for self-contained packages. (Linux only)" OFF)
|
||||
option(DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES "Bundle recommended Qt platform themes for self-contained packages. (Linux only)" ${DESKTOP_APP_USE_PACKAGED_LAZY})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue