Loading...
--- /dev/null
+++ libmalloc/libmalloc-792.80.2/tools/ktrace_count.cpp
@@ -0,0 +1,301 @@
+#include <iomanip>
+#include <iostream>
+#include <map>
+
+#include "../src/trace.h"
+#include <ktrace/ktrace.h>
+#include <sys/kdebug.h>
+
+typedef struct {
+ // allocation size -> number of allocations
+ std::map<size_t, size_t> allocs;
+ size_t null_allocs;
+ size_t total_allocs;
+ // allocation size -> number of frees
+ std::map<size_t, size_t> frees;
+ size_t null_frees;
+ size_t total_frees;
+} analyze_ktrace_thread_t;
+
+typedef struct {
+ // tid -> analyze_ktrace_thread_t
+ std::map<uint64_t, analyze_ktrace_thread_t> thread;
+ // ptr -> allocation size
+ std::unordered_map<uintptr_t, size_t> ptr_size;
+} analyze_ktrace_t;
+
+static inline size_t
+roundup_log2(size_t sz)
+{
+ if (!(sz & (sz - 1u))) {
+ return sz;
+ } else {
+ return (1ul << (CHAR_BIT * sizeof(sz) - (unsigned)__builtin_clzl(sz)));
+ }
+}
+
+static inline void
+track_alloc(analyze_ktrace_t &ak, uint64_t tid, uintptr_t ptr, size_t sz)
+{
+ analyze_ktrace_thread_t &thread = ak.thread[tid];
+
+ if (ptr) {
+ ++thread.allocs[roundup_log2(sz)];
+ ak.ptr_size[ptr] = sz;
+
+ ++thread.total_allocs;
+ } else {
+ ++thread.null_allocs;
+ }
+}
+
+static inline void
+track_free(analyze_ktrace_t &ak, uint64_t tid, uintptr_t ptr)
+{
+ analyze_ktrace_thread_t &thread = ak.thread[tid];
+
+ if (ptr) {
+ auto it = ak.ptr_size.find(ptr);
+ if (it != ak.ptr_size.end()) {
+ ++thread.frees[roundup_log2(it->second)];
+ ak.ptr_size.erase(it);
+ }
+
+ ++thread.total_frees;
+ } else {
+ ++thread.null_frees;
+ }
+}
+
+static inline void
+track_thread(analyze_ktrace_t &ak, uint64_t tid)
+{
+ ak.thread.emplace(tid, analyze_ktrace_thread_t{});
+}
+
+static void
+add_empty_log2(std::map<size_t, size_t> &sizes)
+{
+ // add zero entries, starting from largest allocation size
+ auto rit = sizes.rbegin();
+ if (rit != sizes.rend()) {
+ size_t max_pow2 = rit->first;
+ do {
+ max_pow2 /= 2;
+ sizes.emplace(max_pow2, 0);
+ } while (max_pow2);
+ }
+}
+
+static bool
+analyze_ktrace(ktrace_session_t ks, analyze_ktrace_t &ak)
+{
+ if (ktrace_events_subclass(ks, DBG_UMALLOC, DBG_UMALLOC_EXTERNAL,
+ (^(ktrace_event_t event) {
+ switch (event->debugid) {
+ case TRACE_DATA_NEWTHREAD:
+ // args: child_tid, pid, exec, uniqueid
+ track_thread(ak, event->arg1);
+ break;
+
+ case TRACE_malloc | DBG_FUNC_START:
+ // args: zone, size, type_id, 0
+ break;
+ case TRACE_malloc | DBG_FUNC_END:
+ // args: zone, size, ptr, type_id
+ track_alloc(
+ ak, event->threadid, event->arg3, event->arg2);
+ break;
+
+ case TRACE_malloc_options | DBG_FUNC_START:
+ // args: zone, align, size, 0
+ break;
+ case TRACE_malloc_options | DBG_FUNC_END:
+ // args: zone, align, size, ptr
+ track_alloc(
+ ak, event->threadid, event->arg4, event->arg3);
+ break;
+
+ case TRACE_calloc | DBG_FUNC_START:
+ // args: zone, num_items, size, type_id
+ break;
+ case TRACE_calloc | DBG_FUNC_END:
+ // args: zone, num_items, size, ptr
+ track_alloc(ak, event->threadid, event->arg4,
+ event->arg2 * event->arg3);
+ break;
+
+ case TRACE_valloc | DBG_FUNC_START:
+ // args: zone, size, type_id, 0
+ break;
+ case TRACE_valloc | DBG_FUNC_END:
+ // args: zone, size, ptr, type_id
+ track_alloc(
+ ak, event->threadid, event->arg3, event->arg2);
+ break;
+
+ case TRACE_memalign | DBG_FUNC_START:
+ // args: zone, alignment, size, type_id
+ break;
+ case TRACE_memalign | DBG_FUNC_END:
+ // args: zone, alignment, size, ptr
+ track_alloc(
+ ak, event->threadid, event->arg4, event->arg3);
+ break;
+
+ case TRACE_realloc | DBG_FUNC_START:
+ // args: zone, ptr, size, type_id
+ break;
+ case TRACE_realloc | DBG_FUNC_END:
+ // args: zone, ptr, size, new_ptr
+ track_free(ak, event->threadid, event->arg2);
+ track_alloc(
+ ak, event->threadid, event->arg4, event->arg3);
+ break;
+
+ case TRACE_free:
+ // args: zone, ptr, (ptr) ? *(uintptr_t*)ptr : 0, 0
+ track_free(ak, event->threadid, event->arg2);
+ break;
+ }
+ }))) {
+ return false;
+ }
+
+ dispatch_semaphore_t done = dispatch_semaphore_create(0);
+ if (!done) {
+ return false;
+ }
+
+ ktrace_set_completion_handler(ks, ^{
+ ktrace_end(ks, false);
+
+ dispatch_semaphore_signal(done);
+ });
+
+ dispatch_queue_t mq =
+ dispatch_queue_create("analyze_ktrace", DISPATCH_QUEUE_SERIAL);
+ if (ktrace_start(ks, mq)) {
+ return false;
+ }
+
+ dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
+
+ for (auto &t : ak.thread) {
+ add_empty_log2(t.second.allocs);
+ add_empty_log2(t.second.frees);
+ }
+
+ return true;
+}
+
+static constexpr unsigned FIELD_WIDTH = 10;
+
+static void
+print_thread(const analyze_ktrace_thread_t &at)
+{
+ std::cout << "====== Allocation Sizes ======" << std::endl;
+ for (const auto &kv : at.allocs) {
+ const double alloc_percent =
+ (100. * ((double)kv.second / (double)at.total_allocs));
+ std::cout << "≤ " << std::setw(FIELD_WIDTH) << kv.first << ": "
+ << std::setw(FIELD_WIDTH) << kv.second << " ("
+ << alloc_percent << "%)" << std::endl;
+ }
+ std::cout << std::setw(FIELD_WIDTH + 4)
+ << "Total: " << std::setw(FIELD_WIDTH) << at.total_allocs << "\n"
+ << std::setw(FIELD_WIDTH + 4)
+ << "NULL: " << std::setw(FIELD_WIDTH) << at.null_allocs << "\n"
+ << std::endl;
+
+ std::cout << "========= Free Sizes =========" << std::endl;
+ for (const auto &kv : at.frees) {
+ const double free_percent =
+ (100. * ((double)kv.second / (double)at.total_frees));
+ std::cout << "≤ " << std::setw(FIELD_WIDTH) << kv.first << ": "
+ << std::setw(FIELD_WIDTH) << kv.second << " (" << free_percent
+ << "%)" << std::endl;
+ }
+ std::cout << std::setw(FIELD_WIDTH + 4)
+ << "Total: " << std::setw(FIELD_WIDTH) << at.total_frees << "\n"
+ << std::setw(FIELD_WIDTH + 4)
+ << "NULL: " << std::setw(FIELD_WIDTH) << at.null_frees
+ << std::endl;
+}
+
+static void
+print_analysis(const analyze_ktrace_t &ak)
+{
+ analyze_ktrace_thread_t aggregate{};
+ bool should_aggregate = (ak.thread.size() > 1);
+
+ std::cout << std::fixed << std::setprecision(2);
+
+ for (const auto &t : ak.thread) {
+ if (should_aggregate) {
+ for (const auto &kv : t.second.allocs) {
+ aggregate.allocs[kv.first] += kv.second;
+ }
+ aggregate.null_allocs += t.second.null_allocs;
+ aggregate.total_allocs += t.second.total_allocs;
+
+ for (const auto &kv : t.second.frees) {
+ aggregate.frees[kv.first] += kv.second;
+ }
+ aggregate.null_frees += t.second.null_frees;
+ aggregate.total_frees += t.second.total_frees;
+ }
+
+ std::cout << "\n<<<<<< Thread 0x" << std::setw(8) << std::setfill('0')
+ << std::hex << t.first << " >>>>>>" << std::endl;
+
+ std::cout << std::dec << std::setfill(' ');
+ print_thread(t.second);
+ }
+
+ if (should_aggregate) {
+ std::cout << "\n<<<<<<<<<< Aggregate >>>>>>>>>>" << std::endl;
+ print_thread(aggregate);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0;
+
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " <input ktrace> " << std::endl;
+ return -1;
+ }
+
+ std::cout << "Parsing '" << argv[1] << "'..." << std::endl;
+
+ ktrace_file_t kf = ktrace_file_open(argv[1], false);
+ if (!kf) {
+ std::cerr << "Error: Cannot open ktrace file '" << argv[1] << "'"
+ << std::endl;
+ return -1;
+ }
+
+ analyze_ktrace_t ak{};
+ ktrace_session_t ks = ktrace_session_create_with_flags(KTSF_NONE);
+ // takes ownership of the ktrace_file_t
+ if (ktrace_set_ktrace_file(ks, kf, true)) {
+ ktrace_file_close(kf);
+ std::cerr << "Error: Cannot create ktrace session" << std::endl;
+ ret = -1;
+ goto out;
+ }
+
+ if (!analyze_ktrace(ks, ak)) {
+ std::cerr << "Error: Cannot analyze '" << argv[1] << "'" << std::endl;
+ ret = -1;
+ }
+
+ print_analysis(ak);
+
+out:
+ ktrace_session_destroy(ks);
+ return ret;
+}