-
Notifications
You must be signed in to change notification settings - Fork 444
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use libbacktrace for stack dumps if available
- Memory allocation tracing hooks to track where memory is being allocated and print summary with stack trace of each allocation site. Used when logging in ComputeWriteSet to understand how/why it is using so much memory.
- Loading branch information
Chris Dodd
committed
Aug 14, 2023
1 parent
7f53a12
commit 9f679e6
Showing
14 changed files
with
367 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#include "alloc_trace.h" | ||
|
||
#include "hex.h" | ||
#include "log.h" | ||
#include "n4.h" | ||
|
||
#if HAVE_EXECINFO_H | ||
#include <execinfo.h> | ||
#endif | ||
#if HAVE_LIBBACKTRACE | ||
#include <backtrace.h> | ||
|
||
#include "exename.h" | ||
#endif | ||
#if HAVE_CXXABI_H | ||
#include <cxxabi.h> | ||
#endif | ||
|
||
extern const char *addr2line(void *addr, const char *text); | ||
|
||
#if HAVE_LIBBACKTRACE | ||
extern struct backtrace_state *global_backtrace_state; | ||
|
||
void bt_error(void *, const char *msg, int) { BUG("%s", msg); } | ||
|
||
int bt_print_callback(void *out_, uintptr_t pc, const char *file, int line, const char *func) { | ||
std::ostream &out = *static_cast<std::ostream *>(out_); | ||
if (file) { | ||
if (const char *p = strstr(file, "/frontends/")) { | ||
file = p + 1; | ||
} else if (const char *p = strstr(file, "/backends/")) { | ||
file = p + 1; | ||
} else if (const char *p = strstr(file, "/extensions/")) { | ||
file = p + 1; | ||
} else if (const char *p = strstr(file, "/lib/")) { | ||
file = p + 1; | ||
} else if (const char *p = strstr(file, "/ir/")) { | ||
file = p + 1; | ||
} else if (const char *p = strstr(file, "/include/")) { | ||
file = p + 9; | ||
} | ||
} else { | ||
file = "??"; | ||
} | ||
out << Log::endl << file << ":" << line; | ||
if (func) { | ||
#if HAVE_CXXABI_H | ||
int status; | ||
if (char *fn = abi::__cxa_demangle(func, 0, 0, &status)) { | ||
if (strlen(fn) < 100) out << " (" << fn << ")"; | ||
free(fn); | ||
} else | ||
#endif | ||
out << " (" << func << ")"; | ||
} | ||
out << " [" << hex(pc) << "]"; | ||
return 0; | ||
} | ||
#endif | ||
|
||
void AllocTrace::count(void **bt, size_t sz) { | ||
backtrace tr(bt); | ||
data[tr][sz]++; | ||
} | ||
|
||
std::ostream &operator<<(std::ostream &out, const AllocTrace &at) { | ||
#if HAVE_LIBGC | ||
PauseTrace temp_pause; | ||
#endif | ||
typedef decltype(at.data)::value_type data_t; | ||
std::vector<std::pair<size_t, const data_t *>> sorted; | ||
size_t total_total = 0; | ||
for (auto &e : at.data) { | ||
size_t total = 0; | ||
for (auto &al : e.second) total += al.first * al.second; | ||
sorted.emplace_back(total, &e); | ||
total_total += total; | ||
} | ||
std::sort(sorted.begin(), sorted.end(), [](auto &a, auto &b) { return a.first > b.first; }); | ||
#if HAVE_LIBBACKTRACE | ||
if (!global_backtrace_state) | ||
global_backtrace_state = backtrace_create_state(exename(), 1, bt_error, &out); | ||
#endif | ||
out << "Allocated a total of " << n4(total_total) << "B memory"; | ||
for (auto &s : sorted) { | ||
if (s.first < 1000000) break; // ignore little stuff | ||
size_t count = 0; | ||
for (auto &al : s.second->second) count += al.second; | ||
out << Log::endl << "allocated " << n4(s.first) << "B in " << count << " calls"; | ||
#if HAVE_EXECINFO_H | ||
out << " from:" << Log::indent; | ||
#if HAVE_LIBBACKTRACE | ||
for (int i = 1; i < ALLOC_TRACE_DEPTH; ++i) { | ||
/* due to calling the callback multiple times for inlined functions, we need to | ||
* do this in ascending order or it makes no sense */ | ||
backtrace_pcinfo(global_backtrace_state, | ||
reinterpret_cast<uintptr_t>(s.second->first.trace[i]), | ||
bt_print_callback, bt_error, &out); | ||
} | ||
#else | ||
char **syms = backtrace_symbols(s.second->first.trace, ALLOC_TRACE_DEPTH); | ||
for (int i = ALLOC_TRACE_DEPTH - 1; i >= 1; --i) { | ||
const char *alt = addr2line(s.second->first.trace[i], syms[i]); | ||
out << Log::endl << (alt ? alt : syms[i]) << ' ' << s.second->first.trace[i]; | ||
} | ||
free(syms); | ||
#endif | ||
out << Log::unindent; | ||
#endif | ||
} | ||
return out; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
Copyright 2023-present Intel | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
#ifndef LIB_ALLOC_TRACE_H_ | ||
#define LIB_ALLOC_TRACE_H_ | ||
|
||
#include <map> | ||
#include <ostream> | ||
|
||
#include "exceptions.h" | ||
#include "gc.h" | ||
|
||
class AllocTrace { | ||
struct backtrace { | ||
void *trace[ALLOC_TRACE_DEPTH]; | ||
|
||
backtrace(const backtrace &) = default; | ||
explicit backtrace(void **bt) { memcpy(trace, bt, sizeof(trace)); } | ||
bool operator<(const backtrace &a) const { | ||
for (int i = 0; i < ALLOC_TRACE_DEPTH; ++i) | ||
if (trace[i] != a.trace[i]) return trace[i] < a.trace[i]; | ||
return false; | ||
} | ||
bool operator==(const backtrace &a) const { | ||
for (int i = 0; i < ALLOC_TRACE_DEPTH; ++i) | ||
if (trace[i] != a.trace[i]) return false; | ||
return true; | ||
} | ||
}; | ||
std::map<backtrace, std::map<size_t, int>> data; | ||
void count(void **, size_t); | ||
static void callback(void *t, void **bt, size_t sz) { | ||
static_cast<AllocTrace *>(t)->count(bt, sz); | ||
} | ||
|
||
public: | ||
void clear() { data.clear(); } | ||
#if HAVE_LIBGC | ||
alloc_trace_cb_t start() { return set_alloc_trace(callback, this); } | ||
void stop(alloc_trace_cb_t old) { | ||
auto tmp = set_alloc_trace(old); | ||
BUG_CHECK(tmp.fn == callback && tmp.arg == this, "AllocTrace stopped when not running"); | ||
} | ||
#else | ||
alloc_trace_cb_t start() { | ||
BUG("Can't trace allocations without garbage collection"); | ||
return alloc_trace_cb_t{}; | ||
} | ||
void stop(alloc_trace_cb_t) {} | ||
#endif | ||
friend std::ostream &operator<<(std::ostream &, const AllocTrace &); | ||
}; | ||
|
||
class PauseTrace { | ||
#if HAVE_LIBGC | ||
alloc_trace_cb_t hold; | ||
PauseTrace(const PauseTrace &) = delete; | ||
|
||
public: | ||
PauseTrace() { hold = set_alloc_trace(nullptr, nullptr); } | ||
~PauseTrace() { set_alloc_trace(hold); } | ||
#endif | ||
}; | ||
|
||
#endif /* LIB_ALLOC_TRACE_H_ */ |
Oops, something went wrong.