linux_allocation_trace_reader.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include <iostream>
  8. #include <iomanip>
  9. #include <locale>
  10. #include <unordered_map>
  11. #include <cstring>
  12. #include <ctime>
  13. #include <vector>
  14. constexpr auto kBufferSize = 1024 * 1024;
  15. char Buffer[kBufferSize];
  16. struct Fields {
  17. std::uint32_t time = 0;
  18. std::size_t mallocs = 0;
  19. std::size_t reallocs = 0;
  20. std::size_t frees = 0;
  21. std::size_t unknownReallocs = 0;
  22. std::size_t unknownFrees = 0;
  23. };
  24. Fields State;
  25. std::unordered_map<std::uint64_t, std::size_t> Map;
  26. std::vector<Fields> Snapshots;
  27. void WriteTime(std::uint32_t time) {
  28. std::time_t t = std::time_t(time);
  29. const auto parsed = std::localtime(&t);
  30. std::cout
  31. << std::setw(2) << std::setfill('0') << parsed->tm_hour
  32. << ":"
  33. << std::setw(2) << std::setfill('0') << parsed->tm_min
  34. << ":"
  35. << std::setw(2) << std::setfill('0') << parsed->tm_sec;
  36. }
  37. void PrintState() {
  38. if (!State.time) {
  39. return;
  40. }
  41. WriteTime(State.time);
  42. auto full = std::uint64_t(0);
  43. for (const auto &[address, amount] : Map) {
  44. full += amount;
  45. }
  46. class NumPunct final : public std::numpunct<char> {
  47. protected:
  48. char do_thousands_sep() const override { return '\''; }
  49. std::string do_grouping() const override { return "\03"; }
  50. };
  51. const auto &locale = std::cout.getloc();
  52. std::cout.imbue(std::locale(std::locale::classic(), new NumPunct()));
  53. std::cout
  54. << ": "
  55. << std::setw(13) << std::setfill(' ') << full
  56. << " (unknown: "
  57. << State.unknownFrees
  58. << ")"
  59. << std::endl;
  60. std::cout.imbue(locale);
  61. }
  62. void Add(std::uint64_t address, std::uint64_t size) {
  63. const auto i = Map.find(address);
  64. if (i != end(Map)) {
  65. std::cout
  66. << "WARNING: Repeated entry for "
  67. << address
  68. << " (size: "
  69. << size
  70. << ")."
  71. << std::endl;
  72. return;
  73. }
  74. Map.emplace(address, size);
  75. }
  76. void ParseMalloc(const char *buffer) {
  77. const auto size = *reinterpret_cast<const std::uint64_t*>(buffer);
  78. const auto address = *reinterpret_cast<const std::uint64_t*>(buffer + 8);
  79. Add(address, size);
  80. ++State.mallocs;
  81. }
  82. void ParseRealloc(const char *buffer) {
  83. const auto old = *reinterpret_cast<const std::uint64_t*>(buffer);
  84. const auto size = *reinterpret_cast<const std::uint64_t*>(buffer + 8);
  85. const auto address = *reinterpret_cast<const std::uint64_t*>(buffer + 16);
  86. const auto i = Map.find(old);
  87. if (i == end(Map)) {
  88. ++State.unknownReallocs;
  89. Add(address, size);
  90. } else if (old != address) {
  91. Map.erase(i);
  92. Add(address, size);
  93. ++State.reallocs;
  94. } else {
  95. i->second = size;
  96. ++State.reallocs;
  97. }
  98. }
  99. void ParseFree(const char *buffer) {
  100. const auto address = *reinterpret_cast<const std::uint64_t*>(buffer);
  101. const auto i = Map.find(address);
  102. if (i == end(Map)) {
  103. ++State.unknownFrees;
  104. } else {
  105. Map.erase(i);
  106. ++State.frees;
  107. }
  108. }
  109. long Parse(const char *buffer, const char *end) {
  110. auto result = 0;
  111. while (end > buffer) {
  112. const auto command = buffer[0];
  113. auto entry = 0;
  114. switch (command) {
  115. case 1: entry = 5 + 2 * sizeof(std::uint64_t); break;
  116. case 2: entry = 5 + 3 * sizeof(std::uint64_t); break;
  117. case 3: entry = 5 + sizeof(std::uint64_t); break;
  118. default:
  119. std::cout
  120. << "WARNING: Garbage in trace file, command: "
  121. << int(command)
  122. << "."
  123. << std::endl;
  124. return -1;
  125. }
  126. if (end - buffer < entry) {
  127. break;
  128. }
  129. const auto time = *reinterpret_cast<const std::uint32_t*>(++buffer);
  130. buffer += 4;
  131. if (time > State.time) {
  132. PrintState();
  133. State.time = time;
  134. } else if (time < State.time) {
  135. std::cout
  136. << "WARNING: Time "
  137. << time
  138. << " after "
  139. << State.time
  140. << "."
  141. << std::endl;
  142. }
  143. switch (command) {
  144. case 1: ParseMalloc(buffer); break;
  145. case 2: ParseRealloc(buffer); break;
  146. case 3: ParseFree(buffer); break;
  147. }
  148. buffer += entry - 5;
  149. result += entry;
  150. }
  151. return result;
  152. }
  153. int main(int argc, char *argv[]) {
  154. if (argc != 2) {
  155. std::cout
  156. << "Usage 'allocation_trace_reader [trace_file_path]'."
  157. << std::endl;
  158. return -1;
  159. }
  160. const auto file = fopen(argv[1], "rb");
  161. if (!file) {
  162. std::cout
  163. << "ERROR: Could not open '"
  164. << argv[1]
  165. << "'."
  166. << std::endl;
  167. return -1;
  168. }
  169. char *data = Buffer;
  170. while (true) {
  171. const auto read = fread(data, 1, Buffer + kBufferSize - data, file);
  172. if (read == 0) {
  173. if (data != Buffer) {
  174. std::cout
  175. << "WARNING: Trace file end is corrupt, could not parse: "
  176. << std::size_t(data - Buffer)
  177. << std::endl;
  178. }
  179. break;
  180. }
  181. data += read;
  182. const auto parsed = Parse(Buffer, data);
  183. if (parsed < 0) {
  184. break;
  185. }
  186. data -= parsed;
  187. if (data - Buffer > 0) {
  188. std::memmove(Buffer, Buffer + parsed, data - Buffer);
  189. }
  190. }
  191. PrintState();
  192. std::cout << "Mallocs: " << State.mallocs << "." << std::endl;
  193. std::cout << "Reallocs: " << State.reallocs << "." << std::endl;
  194. std::cout << "Frees: " << State.frees << "." << std::endl;
  195. if (State.unknownReallocs) {
  196. std::cout
  197. << "Unknown realloc() calls: "
  198. << State.unknownReallocs
  199. << "."
  200. << std::endl;
  201. }
  202. return 0;
  203. }