Skip to content

Commit 6393ca1

Browse files
committed
page profile
1 parent c1e1d5c commit 6393ca1

File tree

5 files changed

+273
-13
lines changed

5 files changed

+273
-13
lines changed

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ SRCS := \
4444
jltypes gf typemap smallintset ast builtins module interpreter symbol \
4545
dlload sys init task array genericmemory staticdata toplevel jl_uv datatype \
4646
simplevector runtime_intrinsics precompile jloptions mtarraylist \
47-
threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler method \
47+
threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler gc-page-profiler method \
4848
jlapi signal-handling safepoint timing subtype rtutils gc-heap-snapshot \
4949
crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall
5050

src/gc-page-profiler.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#include "gc-page-profiler.h"
4+
5+
#ifdef __cplusplus
6+
extern "C" {
7+
#endif
8+
9+
// whether page profiling is enabled
10+
int page_profile_enabled;
11+
// number of pages written
12+
size_t page_profile_pages_written;
13+
// stream to write page profile to
14+
ios_t *page_profile_stream;
15+
// mutex for page profile
16+
uv_mutex_t page_profile_lock;
17+
18+
gc_page_profiler_serializer_t gc_page_serializer_create(void) JL_NOTSAFEPOINT
19+
{
20+
gc_page_profiler_serializer_t serializer;
21+
if (__unlikely(page_profile_enabled)) {
22+
arraylist_new(&serializer.typestrs, GC_PAGE_SZ);
23+
}
24+
else {
25+
serializer.typestrs.len = 0;
26+
}
27+
return serializer;
28+
}
29+
30+
void gc_page_serializer_init(gc_page_profiler_serializer_t *serializer,
31+
jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
32+
{
33+
if (__unlikely(page_profile_enabled)) {
34+
serializer->typestrs.len = 0;
35+
serializer->data = (char *)pg->data;
36+
serializer->osize = pg->osize;
37+
}
38+
}
39+
40+
void gc_page_serializer_destroy(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT
41+
{
42+
if (__unlikely(page_profile_enabled)) {
43+
arraylist_free(&serializer->typestrs);
44+
}
45+
}
46+
47+
void gc_page_serializer_write(gc_page_profiler_serializer_t *serializer,
48+
const char *str) JL_NOTSAFEPOINT
49+
{
50+
if (__unlikely(page_profile_enabled)) {
51+
arraylist_push(&serializer->typestrs, (void *)str);
52+
}
53+
}
54+
55+
void gc_enable_page_profile(void) JL_NOTSAFEPOINT
56+
{
57+
page_profile_enabled = 1;
58+
}
59+
60+
void gc_disable_page_profile(void) JL_NOTSAFEPOINT
61+
{
62+
page_profile_enabled = 0;
63+
}
64+
65+
int gc_page_profile_is_enabled(void) JL_NOTSAFEPOINT
66+
{
67+
return page_profile_enabled;
68+
}
69+
70+
void gc_page_profile_write_preamble(gc_page_profiler_serializer_t *serializer)
71+
JL_NOTSAFEPOINT
72+
{
73+
if (__unlikely(page_profile_enabled)) {
74+
char str[GC_TYPE_STR_MAXLEN];
75+
snprintf(str, GC_TYPE_STR_MAXLEN,
76+
"{\"address\": \"%p\",\"object_size\": %d,\"objects\": [",
77+
serializer->data, serializer->osize);
78+
ios_write(page_profile_stream, str, strlen(str));
79+
}
80+
}
81+
82+
void gc_page_profile_write_epilogue(gc_page_profiler_serializer_t *serializer)
83+
JL_NOTSAFEPOINT
84+
{
85+
if (__unlikely(page_profile_enabled)) {
86+
char str[GC_TYPE_STR_MAXLEN];
87+
snprintf(str, GC_TYPE_STR_MAXLEN, "]}");
88+
ios_write(page_profile_stream, str, strlen(str));
89+
}
90+
}
91+
92+
void gc_page_profile_write_comma(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT
93+
{
94+
if (__unlikely(page_profile_enabled)) {
95+
// write comma if not first page
96+
if (page_profile_pages_written > 0) {
97+
char str[GC_TYPE_STR_MAXLEN];
98+
snprintf(str, GC_TYPE_STR_MAXLEN, ",");
99+
ios_write(page_profile_stream, str, strlen(str));
100+
}
101+
}
102+
}
103+
104+
void gc_page_profile_write_to_file(gc_page_profiler_serializer_t *serializer)
105+
JL_NOTSAFEPOINT
106+
{
107+
if (__unlikely(page_profile_enabled)) {
108+
// write to file
109+
uv_mutex_lock(&page_profile_lock);
110+
gc_page_profile_write_comma(serializer);
111+
gc_page_profile_write_preamble(serializer);
112+
char str[GC_TYPE_STR_MAXLEN];
113+
for (size_t i = 0; i < serializer->typestrs.len; i++) {
114+
const char *name = (const char *)serializer->typestrs.items[i];
115+
if (name == GC_SERIALIZER_EMPTY) {
116+
snprintf(str, GC_TYPE_STR_MAXLEN, "\"empty\",");
117+
}
118+
else if (name == GC_SERIALIZER_GARBAGE) {
119+
snprintf(str, GC_TYPE_STR_MAXLEN, "\"garbage\",");
120+
}
121+
else {
122+
snprintf(str, GC_TYPE_STR_MAXLEN, "\"%s\",", name);
123+
}
124+
// remove trailing comma for last element
125+
if (i == serializer->typestrs.len - 1) {
126+
str[strlen(str) - 1] = '\0';
127+
}
128+
ios_write(page_profile_stream, str, strlen(str));
129+
}
130+
gc_page_profile_write_epilogue(serializer);
131+
page_profile_pages_written++;
132+
uv_mutex_unlock(&page_profile_lock);
133+
}
134+
}
135+
136+
void gc_page_profile_write_json_preamble(ios_t *stream) JL_NOTSAFEPOINT
137+
{
138+
if (__unlikely(page_profile_enabled)) {
139+
uv_mutex_lock(&page_profile_lock);
140+
char str[GC_TYPE_STR_MAXLEN];
141+
snprintf(str, GC_TYPE_STR_MAXLEN, "{\"pages\": [");
142+
ios_write(stream, str, strlen(str));
143+
uv_mutex_unlock(&page_profile_lock);
144+
}
145+
}
146+
147+
void gc_page_profile_write_json_epilogue(ios_t *stream) JL_NOTSAFEPOINT
148+
{
149+
if (__unlikely(page_profile_enabled)) {
150+
uv_mutex_lock(&page_profile_lock);
151+
char str[GC_TYPE_STR_MAXLEN];
152+
snprintf(str, GC_TYPE_STR_MAXLEN, "]}");
153+
ios_write(stream, str, strlen(str));
154+
uv_mutex_unlock(&page_profile_lock);
155+
}
156+
}
157+
158+
JL_DLLEXPORT void jl_gc_take_page_profile(ios_t *stream)
159+
{
160+
gc_enable_page_profile();
161+
page_profile_pages_written = 0;
162+
page_profile_stream = stream;
163+
gc_page_profile_write_json_preamble(stream);
164+
jl_gc_collect(JL_GC_FULL);
165+
gc_page_profile_write_json_epilogue(stream);
166+
gc_disable_page_profile();
167+
}
168+
169+
#ifdef __cplusplus
170+
}
171+
#endif

src/gc-page-profiler.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#ifndef GC_PAGE_PROFILER_H
4+
#define GC_PAGE_PROFILER_H
5+
6+
#include "gc.h"
7+
8+
#ifdef __cplusplus
9+
extern "C" {
10+
#endif
11+
12+
#define GC_TYPE_STR_MAXLEN (512)
13+
14+
typedef struct {
15+
arraylist_t typestrs;
16+
char *data;
17+
int osize;
18+
} gc_page_profiler_serializer_t;
19+
20+
// mutex for page profile
21+
extern uv_mutex_t page_profile_lock;
22+
23+
// Serializer functions
24+
gc_page_profiler_serializer_t gc_page_serializer_create(void) JL_NOTSAFEPOINT;
25+
void gc_page_serializer_init(gc_page_profiler_serializer_t *serializer, jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT;
26+
void gc_page_serializer_destroy(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
27+
void gc_page_serializer_write(gc_page_profiler_serializer_t *serializer, const char *str) JL_NOTSAFEPOINT;
28+
// Page profile functions
29+
#define GC_SERIALIZER_EMPTY ((const char *)0x1)
30+
#define GC_SERIALIZER_GARBAGE ((const char *)0x2)
31+
STATIC_INLINE void gc_page_profile_write_empty_page(gc_page_profiler_serializer_t *serializer,
32+
int enabled) JL_NOTSAFEPOINT
33+
{
34+
if (__unlikely(enabled)) {
35+
gc_page_serializer_write(serializer, GC_SERIALIZER_EMPTY);
36+
}
37+
}
38+
STATIC_INLINE void gc_page_profile_write_garbage(gc_page_profiler_serializer_t *serializer,
39+
int enabled) JL_NOTSAFEPOINT
40+
{
41+
if (__unlikely(enabled)) {
42+
gc_page_serializer_write(serializer, GC_SERIALIZER_GARBAGE);
43+
}
44+
}
45+
STATIC_INLINE void gc_page_profile_write_live_obj(gc_page_profiler_serializer_t *serializer,
46+
jl_taggedvalue_t *v,
47+
int enabled) JL_NOTSAFEPOINT
48+
{
49+
if (__unlikely(enabled)) {
50+
const char *name = jl_typeof_str(jl_valueof(v));
51+
gc_page_serializer_write(serializer, name);
52+
}
53+
}
54+
void gc_enable_page_profile(void) JL_NOTSAFEPOINT;
55+
void gc_disable_page_profile(void) JL_NOTSAFEPOINT;
56+
int gc_page_profile_is_enabled(void) JL_NOTSAFEPOINT;
57+
void gc_page_profile_write_to_file(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
58+
59+
#ifdef __cplusplus
60+
}
61+
#endif
62+
63+
#endif // GC_PAGE_PROFILER_H

src/gc.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is a part of Julia. License is MIT: https://julialang.org/license
22

33
#include "gc.h"
4+
#include "gc-page-profiler.h"
45
#include "julia.h"
56
#include "julia_gcext.h"
67
#include "julia_assert.h"
@@ -1426,7 +1427,7 @@ STATIC_INLINE void gc_dump_page_utilization_data(void) JL_NOTSAFEPOINT
14261427
int64_t buffered_pages = 0;
14271428

14281429
// Returns pointer to terminal pointer of list rooted at *pfl.
1429-
static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *buffered,
1430+
static void gc_sweep_page(gc_page_profiler_serializer_t *s, jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *buffered,
14301431
jl_gc_pagemeta_t *pg, int osize) JL_NOTSAFEPOINT
14311432
{
14321433
char *data = pg->data;
@@ -1438,6 +1439,9 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
14381439
}
14391440
size_t old_nfree = pg->nfree;
14401441
size_t nfree;
1442+
gc_page_serializer_init(s, pg);
1443+
// avoid loading a global variable in the hot path
1444+
int enabled = gc_page_profile_is_enabled();
14411445

14421446
int re_use_page = 1;
14431447
int keep_as_local_buffer = 0;
@@ -1457,6 +1461,7 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
14571461
}
14581462
#endif
14591463
nfree = (GC_PAGE_SZ - GC_PAGE_OFFSET) / osize;
1464+
gc_page_profile_write_empty_page(s, enabled);
14601465
goto done;
14611466
}
14621467
// For quick sweep, we might be able to skip the page if the page doesn't
@@ -1466,6 +1471,7 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
14661471
if (!prev_sweep_full || pg->prev_nold == pg->nold) {
14671472
freedall = 0;
14681473
nfree = pg->nfree;
1474+
gc_page_profile_write_empty_page(s, enabled);
14691475
goto done;
14701476
}
14711477
}
@@ -1483,12 +1489,14 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
14831489
int bits = v->bits.gc;
14841490
// if an object is past `lim_newpages` then we can guarantee it's garbage
14851491
if (!gc_marked(bits) || (char*)v >= lim_newpages) {
1492+
gc_page_profile_write_garbage(s, enabled);
14861493
*pfl = v;
14871494
pfl = &v->next;
14881495
pfl_begin = (pfl_begin != NULL) ? pfl_begin : pfl;
14891496
pg_nfree++;
14901497
}
14911498
else { // marked young or old
1499+
gc_page_profile_write_live_obj(s, v, enabled);
14921500
if (current_sweep_full || bits == GC_MARKED) { // old enough
14931501
bits = v->bits.gc = GC_OLD; // promote
14941502
}
@@ -1532,6 +1540,7 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
15321540
push_lf_back(&global_page_pool_lazily_freed, pg);
15331541
}
15341542
}
1543+
gc_page_profile_write_to_file(s);
15351544
gc_update_page_fragmentation_data(pg);
15361545
gc_time_count_page(freedall, pg_skpd);
15371546
jl_ptls_t ptls = gc_all_tls_states[pg->thread_n];
@@ -1540,15 +1549,15 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
15401549
}
15411550

15421551
// the actual sweeping over all allocated pages in a memory pool
1543-
STATIC_INLINE void gc_sweep_pool_page(jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *lazily_freed,
1552+
STATIC_INLINE void gc_sweep_pool_page(gc_page_profiler_serializer_t *s, jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *lazily_freed,
15441553
jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
15451554
{
15461555
int p_n = pg->pool_n;
15471556
int t_n = pg->thread_n;
15481557
jl_ptls_t ptls2 = gc_all_tls_states[t_n];
15491558
jl_gc_pool_t *p = &ptls2->heap.norm_pools[p_n];
15501559
int osize = pg->osize;
1551-
gc_sweep_page(p, allocd, lazily_freed, pg, osize);
1560+
gc_sweep_page(s, p, allocd, lazily_freed, pg, osize);
15521561
}
15531562

15541563
// sweep over all memory that is being used and not in a pool
@@ -1585,11 +1594,20 @@ void gc_sweep_wake_all(void)
15851594
uv_mutex_unlock(&gc_threads_lock);
15861595
}
15871596

1597+
void gc_sweep_wait_for_all(void)
1598+
{
1599+
jl_atomic_store(&gc_allocd_scratch, NULL);
1600+
while (jl_atomic_load_relaxed(&gc_n_threads_sweeping) != 0) {
1601+
jl_cpu_pause();
1602+
}
1603+
}
1604+
15881605
void gc_sweep_pool_parallel(void)
15891606
{
15901607
jl_atomic_fetch_add(&gc_n_threads_sweeping, 1);
15911608
jl_gc_page_stack_t *allocd_scratch = jl_atomic_load(&gc_allocd_scratch);
15921609
if (allocd_scratch != NULL) {
1610+
gc_page_profiler_serializer_t serializer = gc_page_serializer_create();
15931611
while (1) {
15941612
int found_pg = 0;
15951613
for (int t_i = 0; t_i < gc_n_threads; t_i++) {
@@ -1602,25 +1620,18 @@ void gc_sweep_pool_parallel(void)
16021620
if (pg == NULL) {
16031621
continue;
16041622
}
1605-
gc_sweep_pool_page(allocd, &ptls2->page_metadata_buffered, pg);
1623+
gc_sweep_pool_page(&serializer, allocd, &ptls2->page_metadata_buffered, pg);
16061624
found_pg = 1;
16071625
}
16081626
if (!found_pg) {
16091627
break;
16101628
}
16111629
}
1630+
gc_page_serializer_destroy(&serializer);
16121631
}
16131632
jl_atomic_fetch_add(&gc_n_threads_sweeping, -1);
16141633
}
16151634

1616-
void gc_sweep_wait_for_all(void)
1617-
{
1618-
jl_atomic_store(&gc_allocd_scratch, NULL);
1619-
while (jl_atomic_load_relaxed(&gc_n_threads_sweeping) != 0) {
1620-
jl_cpu_pause();
1621-
}
1622-
}
1623-
16241635
void gc_free_pages(void)
16251636
{
16261637
while (1) {
@@ -3862,6 +3873,7 @@ void jl_gc_init(void)
38623873
{
38633874
JL_MUTEX_INIT(&heapsnapshot_lock, "heapsnapshot_lock");
38643875
JL_MUTEX_INIT(&finalizers_lock, "finalizers_lock");
3876+
uv_mutex_init(&page_profile_lock);
38653877
uv_mutex_init(&gc_cache_lock);
38663878
uv_mutex_init(&gc_perm_lock);
38673879
uv_mutex_init(&gc_threads_lock);

0 commit comments

Comments
 (0)