Skip to content

Commit 1956405

Browse files
committed
pinning log
1 parent b3284fc commit 1956405

File tree

4 files changed

+280
-55
lines changed

4 files changed

+280
-55
lines changed

src/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ FLAGS += -I$(LOCALBASE)/include
4848
endif
4949

5050
# GC source code. It depends on which GC implementation to use.
51-
GC_SRCS := gc-common gc-stacks gc-alloc-profiler gc-heap-snapshot
51+
GC_SRCS := gc-common gc-stacks gc-alloc-profiler gc-heap-snapshot gc-pinning-log
5252
ifeq (${USE_THIRD_PARTY_GC},mmtk)
5353
GC_SRCS += gc-mmtk
5454
else
@@ -69,7 +69,7 @@ CG_LLVMLINK :=
6969

7070
ifeq ($(JULIACODEGEN),LLVM)
7171
# Currently these files are used by both GCs. But we should make the list specific to stock, and MMTk should have its own implementation.
72-
GC_CODEGEN_SRCS := llvm-final-gc-lowering llvm-late-gc-lowering llvm-gc-invariant-verifier
72+
GC_CODEGEN_SRCS := llvm-final-gc-lowering llvm-late-gc-lowering llvm-gc-invariant-verifier gc-pinning-log
7373
ifeq (${USE_THIRD_PARTY_GC},mmtk)
7474
FLAGS += -I$(MMTK_API_INC)
7575
GC_CODEGEN_SRCS += llvm-late-gc-lowering-mmtk

src/gc-mmtk.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extern void mmtk_post_alloc(void* mutator, void* refer, size_t bytes, int alloca
5555
extern void mmtk_store_obj_size_c(void* obj, size_t size);
5656
extern bool mmtk_is_pinned(void* obj);
5757
extern unsigned char mmtk_pin_object(void* obj);
58+
extern bool mmtk_is_reachable_object(void* obj);
59+
extern bool mmtk_is_live_object(void* obj);
5860
extern bool mmtk_is_object_pinned(void* obj);
5961
extern unsigned char mmtk_pin_pointer(void* ptr);
6062
extern bool mmtk_is_pointer_pinned(void* ptr);
@@ -69,6 +71,8 @@ void jl_gc_init(void) {
6971
// TODO: use jl_options.heap_size_hint to set MMTk's fixed heap size? (see issue: https://github.com/mmtk/mmtk-julia/issues/167)
7072
JL_MUTEX_INIT(&finalizers_lock, "finalizers_lock");
7173

74+
jl_set_check_alive_fn(mmtk_is_reachable_object);
75+
7276
arraylist_new(&to_finalize, 0);
7377
arraylist_new(&finalizer_list_marked, 0);
7478
gc_num.interval = default_collect_interval;
@@ -332,6 +336,9 @@ JL_DLLEXPORT void jl_gc_prepare_to_collect(void)
332336
#endif
333337
errno = last_errno;
334338
// print_fragmentation();
339+
#ifdef ENABLE_PINNING_LOGGING
340+
jl_print_pinning_log();
341+
#endif
335342
}
336343

337344
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {

src/gc-pinning-log.cpp

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#include <cassert>
4+
#include <iostream>
5+
#include <map>
6+
#include <mutex>
7+
8+
#include "julia.h"
9+
10+
struct pinning_site_t {
11+
int lineno;
12+
const char *filename;
13+
pinning_site_t() : lineno(0), filename(nullptr) {}
14+
pinning_site_t(int line, const char *file) : lineno(line), filename(file) {}
15+
bool operator<(const pinning_site_t &other) const {
16+
if (lineno != other.lineno) {
17+
return lineno < other.lineno;
18+
}
19+
return filename < other.filename;
20+
}
21+
bool operator==(const pinning_site_t &other) const {
22+
return lineno == other.lineno && filename == other.filename;
23+
}
24+
};
25+
26+
struct pinning_log_entry_t {
27+
void *pinned_object;
28+
pinning_site_t site;
29+
pinning_log_entry_t() : pinned_object(nullptr), site(pinning_site_t{}) {}
30+
};
31+
32+
struct linear_pinning_log_t {
33+
#define BUFFER_CAPACITY (1ULL << 20)
34+
size_t idx;
35+
pinning_log_entry_t buffer[BUFFER_CAPACITY];
36+
linear_pinning_log_t() : idx(0) {
37+
for (size_t i = 0; i < BUFFER_CAPACITY; ++i) {
38+
buffer[i] = pinning_log_entry_t{};
39+
}
40+
}
41+
pinning_log_entry_t *bump_alloc_log_entry() {
42+
if (idx >= BUFFER_CAPACITY) {
43+
assert(0 && "Exceeded buffer capacity");
44+
}
45+
auto e = &buffer[idx++];
46+
return e;
47+
}
48+
void reset_log_buffer() {
49+
idx = 0;
50+
for (size_t i = 0; i < BUFFER_CAPACITY; ++i) {
51+
buffer[i] = pinning_log_entry_t{};
52+
}
53+
}
54+
};
55+
56+
struct coalesced_pinning_log_t {
57+
std::map<void *, std::map<pinning_site_t, size_t>> objects_to_pinning_sites;
58+
coalesced_pinning_log_t() = default;
59+
void add_pinning_event(void *pinned_object, const pinning_site_t &site) {
60+
if (objects_to_pinning_sites.find(pinned_object) == objects_to_pinning_sites.end()) {
61+
std::map<pinning_site_t, size_t> site_map{};
62+
objects_to_pinning_sites[pinned_object] = std::map<pinning_site_t, size_t>{};
63+
}
64+
auto &site_map = objects_to_pinning_sites[pinned_object];
65+
if (site_map.find(site) == site_map.end()) {
66+
site_map[site] = 0;
67+
}
68+
site_map[site]++;
69+
}
70+
};
71+
72+
struct pinning_log_t {
73+
linear_pinning_log_t linear_log;
74+
coalesced_pinning_log_t coalesced_log;
75+
check_alive_fn is_alive;
76+
std::mutex mu;
77+
pinning_log_entry_t *alloc_pinning_log_entry(void *pinned_object) {
78+
pinning_log_entry_t *e;
79+
mu.lock();
80+
e = linear_log.bump_alloc_log_entry();
81+
mu.unlock();
82+
return e;
83+
}
84+
void coalesce_linear_pinning_log() {
85+
mu.lock();
86+
for (size_t i = 0; i < linear_log.idx; ++i) {
87+
auto &entry = linear_log.buffer[i];
88+
if (entry.pinned_object != nullptr) {
89+
coalesced_log.add_pinning_event(entry.pinned_object, entry.site);
90+
}
91+
}
92+
linear_log.reset_log_buffer();
93+
mu.unlock();
94+
}
95+
void set_check_alive_fn(check_alive_fn fn) {
96+
mu.lock();
97+
is_alive = fn;
98+
mu.unlock();
99+
}
100+
void gc_log(void) {
101+
coalesce_linear_pinning_log();
102+
mu.lock();
103+
for (auto it = coalesced_log.objects_to_pinning_sites.begin(); it != coalesced_log.objects_to_pinning_sites.end();) {
104+
if (!is_alive(reinterpret_cast<jl_value_t *>(it->first))) {
105+
it = coalesced_log.objects_to_pinning_sites.erase(it);
106+
} else {
107+
++it;
108+
}
109+
}
110+
mu.unlock();
111+
}
112+
void print_pinning_log_as_json() {
113+
mu.lock();
114+
std::cerr << "[\n";
115+
bool first = true;
116+
for (const auto &object_entry : coalesced_log.objects_to_pinning_sites) {
117+
if (!first) {
118+
std::cerr << ",\n";
119+
}
120+
first = false;
121+
std::cerr << " {\n";
122+
std::cerr << " \"pinned_object\": \"" << object_entry.first << "\",\n";
123+
const char *type;
124+
if (is_alive(reinterpret_cast<jl_value_t *>(object_entry.first))) {
125+
type = jl_typeof_str(reinterpret_cast<jl_value_t *>(object_entry.first));
126+
} else {
127+
type = "unknown";
128+
}
129+
std::cerr << " \"type\": \"" << type << "\",\n";
130+
std::cerr << " \"pinning_sites\": [\n";
131+
bool first_site = true;
132+
for (const auto &site_entry : object_entry.second) {
133+
if (!first_site) {
134+
std::cerr << ",\n";
135+
}
136+
first_site = false;
137+
std::cerr << " {\n";
138+
auto filename = site_entry.first.filename ? site_entry.first.filename : "unknown";
139+
std::cerr << " \"filename\": \"" << filename << "\",\n";
140+
std::cerr << " \"lineno\": " << site_entry.first.lineno << ",\n";
141+
std::cerr << " \"count\": " << site_entry.second << "\n";
142+
std::cerr << " }";
143+
}
144+
std::cerr << "\n ]\n";
145+
std::cerr << " }";
146+
}
147+
std::cerr << "\n]\n";
148+
mu.unlock();
149+
}
150+
};
151+
152+
pinning_log_t pinning_log;
153+
154+
#ifdef __cplusplus
155+
extern "C" {
156+
#endif
157+
158+
int pinning_log_enabled;
159+
160+
JL_DLLEXPORT void jl_set_check_alive_fn(check_alive_fn fn) {
161+
pinning_log.set_check_alive_fn(fn);
162+
}
163+
JL_DLLEXPORT void jl_enable_pinning_log(void) {
164+
pinning_log_enabled = 1;
165+
}
166+
JL_DLLEXPORT void jl_gc_log(void) {
167+
if (!pinning_log_enabled) {
168+
return;
169+
}
170+
pinning_log.gc_log();
171+
}
172+
JL_DLLEXPORT void jl_log_pinning_event(void *pinned_object, const char *filename, int lineno) {
173+
if (!pinning_log_enabled) {
174+
return;
175+
}
176+
auto pe = pinning_log.alloc_pinning_log_entry(pinned_object);
177+
pe->pinned_object = pinned_object;
178+
pe->site.lineno = lineno;
179+
pe->site.filename = filename;
180+
}
181+
JL_DLLEXPORT void jl_print_pinning_log(void) {
182+
if (!pinning_log_enabled) {
183+
return;
184+
}
185+
pinning_log.coalesce_linear_pinning_log();
186+
pinning_log.print_pinning_log_as_json();
187+
jl_safe_printf("=========================\n");
188+
}
189+
190+
#ifdef __cplusplus
191+
}
192+
#endif

src/julia.h

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "libsupport.h"
2121
#include <stdint.h>
2222
#include <string.h>
23+
#include <stdbool.h>
2324

2425
#include "htable.h"
2526
#include "arraylist.h"
@@ -91,59 +92,6 @@ typedef struct _jl_value_t jl_value_t;
9192
extern "C" {
9293
#endif
9394

94-
// object pinning ------------------------------------------------------------
95-
96-
// FIXME: Pinning objects that get hashed in the ptrhash table
97-
// until we implement address space hashing.
98-
#define OBJHASH_PIN(key) if (key) jl_gc_pin_object(key);
99-
#define PTRHASH_PIN(key) if (key) jl_gc_pin_pointer(key);
100-
101-
// Called when pinning objects that would cause an error if moved
102-
// The difference: the argument for pin_object needs to pointer to an object (jl_value_t*),
103-
// but the argument for pin_pointer can be an internal pointer.
104-
#define OBJ_PIN(key) if (key) jl_gc_pin_object(key);
105-
#define PTR_PIN(key) if (key) jl_gc_pin_pointer(key);
106-
107-
#ifdef __cplusplus
108-
} // extern "C"
109-
#endif
110-
111-
#ifdef __cplusplus
112-
113-
// C++ template version
114-
template<typename T>
115-
class pinned_ref {
116-
T* ptr;
117-
public:
118-
explicit pinned_ref() : ptr(static_cast<T*>(assume(NULL))) {}
119-
explicit pinned_ref(void* p) : ptr(static_cast<T*>(p)) {}
120-
operator void*() const { return ptr; }
121-
T* get() const { return ptr; }
122-
static pinned_ref create(void* p) { OBJ_PIN(p); return pinned_ref(p); }
123-
static pinned_ref assume(void* p) { return pinned_ref(p); }
124-
};
125-
126-
// Redefine macros for C++ to use the template version
127-
#define jl_pinned_ref(T) pinned_ref<T>
128-
#define jl_pinned_ref_assume(T, ptr) pinned_ref<T>::assume(ptr)
129-
#define jl_pinned_ref_create(T, ptr) pinned_ref<T>::create(ptr)
130-
#define jl_pinned_ref_get(ref) (ref).get()
131-
132-
#else
133-
134-
// Primary type definition
135-
#define jl_pinned_ref(T) union { T* t; void* unused; }
136-
#define jl_pinned_ref_assume(T, ptr) ((jl_pinned_ref(T)){ .t = (ptr) })
137-
// Assignment macro
138-
#define jl_pinned_ref_set(lhs, ptr) OBJ_PIN(ptr); jl_pinned_ref_get(lhs) = ptr;
139-
// Getter macro
140-
#define jl_pinned_ref_get(ref) ((ref).t)
141-
142-
#endif
143-
144-
#ifdef __cplusplus
145-
extern "C" {
146-
#endif
14795
// core data types ------------------------------------------------------------
14896

14997
struct _jl_taggedvalue_bits {
@@ -1396,6 +1344,84 @@ STATIC_INLINE jl_value_t *jl_svecset(
13961344
JL_DLLEXPORT JL_CONST_FUNC jl_gcframe_t **(jl_get_pgcstack)(void) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT;
13971345
#define jl_current_task (container_of(jl_get_pgcstack(), jl_task_t, gcstack))
13981346

1347+
// object pinning ------------------------------------------------------------
1348+
1349+
typedef bool (*check_alive_fn)(void *);
1350+
JL_DLLEXPORT void jl_set_check_alive_fn(check_alive_fn fn);
1351+
JL_DLLEXPORT void jl_log_pinning_event(void *pinned_object, const char *filename, int lineno);
1352+
JL_DLLEXPORT void jl_print_pinning_log(void);
1353+
1354+
#define ENABLE_PINNING_LOGGING
1355+
#ifdef ENABLE_PINNING_LOGGING
1356+
#define LOG_PINNING_EVENT(key) do { \
1357+
jl_log_pinning_event(key, __FILE__, __LINE__); \
1358+
} while (0);
1359+
#else
1360+
#define LOG_PINNING_EVENT(key) ;
1361+
#endif
1362+
1363+
// FIXME: Pinning objects that get hashed in the ptrhash table
1364+
// until we implement addrePTR_PINss space hashing.
1365+
#define OBJHASH_PIN(key) do { \
1366+
if (key) { \
1367+
LOG_PINNING_EVENT(key); \
1368+
jl_gc_pin_object(key); \
1369+
} \
1370+
} while (0);
1371+
#define PTRHASH_PIN(key) if (key) jl_gc_pin_pointer(key);
1372+
1373+
// Called when pinning objects that would cause an error if moved
1374+
// The difference: the argument for pin_object needs to pointer to an object (jl_value_t*),
1375+
// but the argument for pin_pointer can be an internal pointer.
1376+
#define OBJ_PIN(key) do { \
1377+
if (key) { \
1378+
LOG_PINNING_EVENT(key); \
1379+
jl_gc_pin_object(key); \
1380+
} \
1381+
} while (0);
1382+
#define PTR_PIN(key) if (key) jl_gc_pin_pointer(key);
1383+
1384+
#ifdef __cplusplus
1385+
} // extern "C"
1386+
#endif
1387+
1388+
#ifdef __cplusplus
1389+
1390+
// C++ template version
1391+
template<typename T>
1392+
class pinned_ref {
1393+
T* ptr;
1394+
public:
1395+
explicit pinned_ref() : ptr(static_cast<T*>(assume(NULL))) {}
1396+
explicit pinned_ref(void* p) : ptr(static_cast<T*>(p)) {}
1397+
operator void*() const { return ptr; }
1398+
T* get() const { return ptr; }
1399+
static pinned_ref create(void* p) { OBJ_PIN(p); return pinned_ref(p); }
1400+
static pinned_ref assume(void* p) { return pinned_ref(p); }
1401+
};
1402+
1403+
// Redefine macros for C++ to use the template version
1404+
#define jl_pinned_ref(T) pinned_ref<T>
1405+
#define jl_pinned_ref_assume(T, ptr) pinned_ref<T>::assume(ptr)
1406+
#define jl_pinned_ref_create(T, ptr) pinned_ref<T>::create(ptr)
1407+
#define jl_pinned_ref_get(ref) (ref).get()
1408+
1409+
#else
1410+
1411+
// Primary type definition
1412+
#define jl_pinned_ref(T) union { T* t; void* unused; }
1413+
#define jl_pinned_ref_assume(T, ptr) ((jl_pinned_ref(T)){ .t = (ptr) })
1414+
// Assignment macro
1415+
#define jl_pinned_ref_set(lhs, ptr) OBJ_PIN(ptr); jl_pinned_ref_get(lhs) = ptr;
1416+
// Getter macro
1417+
#define jl_pinned_ref_get(ref) ((ref).t)
1418+
1419+
#endif
1420+
1421+
#ifdef __cplusplus
1422+
extern "C" {
1423+
#endif
1424+
13991425
STATIC_INLINE jl_value_t *jl_genericmemory_owner(jl_genericmemory_t *m JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT;
14001426

14011427
// write barriers

0 commit comments

Comments
 (0)