Skip to content

Commit

Permalink
Merge pull request #4 from vilterp/pv-alloc-profile-decode-in-julia
Browse files Browse the repository at this point in the history
decode in Julia
  • Loading branch information
vilterp authored Dec 10, 2021
2 parents 6844aba + 3e3e733 commit 10d79a8
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 258 deletions.
8 changes: 0 additions & 8 deletions base/gcutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,6 @@ Control whether garbage collection is enabled using a boolean argument (`true` f
"""
enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0

function start_alloc_profile(skip_every::Int=0)
ccall(:jl_start_alloc_profile, Cvoid, (Cint,), skip_every)
end

function stop_and_write_alloc_profile(io)
ccall(:jl_stop_and_write_alloc_profile, Cvoid, (Ptr{Cvoid},), io.handle)
end

"""
GC.enable_finalizers(on::Bool)
Expand Down
283 changes: 42 additions & 241 deletions src/gc-alloc-profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,248 +13,37 @@ using std::unordered_map;
using std::string;
using std::vector;

struct StackFrame {
string func_name;
string file_name;
intptr_t line_no;

string total; // cache of the above fields concatenated
};

struct RawBacktrace {
jl_bt_element_t *data;
size_t size;

RawBacktrace(
jl_bt_element_t *_data,
size_t _size
) : data(_data), size(_size) {}

~RawBacktrace() {
if (data != nullptr) {
free(data);
}
}
// Move constructor (X a = X{...})
RawBacktrace(RawBacktrace&& rhs) :
data(rhs.data), size(rhs.size)
{
rhs.data = nullptr;
rhs.size = 0;
}
private:
// Disallow copy and copy-assignment
RawBacktrace(const RawBacktrace&) = delete; // X b(a);
RawBacktrace& operator=(const RawBacktrace& other) = delete; // b = a;
};

struct Alloc {
size_t type_address;
struct RawAlloc {
jl_datatype_t *type_address;
RawBacktrace backtrace;
size_t size;
};

struct AllocProfile {
AllocProfile(int _skip_every) {
reset(_skip_every);
}

int skip_every;

vector<Alloc> allocs;
unordered_map<size_t, string> type_name_by_address;
vector<RawAlloc> allocs;
unordered_map<size_t, size_t> type_address_by_value_address;
unordered_map<size_t, size_t> frees_by_type_address;

size_t alloc_counter;
size_t last_recorded_alloc;

void reset(int _skip_every) {
skip_every = _skip_every;
alloc_counter = 0;
last_recorded_alloc = 0;
}

private:
AllocProfile(const AllocProfile&) = delete; // X b(a);
AllocProfile& operator=(const AllocProfile& other) = delete; // b = a;
};

// == global variables manipulated by callbacks ==

AllocProfile g_alloc_profile(0);
AllocProfile g_alloc_profile;
int g_alloc_profile_enabled = false;


// == utility functions ==

// https://stackoverflow.com/a/33799784/751061
// TODO: dedup with heap snapshot, or rebase off of that branch
void print_str_escape_json(ios_t *stream, const string &s) {
ios_printf(stream, "\"");
for (auto c = s.cbegin(); c != s.cend(); c++) {
switch (*c) {
case '"': ios_printf(stream, "\\\""); break;
case '\\': ios_printf(stream, "\\\\"); break;
case '\b': ios_printf(stream, "\\b"); break;
case '\f': ios_printf(stream, "\\f"); break;
case '\n': ios_printf(stream, "\\n"); break;
case '\r': ios_printf(stream, "\\r"); break;
case '\t': ios_printf(stream, "\\t"); break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
ios_printf(stream, "\\u%04x", (int)*c);
} else {
ios_printf(stream, "%c", *c);
}
}
}
ios_printf(stream, "\"");
}

struct StringTable {
typedef unordered_map<string, size_t> MapType;

MapType map;
vector<string> strings;

StringTable() {}
StringTable(std::initializer_list<string> strs) : strings(strs) {
for (const auto& str : strs) {
map.insert({str, map.size()});
}
}

size_t find_or_create_string_id(string key) {
auto val = map.find(key);
if (val == map.end()) {
val = map.insert(val, {key, map.size()});
strings.push_back(key);
}
return val->second;
}

void print_json_array(ios_t *stream, string key, bool newlines) {
ios_printf(stream, "[");
bool first = true;
size_t id = 0;
for (const auto &str : strings) {
if (first) {
first = false;
} else {
ios_printf(stream, newlines ? ",\n" : ",");
}
ios_printf(stream, "{\"id\":%zu", id);
id++;
ios_printf(stream, ",\"%s\":", key.c_str());
print_str_escape_json(stream, str);
ios_printf(stream, "}");
}
ios_printf(stream, "]");
}
};

string frame_as_string(jl_bt_element_t *entry, size_t entry_size) {
auto size_in_bytes = entry_size * sizeof(jl_bt_element_t);
char *buf = (char*)malloc(size_in_bytes);
for (int i=0; i < size_in_bytes; i++) {
buf[i] = ((char*)entry)[i];
}
return string(buf, size_in_bytes);
}

// https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c
bool ends_with(string const &full_string, string const &ending) {
if (full_string.length() >= ending.length()) {
return (0 == full_string.compare(full_string.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}

bool starts_with(string const &full_string, string const &beginning) {
if (full_string.length() >= beginning.length()) {
return (0 == full_string.compare(0, beginning.length(), beginning));
} else {
return false;
}
}

string _type_as_string(jl_datatype_t *type) {
if ((uintptr_t)type < 4096U) {
return "<corrupt>";
} else if (type == (jl_datatype_t*)jl_buff_tag) {
return "<buffer>";
} else if (type == (jl_datatype_t*)jl_malloc_tag) {
return "<malloc>";
} else if (type == jl_string_type) {
return "<string>";
} else if (type == jl_symbol_type) {
return "<symbol>";
} else if (jl_is_datatype(type)) {
ios_t str_;
ios_mem(&str_, 10024);
JL_STREAM* str = (JL_STREAM*)&str_;

jl_static_show(str, (jl_value_t*)type);

string type_str = string((const char*)str_.buf, str_.size);
ios_close(&str_);

return type_str;
} else {
return "<missing>";
}
}

// === stack stuff ===

vector<StackFrame> get_julia_frames(jl_bt_element_t *bt_entry) {
vector<StackFrame> ret;

size_t ip = jl_bt_entry_header(bt_entry);
jl_value_t *code = jl_bt_entry_jlvalue(bt_entry, 0);
if (jl_is_method_instance(code)) {
// When interpreting a method instance, need to unwrap to find the code info
code = ((jl_method_instance_t*)code)->uninferred;
}
if (jl_is_code_info(code)) {
jl_code_info_t *src = (jl_code_info_t*)code;
// See also the debug info handling in codegen.cpp.
// NB: debuginfoloc is 1-based!
intptr_t debuginfoloc = ((int32_t*)jl_array_data(src->codelocs))[ip];
while (debuginfoloc != 0) {
jl_line_info_node_t *locinfo = (jl_line_info_node_t*)
jl_array_ptr_ref(src->linetable, debuginfoloc - 1);
assert(jl_typeis(locinfo, jl_lineinfonode_type));
const char *func_name = "Unknown";
jl_value_t *method = locinfo->method;
if (jl_is_method_instance(method))
method = ((jl_method_instance_t*)method)->def.value;
if (jl_is_method(method))
method = (jl_value_t*)((jl_method_t*)method)->name;
if (jl_is_symbol(method))
func_name = jl_symbol_name((jl_sym_t*)method);

ret.push_back(StackFrame{
func_name,
jl_symbol_name(locinfo->file),
locinfo->line,
});

debuginfoloc = locinfo->inlined_at;
}
}
else {
// If we're using this function something bad has already happened;
// be a bit defensive to avoid crashing while reporting the crash.
jl_safe_printf("No code info - unknown interpreter state!\n");
}
return ret;
}

RawBacktrace get_raw_backtrace() {
static jl_bt_element_t bt_data[JL_MAX_BT_SIZE];
jl_bt_element_t *bt_data = (jl_bt_element_t*) malloc(JL_MAX_BT_SIZE);

// TODO: tune the number of frames that are skipped
size_t bt_size = rec_backtrace(bt_data, JL_MAX_BT_SIZE, 1);
Expand All @@ -268,35 +57,49 @@ RawBacktrace get_raw_backtrace() {
// == exported interface ==

JL_DLLEXPORT void jl_start_alloc_profile(int skip_every) {
jl_printf(JL_STDERR, "g_alloc_profile se:%d allocs:%d \n", g_alloc_profile.skip_every, g_alloc_profile.allocs.size());
g_alloc_profile_enabled = true;
g_alloc_profile.reset(skip_every);
g_alloc_profile = AllocProfile{skip_every};
}

extern "C" { // Needed since the function doesn't take any arguments.

JL_DLLEXPORT struct AllocResults jl_stop_and_write_alloc_profile() {
JL_DLLEXPORT struct RawAllocResults jl_stop_alloc_profile() {
g_alloc_profile_enabled = false;

return AllocResults{g_alloc_profile.allocs.size(),
g_alloc_profile.allocs.data()};
}
auto results = RawAllocResults{
g_alloc_profile.allocs.data(),
g_alloc_profile.allocs.size()
};

}
// package up frees
results.num_frees = g_alloc_profile.frees_by_type_address.size();
results.frees = (FreeInfo*) malloc(sizeof(FreeInfo) * results.num_frees);
int j = 0;
for (auto type_addr_free_count : g_alloc_profile.frees_by_type_address) {
results.frees[j++] = FreeInfo{
type_addr_free_count.first,
type_addr_free_count.second
};
}

// == callbacks called into by the outside ==
return results;
}

void register_type_string(jl_datatype_t *type) {
auto id = g_alloc_profile.type_name_by_address.find((size_t)type);
if (id != g_alloc_profile.type_name_by_address.end()) {
return;
JL_DLLEXPORT void jl_free_alloc_profile() {
g_alloc_profile.frees_by_type_address.clear();
g_alloc_profile.type_address_by_value_address.clear();
g_alloc_profile.alloc_counter = 0;
for (auto alloc : g_alloc_profile.allocs) {
free(alloc.backtrace.data);
}
g_alloc_profile.allocs.clear();
}

string type_str = _type_as_string(type);
g_alloc_profile.type_name_by_address[(size_t)type] = type_str;
}

void _record_allocated_value(jl_value_t *val, size_t size) {
// == callbacks called into by the outside ==

void _record_allocated_value(jl_value_t *val, size_t size) JL_NOTSAFEPOINT {
auto& profile = g_alloc_profile;
profile.alloc_counter++;
auto diff = profile.alloc_counter - profile.last_recorded_alloc;
Expand All @@ -306,19 +109,17 @@ void _record_allocated_value(jl_value_t *val, size_t size) {
profile.last_recorded_alloc = profile.alloc_counter;

auto type = (jl_datatype_t*)jl_typeof(val);
register_type_string(type);

profile.type_address_by_value_address[(size_t)val] = (size_t)type;

// TODO: get stack, push into vector
profile.allocs.emplace_back(Alloc{
(size_t) type,
profile.allocs.emplace_back(RawAlloc{
type,
get_raw_backtrace(),
size
});
}

void _record_freed_value(jl_taggedvalue_t *tagged_val) {
void _record_freed_value(jl_taggedvalue_t *tagged_val) JL_NOTSAFEPOINT {
jl_value_t *val = jl_valueof(tagged_val);

auto value_address = (size_t)val;
Expand All @@ -335,16 +136,16 @@ void _record_freed_value(jl_taggedvalue_t *tagged_val) {
}
}

void _report_gc_started() {
// TODO: remove these or make them toggle-able.

void _report_gc_started() JL_NOTSAFEPOINT {
// ...
}

// TODO: figure out how to pass all of these in as a struct
void _report_gc_finished(uint64_t pause, uint64_t freed, uint64_t allocd) {
void _report_gc_finished(uint64_t pause, uint64_t freed, uint64_t allocd) JL_NOTSAFEPOINT {
// TODO: figure out how to put in commas
jl_printf(
JL_STDERR,
"GC: pause %fms. collected %fMB. %lld allocs total\n",
jl_safe_printf("GC: pause %fms. collected %fMB. %lld allocs total\n",
pause/1e6, freed/1e6, allocd
);
}
Loading

0 comments on commit 10d79a8

Please sign in to comment.