215 lines
6 KiB
C++
215 lines
6 KiB
C++
// 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
|
|
//
|
|
#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;
|
|
}
|