Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ignore #28

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/heaptrace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum options {
OPT_sort,
OPT_flamegraph,
OPT_outfile,
OPT_ignore,
};

static struct argp_option heaptrace_options[] = {
Expand All @@ -34,6 +35,7 @@ static struct argp_option heaptrace_options[] = {
{ "sort", 's', "KEYs", 0, "Sort backtraces based on KEYs (size or count)" },
{ "flame-graph", OPT_flamegraph, nullptr, 0, "Print heap trace info in flamegraph format" },
{ "outfile", OPT_outfile, "FILE", 0, "Save log messages to this file" },
{ "ignore", OPT_ignore, "FILE", 0, "Apply ignore rules from this file" },
{ nullptr }
};

Expand Down Expand Up @@ -62,6 +64,10 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
opts->outfile = arg;
break;

case OPT_ignore:
opts->ignore = arg;
break;

case ARGP_KEY_ARG:
if (state->arg_num)
return ARGP_ERR_UNKNOWN;
Expand Down Expand Up @@ -136,6 +142,9 @@ static void setup_child_environ(struct opts *opts, int argc, char *argv[])

if (opts->outfile)
setenv("HEAPTRACE_OUTFILE", opts->outfile, 1);

if (opts->ignore)
setenv("HEAPTRACE_IGNORE", opts->ignore, 1);
}

int main(int argc, char *argv[])
Expand Down
1 change: 1 addition & 0 deletions src/heaptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct opts {
const char *sort_keys;
bool flamegraph;
char *outfile;
char *ignore;
};

extern opts opts;
Expand Down
114 changes: 79 additions & 35 deletions src/stacktrace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

#include <algorithm>
#include <fstream>
#include <iomanip>
#include <map>
#include <sstream>
#include <vector>

#include <mutex>

#include "compiler.h"
Expand All @@ -28,9 +28,40 @@

std::map<stack_trace_t, stack_info_t> stackmap;
std::map<addr_t, object_info_t> addrmap;
std::vector<std::string> ignorevec;
bool ignorevec_initialized = false;

std::recursive_mutex container_mutex;

static void lazyinit_ignorevec()
{
if (ignorevec_initialized)
return;

opts.ignore = getenv("HEAPTRACE_IGNORE");
if (opts.ignore) {
std::ifstream file(opts.ignore);
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
ignorevec.push_back(line);
}
file.close();
}
else {
pr_out("Failed to open file %s\n", opts.ignore);
}
}
ignorevec_initialized = true;
}

static bool is_ignored(const std::string &report)
{
lazyinit_ignorevec();
return std::any_of(ignorevec.begin(), ignorevec.end(), [&report](const std::string& s)
{ return report.find(s) != std::string::npos; });
}

// record_backtrace() is defined in stacktrace.h as an inline function.
void __record_backtrace(size_t size, void *addr, stack_trace_t &stack_trace, int nptrs)
{
Expand Down Expand Up @@ -89,7 +120,7 @@ void release_backtrace(void *addr)
addrmap.erase(addrit);
}

static void print_backtrace_symbol(int count, void *addr)
static void get_backtrace_string(int count, void *addr, std::stringstream &ss_bt)
{
Dl_info dlip;
char *symbol;
Expand All @@ -98,15 +129,12 @@ static void print_backtrace_symbol(int count, void *addr)
int dl_ret;
int len = SYMBOL_MAXLEN;

#if __SIZEOF_LONG__ == 4
pr_out("%2d [%#10lx] ", count, (unsigned long)addr);
#else
pr_out("%2d [%#14lx] ", count, (unsigned long)addr);
#endif
ss_bt << std::dec << count << " [0x" << std::hex <<
std::setw(4 + __SIZEOF_LONG__) << (unsigned long)addr << "] ";
// dladdr() translates address to symbolic info.
dl_ret = dladdr(addr, &dlip);
if (dl_ret == 0) {
pr_out("?\n");
ss_bt << "?\n";
return;
}

Expand All @@ -123,14 +151,15 @@ static void print_backtrace_symbol(int count, void *addr)
}
offset = static_cast<int>(static_cast<int *>(addr) -
static_cast<int *>(dlip.dli_saddr));
pr_out("%s +%#x ", symbol, offset);
ss_bt << symbol << " +0x" << offset << " ";
free(symbol);
}
offset = (int)((char *)addr - (char *)(dlip.dli_fbase));
pr_out("(%s +%#x)\n", dlip.dli_fname, offset);
ss_bt << "(" << dlip.dli_fname << " +0x" << offset << ")\n";
}

static void print_backtrace_symbol_flamegraph(void *addr, const char *semicolon)
static void get_backtrace_string_flamegraph(void *addr, const char *semicolon,
std::stringstream &ss_bt)
{
Dl_info dlip;
char *symbol;
Expand All @@ -156,11 +185,11 @@ static void print_backtrace_symbol_flamegraph(void *addr, const char *semicolon)
}
offset = static_cast<int>(static_cast<int *>(addr) -
static_cast<int *>(dlip.dli_saddr));
pr_out("%s%s+%#x", semicolon, symbol, offset);
ss_bt << semicolon << symbol << "+0x" << offset;
free(symbol);
}
else {
pr_out("%s%s+%p", semicolon, dlip.dli_fname, addr);
ss_bt << semicolon << dlip.dli_fname << addr;
}
}

Expand Down Expand Up @@ -281,50 +310,65 @@ print_dump_stackmap_footer(const std::vector<std::pair<stack_trace_t, stack_info
static void print_dump_stackmap(std::vector<std::pair<stack_trace_t, stack_info_t>> &sorted_stack)
{
const time_point_t current = std::chrono::steady_clock::now();
int cnt = 0;
int cnt = 1;
int top = opts.top;
int i = 0;

size_t stack_size = sorted_stack.size();
for (int i = 0; i < stack_size; i++) {
while (i < stack_size && i < top) {
const stack_info_t &info = sorted_stack[i].second;

if (i >= opts.top)
break;

const stack_trace_t &stack_trace = sorted_stack[i].first;
std::string age = get_delta_time_unit(current - info.birth_time);

pr_out("=== backtrace #%d === [count/peak: %zd/%zd] "
"[size/peak: %s/%s] [age: %s]\n",
++cnt, info.count, info.peak_count, get_byte_unit(info.total_size).c_str(),
get_byte_unit(info.peak_total_size).c_str(), age.c_str());

std::stringstream ss_intro;
std::stringstream ss_bt;

ss_intro << "=== backtrace #" << cnt << " === [count/peak: " << info.count << "/"
<< info.peak_count << "] "
<< "[size/peak: " << get_byte_unit(info.total_size) << "/"
<< get_byte_unit(info.peak_total_size) << "] [age: " << age << "]\n";
ss_bt << std::setfill('0');
for (int j = 0; j < info.stack_depth; j++)
print_backtrace_symbol(j, stack_trace[j]);
get_backtrace_string(j, stack_trace[j], ss_bt);

pr_out("\n");
if (is_ignored(ss_bt.str())) {
++top;
}
else {
pr_out("%s%s\n", ss_intro.str().c_str(), ss_bt.str().c_str());
++cnt;
}
++i;
}
}

static void
print_dump_stackmap_flamegraph(std::vector<std::pair<stack_trace_t, stack_info_t>> &sorted_stack)
{
size_t stack_size = sorted_stack.size();
for (int i = 0; i < stack_size; i++) {
int i = 0;
int top = opts.top;

while (i < stack_size && i < top) {
const stack_info_t &info = sorted_stack[i].second;
uint64_t size = info.total_size;
const char *semicolon = "";

if (i >= opts.top)
break;

const stack_trace_t &stack_trace = sorted_stack[i].first;
std::stringstream ss_bt;

ss_bt << std::hex;
for (size_t j = 0; j < info.stack_depth; ++j) {
print_backtrace_symbol_flamegraph(stack_trace[info.stack_depth - 1 - j],
semicolon);
get_backtrace_string_flamegraph(stack_trace[info.stack_depth - 1 - j],
semicolon, ss_bt);
semicolon = ";";
}
pr_out(" %" PRIu64 "\n", size);
if (is_ignored(ss_bt.str())) {
++top;
}
else {
pr_out("%s", ss_bt.str().c_str());
pr_out(" %" PRIu64 "\n", size);
}
++i;
}

fflush(outfp);
Expand Down