1
0
Fork 0

Add simple allocation tracer on Linux.

This commit is contained in:
John Preston 2021-05-14 15:09:28 +04:00
parent 538b39ec9d
commit 42d3840cde
7 changed files with 462 additions and 0 deletions

View file

@ -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()

View file

@ -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

View 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})

View 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;
}

View 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;
}

View 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 *));

View file

@ -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})