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

GC page profiler #52567

Merged
merged 1 commit into from
Dec 23, 2023
Merged
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
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ SRCS := \
jltypes gf typemap smallintset ast builtins module interpreter symbol \
dlload sys init task array genericmemory staticdata toplevel jl_uv datatype \
simplevector runtime_intrinsics precompile jloptions mtarraylist \
threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler method \
threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler gc-page-profiler method \
jlapi signal-handling safepoint timing subtype rtutils gc-heap-snapshot \
crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall

Expand Down
167 changes: 167 additions & 0 deletions src/gc-page-profiler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#include "gc-page-profiler.h"

#ifdef __cplusplus
extern "C" {
#endif

// whether page profiling is enabled
int page_profile_enabled;
// number of pages written
size_t page_profile_pages_written;
// stream to write page profile to
ios_t *page_profile_stream;
// mutex for page profile
uv_mutex_t page_profile_lock;

gc_page_profiler_serializer_t gc_page_serializer_create(void) JL_NOTSAFEPOINT
{
gc_page_profiler_serializer_t serializer;
if (__unlikely(page_profile_enabled)) {
arraylist_new(&serializer.typestrs, GC_PAGE_SZ);
}
else {
serializer.typestrs.len = 0;
}
return serializer;
}

void gc_page_serializer_init(gc_page_profiler_serializer_t *serializer,
jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
serializer->typestrs.len = 0;
serializer->data = (char *)pg->data;
serializer->osize = pg->osize;
}
}

void gc_page_serializer_destroy(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
arraylist_free(&serializer->typestrs);
}
}

void gc_page_serializer_write(gc_page_profiler_serializer_t *serializer,
const char *str) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
arraylist_push(&serializer->typestrs, (void *)str);
}
}

void gc_enable_page_profile(void) JL_NOTSAFEPOINT
{
page_profile_enabled = 1;
}

void gc_disable_page_profile(void) JL_NOTSAFEPOINT
{
page_profile_enabled = 0;
}

int gc_page_profile_is_enabled(void) JL_NOTSAFEPOINT
{
return page_profile_enabled;
}

void gc_page_profile_write_preamble(gc_page_profiler_serializer_t *serializer)
JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
char str[GC_TYPE_STR_MAXLEN];
snprintf(str, GC_TYPE_STR_MAXLEN,
"{\"address\": \"%p\",\"object_size\": %d,\"objects\": [",
serializer->data, serializer->osize);
ios_write(page_profile_stream, str, strlen(str));
}
}

void gc_page_profile_write_epilogue(gc_page_profiler_serializer_t *serializer)
JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
const char *str = "]}";
ios_write(page_profile_stream, str, strlen(str));
}
}

void gc_page_profile_write_comma(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
// write comma if not first page
if (page_profile_pages_written > 0) {
const char *str = ",";
ios_write(page_profile_stream, str, strlen(str));
}
}
}

void gc_page_profile_write_to_file(gc_page_profiler_serializer_t *serializer)
JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
// write to file
uv_mutex_lock(&page_profile_lock);
gc_page_profile_write_comma(serializer);
gc_page_profile_write_preamble(serializer);
char str[GC_TYPE_STR_MAXLEN];
for (size_t i = 0; i < serializer->typestrs.len; i++) {
const char *name = (const char *)serializer->typestrs.items[i];
if (name == GC_SERIALIZER_EMPTY) {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"empty\",");
}
else if (name == GC_SERIALIZER_GARBAGE) {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"garbage\",");
}
else {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"%s\",", name);
}
// remove trailing comma for last element
if (i == serializer->typestrs.len - 1) {
str[strlen(str) - 1] = '\0';
}
ios_write(page_profile_stream, str, strlen(str));
}
gc_page_profile_write_epilogue(serializer);
page_profile_pages_written++;
uv_mutex_unlock(&page_profile_lock);
}
}

void gc_page_profile_write_json_preamble(ios_t *stream) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
uv_mutex_lock(&page_profile_lock);
const char *str = "{\"pages\": [";
ios_write(stream, str, strlen(str));
uv_mutex_unlock(&page_profile_lock);
}
}

void gc_page_profile_write_json_epilogue(ios_t *stream) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
uv_mutex_lock(&page_profile_lock);
const char *str = "]}";
ios_write(stream, str, strlen(str));
uv_mutex_unlock(&page_profile_lock);
}
}

JL_DLLEXPORT void jl_gc_take_page_profile(ios_t *stream)
{
gc_enable_page_profile();
page_profile_pages_written = 0;
page_profile_stream = stream;
gc_page_profile_write_json_preamble(stream);
jl_gc_collect(JL_GC_FULL);
gc_page_profile_write_json_epilogue(stream);
gc_disable_page_profile();
}

#ifdef __cplusplus
}
#endif
63 changes: 63 additions & 0 deletions src/gc-page-profiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#ifndef GC_PAGE_PROFILER_H
#define GC_PAGE_PROFILER_H

#include "gc.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GC_TYPE_STR_MAXLEN (512)

typedef struct {
arraylist_t typestrs;
char *data;
int osize;
} gc_page_profiler_serializer_t;

// mutex for page profile
extern uv_mutex_t page_profile_lock;

// Serializer functions
gc_page_profiler_serializer_t gc_page_serializer_create(void) JL_NOTSAFEPOINT;
void gc_page_serializer_init(gc_page_profiler_serializer_t *serializer, jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT;
void gc_page_serializer_destroy(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
void gc_page_serializer_write(gc_page_profiler_serializer_t *serializer, const char *str) JL_NOTSAFEPOINT;
// Page profile functions
#define GC_SERIALIZER_EMPTY ((const char *)0x1)
#define GC_SERIALIZER_GARBAGE ((const char *)0x2)
STATIC_INLINE void gc_page_profile_write_empty_page(gc_page_profiler_serializer_t *serializer,
int enabled) JL_NOTSAFEPOINT
{
if (__unlikely(enabled)) {
gc_page_serializer_write(serializer, GC_SERIALIZER_EMPTY);
}
}
STATIC_INLINE void gc_page_profile_write_garbage(gc_page_profiler_serializer_t *serializer,
int enabled) JL_NOTSAFEPOINT
{
if (__unlikely(enabled)) {
gc_page_serializer_write(serializer, GC_SERIALIZER_GARBAGE);
}
}
STATIC_INLINE void gc_page_profile_write_live_obj(gc_page_profiler_serializer_t *serializer,
jl_taggedvalue_t *v,
int enabled) JL_NOTSAFEPOINT
{
if (__unlikely(enabled)) {
const char *name = jl_typeof_str(jl_valueof(v));
gc_page_serializer_write(serializer, name);
}
}
void gc_enable_page_profile(void) JL_NOTSAFEPOINT;
void gc_disable_page_profile(void) JL_NOTSAFEPOINT;
int gc_page_profile_is_enabled(void) JL_NOTSAFEPOINT;
void gc_page_profile_write_to_file(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;

#ifdef __cplusplus
}
#endif

#endif // GC_PAGE_PROFILER_H
Loading