Skip to content

Commit

Permalink
page profile
Browse files Browse the repository at this point in the history
  • Loading branch information
d-netto committed Dec 17, 2023
1 parent 67c7843 commit 5214188
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 13 deletions.
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
237 changes: 237 additions & 0 deletions src/gc-page-profiler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// 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;

#define GC_SERIALIZER_EMPTY ((const char *)0x1)
#define GC_SERIALIZER_GARBAGE ((const char *)0x2)

gc_page_profiler_serializer_t gc_page_serializer_create(void) JL_NOTSAFEPOINT
{
gc_page_profiler_serializer_t serializer;
serializer.length = 0;
serializer.capacity = GC_PAGE_SZ;
if (__unlikely(page_profile_enabled)) {
serializer.buffer = (char const **)calloc_s(serializer.capacity);
}
else {
serializer.buffer = NULL;
}
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)) {
memset(serializer->buffer, 0, serializer->capacity);
serializer->length = 0;
serializer->data = (char *)pg->data;
serializer->osize = pg->osize;
}
}

void gc_page_serializer_destroy(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT
{
free(serializer->buffer);
}

void gc_page_serializer_write(gc_page_profiler_serializer_t *serializer,
const char *str) JL_NOTSAFEPOINT
{
serializer->buffer[serializer->length++] = 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;
}

void gc_page_profile_write_empty_page(gc_page_profiler_serializer_t *serializer)
JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
gc_page_serializer_write(serializer, GC_SERIALIZER_EMPTY);
}
}

void gc_page_profile_write_garbage(gc_page_profiler_serializer_t *serializer)
JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
gc_page_serializer_write(serializer, GC_SERIALIZER_GARBAGE);
}
}

void gc_page_profile_write_live_obj(gc_page_profiler_serializer_t *serializer,
jl_taggedvalue_t *v) JL_NOTSAFEPOINT
{
if (__unlikely(page_profile_enabled)) {
const char *name = jl_typeof_str(jl_valueof(v));
gc_page_serializer_write(serializer, name);
}
}

// inspired by the function from `src/gc-heap-snapshot.cpp`
void gc_page_profile_print_escape_json(ios_t *stream, const char *s,
size_t len) JL_NOTSAFEPOINT
{
{
for (size_t i = 0; i < len; i++) {
const char *c = &s[i];
switch (*c) {
case '\\': ios_write(stream, "\\\\", 2); break;
case '\b': ios_write(stream, "\\b", 2); break;
case '\f': ios_write(stream, "\\f", 2); break;
case '\n': ios_write(stream, "\\n", 2); break;
case '\r': ios_write(stream, "\\r", 2); break;
case '\t': ios_write(stream, "\\t", 2); break;
default:
if (('\x00' <= *c) & (*c <= '\x1f')) {
ios_printf(stream, "\\u%04x", (int)*c);
}
else {
ios_putc(*c, stream);
}
}
}
}
}

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];
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "{");
gc_page_profile_print_escape_json(page_profile_stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "\"address\": \"%p\",", serializer->data);
gc_page_profile_print_escape_json(page_profile_stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "\"object_size\": %d,", serializer->osize);
gc_page_profile_print_escape_json(page_profile_stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "\"objects\": [");
gc_page_profile_print_escape_json(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)) {
char str[GC_TYPE_STR_MAXLEN];
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "]");
gc_page_profile_print_escape_json(page_profile_stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "}");
gc_page_profile_print_escape_json(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)) {
if (page_profile_pages_written > 0) {
char str[GC_TYPE_STR_MAXLEN];
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, ",");
gc_page_profile_print_escape_json(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->length; i++) {
memset(str, 0, GC_TYPE_STR_MAXLEN);
if (serializer->buffer[i] == GC_SERIALIZER_EMPTY) {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"empty\",");
}
else if (serializer->buffer[i] == GC_SERIALIZER_GARBAGE) {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"garbage\",");
}
else {
snprintf(str, GC_TYPE_STR_MAXLEN, "\"%s\",", serializer->buffer[i]);
}
if (i == serializer->length - 1) {
str[strlen(str) - 1] = '\0';
}
gc_page_profile_print_escape_json(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);
char str[GC_TYPE_STR_MAXLEN];
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "{");
gc_page_profile_print_escape_json(stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "\"pages\": [");
gc_page_profile_print_escape_json(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);
char str[GC_TYPE_STR_MAXLEN];
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "]");
gc_page_profile_print_escape_json(stream, str, strlen(str));
memset(str, 0, GC_TYPE_STR_MAXLEN);
snprintf(str, GC_TYPE_STR_MAXLEN, "}");
gc_page_profile_print_escape_json(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_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
44 changes: 44 additions & 0 deletions src/gc-page-profiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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 {
size_t length;
size_t capacity;
char const **buffer;
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
void gc_page_profile_write_preamble(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
void gc_page_profile_write_epilogue(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
void gc_page_profile_write_empty_page(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
void gc_page_profile_write_garbage(gc_page_profiler_serializer_t *serializer) JL_NOTSAFEPOINT;
void gc_page_profile_write_live_obj(gc_page_profiler_serializer_t *serializer, jl_taggedvalue_t *v) JL_NOTSAFEPOINT;
void gc_enable_page_profile(void) JL_NOTSAFEPOINT;
void gc_disable_page_profile(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

0 comments on commit 5214188

Please sign in to comment.