diff --git a/heaptrace.cc b/heaptrace.cc index 6fe97de..3158ef9 100644 --- a/heaptrace.cc +++ b/heaptrace.cc @@ -31,7 +31,7 @@ enum options { static struct argp_option heaptrace_options[] = { { "help", 'h', 0, 0, "Give this help list" }, { "top", OPT_top, "NUM", 0, "Set number of top backtraces to show (default 10)" }, - { "sort", 's', "KEY", 0, "Sort backtraces based on KEY (size or count)" }, + { "sort", 's', "KEYs", 0, "Sort backtraces based on KEYs (size or count)" }, { "flame-graph", OPT_flamegraph, 0, 0, "Print heap trace info in flamegraph format" }, { "outfile", OPT_outfile, "FILE", 0, "Save log messages to this file" }, { 0 } @@ -51,7 +51,7 @@ static error_t parse_option(int key, char *arg, struct argp_state *state) break; case 's': - opts->sortkey = arg; + opts->sort_keys = arg; break; case OPT_flamegraph: @@ -102,7 +102,7 @@ static void init_options(int argc, char *argv[]) // set default option values opts.top = 10; - opts.sortkey = "size"; + opts.sort_keys = "size"; opts.flamegraph = false; argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opts); @@ -129,7 +129,7 @@ static void setup_child_environ(struct opts *opts, int argc, char *argv[]) snprintf(buf, sizeof(buf), "%d", opts->top); setenv("HEAPTRACE_NUM_TOP_BACKTRACE", buf, 1); - setenv("HEAPTRACE_SORTKEY", opts->sortkey, 1); + setenv("HEAPTRACE_SORT_KEYS", opts->sort_keys, 1); snprintf(buf, sizeof(buf), "%d", opts->flamegraph); setenv("HEAPTRACE_FLAME_GRAPH", buf, 1); diff --git a/heaptrace.h b/heaptrace.h index ce3ef8e..0172b5c 100644 --- a/heaptrace.h +++ b/heaptrace.h @@ -39,7 +39,7 @@ struct opts { char *exename; int top; - const char *sortkey; + const char *sort_keys; bool flamegraph; char *outfile; }; diff --git a/libheaptrace.cc b/libheaptrace.cc index 81462b3..0c064ca 100644 --- a/libheaptrace.cc +++ b/libheaptrace.cc @@ -13,6 +13,7 @@ #include #include +#include #include "heaptrace.h" #include "compiler.h" @@ -89,7 +90,7 @@ static void heaptrace_init() // setup option values opts.top = strtol(getenv("HEAPTRACE_NUM_TOP_BACKTRACE"), NULL, 0); - opts.sortkey = getenv("HEAPTRACE_SORTKEY"); + opts.sort_keys = getenv("HEAPTRACE_SORT_KEYS"); opts.flamegraph = strtol(getenv("HEAPTRACE_FLAME_GRAPH"), NULL, 0); opts.outfile = getenv("HEAPTRACE_OUTFILE"); @@ -114,7 +115,6 @@ static void heaptrace_fini() { auto* tfs = &thread_flags; int pid = getpid(); - enum alloc_sort_order order = ALLOC_SIZE; std::string comm = utils::get_comm_name(); if (!opts.flamegraph) { @@ -122,10 +122,7 @@ static void heaptrace_fini() pid, comm.c_str()); } - if (!strcmp(opts.sortkey, "count")) - order = ALLOC_COUNT; - - dump_stackmap(order, opts.flamegraph); + dump_stackmap(opts.sort_keys, opts.flamegraph); if (opts.outfile) fclose(outfp); diff --git a/sighandler.cc b/sighandler.cc index 2ffb30c..971483e 100644 --- a/sighandler.cc +++ b/sighandler.cc @@ -8,13 +8,13 @@ static void sigusr1_handler(int signo) { pr_dbg("\n=== sigusr1_handler(%d) ===\n", signo); - dump_stackmap(ALLOC_SIZE, opts.flamegraph); + dump_stackmap("size", opts.flamegraph); } static void sigusr2_handler(int signo) { pr_dbg("\n=== sigusr2_handler(%d) ===\n", signo); - dump_stackmap(ALLOC_COUNT, opts.flamegraph); + dump_stackmap("count", opts.flamegraph); } static void sigquit_handler(int signo) diff --git a/stacktrace.cc b/stacktrace.cc index adc0060..b991525 100644 --- a/stacktrace.cc +++ b/stacktrace.cc @@ -245,23 +245,47 @@ std::string read_statm() { return str; } -static void print_dump_stackmap(const time_point_t& current, struct mallinfo& info, - std::vector>& sorted_stack) +static void print_dump_stackmap_header(const char *sort_key) { - int cnt = 0; + pr_out("[heaptrace] dump allocation sorted by '%s' for /proc/%d/maps (%s)\n", + sort_key, utils::gettid(), utils::get_comm_name().c_str()); +} + +static void print_dump_stackmap_footer( + const std::vector>& sorted_stack) +{ + // get allocated size info from the allocator + struct mallinfo minfo = mallinfo(); + uint64_t total_size = 0; - int tid = utils::gettid(); + size_t stack_size = sorted_stack.size(); + for (int i = 0; i < stack_size; i++) { + const stack_info_t& sinfo = sorted_stack[i].second; + total_size += sinfo.total_size; + } + + pr_out("[heaptrace] heap traced num of backtrace : %zd\n", stack_size); + + pr_out("[heaptrace] heap traced allocation size : %s\n", + get_byte_unit(total_size).c_str()); - pr_out("=================================================================\n"); - pr_out("[heaptrace] dump allocation status for /proc/%d/maps (%s)\n", - tid, utils::get_comm_name().c_str()); + pr_out("[heaptrace] allocator info (virtual) : %s\n", + get_byte_unit(minfo.arena + minfo.hblkhd).c_str()); + pr_out("[heaptrace] allocator info (resident) : %s\n", + get_byte_unit(minfo.uordblks).c_str()); + + pr_out("[heaptrace] statm info (VSS/RSS/shared) : %s\n", read_statm().c_str()); +} + +static void print_dump_stackmap(std::vector>& sorted_stack) +{ + const time_point_t current = std::chrono::steady_clock::now(); + int cnt = 0; size_t stack_size = sorted_stack.size(); for (int i = 0; i < stack_size; i++) { const stack_info_t& info = sorted_stack[i].second; - total_size += info.total_size; - if (i >= opts.top) continue; @@ -280,22 +304,6 @@ static void print_dump_stackmap(const time_point_t& current, struct mallinfo& in pr_out("\n"); } - - pr_out("[heaptrace] heap traced num of backtrace : %zd\n", - stack_size); - pr_out("[heaptrace] heap traced allocation size : %s\n", - get_byte_unit(total_size).c_str()); - - pr_out("[heaptrace] allocator info (virtual) : %s\n", - get_byte_unit(info.arena + info.hblkhd).c_str()); - pr_out("[heaptrace] allocator info (resident) : %s\n", - get_byte_unit(info.uordblks).c_str()); - - pr_out("[heaptrace] statm info (VSS/RSS/shared) : %s\n", - read_statm().c_str()); - pr_out("=================================================================\n"); - - fflush(outfp); } static void print_dump_stackmap_flamegraph(std::vector>& sorted_stack) @@ -322,21 +330,36 @@ static void print_dump_stackmap_flamegraph(std::vector>& sorted_stack) +{ + std::sort(sorted_stack.begin(), sorted_stack.end(), + [&order](const std::pair& p1, + const std::pair& p2) { + if (order == "count") { + if (p1.second.count == p2.second.count) + return p1.second.total_size > p2.second.total_size; + return p1.second.count > p2.second.count; + } + else { + // sort based on size for unknown sort order. + if (p1.second.total_size == p2.second.total_size) + return p1.second.count > p2.second.count; + return p1.second.total_size > p2.second.total_size; + } + }); +} + +void dump_stackmap(const char *sort_keys, bool flamegraph) { auto* tfs = &thread_flags; - time_point_t current; if (stackmap.empty()) return; - tfs->hook_guard = true; - - // get allocated size info from the allocator - struct mallinfo info = mallinfo(); + std::vector sort_key_vec = utils::string_split(sort_keys, ','); - // get current time - current = std::chrono::steady_clock::now(); + tfs->hook_guard = true; // sort the stack trace based on the count and then total_size std::vector> sorted_stack; @@ -347,27 +370,23 @@ void dump_stackmap(enum alloc_sort_order order, bool flamegraph) for (auto& p : stackmap) sorted_stack.push_back(make_pair(p.first, p.second)); } - std::sort(sorted_stack.begin(), sorted_stack.end(), - [order](const std::pair& p1, - const std::pair& p2) { - if (order == ALLOC_COUNT) { - if (p1.second.count == p2.second.count) - return p1.second.total_size > p2.second.total_size; - return p1.second.count > p2.second.count; - } - else if (order == ALLOC_SIZE) { - if (p1.second.total_size == p2.second.total_size) - return p1.second.count > p2.second.count; - return p1.second.total_size > p2.second.total_size; - } - // not implemented yet - abort(); - }); - if (flamegraph) + if (flamegraph) { + // use only the first sort order given by -s/--sort option. + sort_stack(sort_key_vec.front(), sorted_stack); print_dump_stackmap_flamegraph(sorted_stack); - else - print_dump_stackmap(current, info, sorted_stack); + } + else { + pr_out("=================================================================\n"); + for (const auto& sort_key : sort_key_vec) { + print_dump_stackmap_header(sort_key.c_str()); + sort_stack(sort_key, sorted_stack); + print_dump_stackmap(sorted_stack); + } + print_dump_stackmap_footer(sorted_stack); + pr_out("=================================================================\n"); + fflush(outfp); + } tfs->hook_guard = false; } diff --git a/stacktrace.h b/stacktrace.h index 2265e6e..79c15aa 100644 --- a/stacktrace.h +++ b/stacktrace.h @@ -32,12 +32,6 @@ struct object_info_t { uint64_t size; }; -enum alloc_sort_order { - ALLOC_COUNT, - ALLOC_SIZE, - ALLOC_AGE, -}; - void __record_backtrace(size_t size, void* addr, stack_trace_t& stack_trace, int nptrs); @@ -58,7 +52,7 @@ inline void record_backtrace(size_t size, void* addr) void release_backtrace(void* addr); -void dump_stackmap(enum alloc_sort_order order, bool flamegraph = false); +void dump_stackmap(const char *sort_str, bool flamegraph = false); void clear_stackmap(void);