Skip to content

Commit

Permalink
Lazily initialize Profile buffer (#46239)
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth authored Aug 6, 2022
1 parent b0ec43c commit 72473ae
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 52 deletions.
92 changes: 46 additions & 46 deletions src/signal-handling.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,52 @@ static const uint64_t GIGA = 1000000000ULL;
JL_DLLEXPORT void jl_profile_stop_timer(void);
JL_DLLEXPORT int jl_profile_start_timer(void);

///////////////////////
// Utility functions //
///////////////////////
JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec)
{
bt_size_max = maxsize;
nsecprof = delay_nsec;
if (bt_data_prof != NULL)
free((void*)bt_data_prof);
bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t));
if (bt_data_prof == NULL && maxsize > 0)
return -1;
bt_size_cur = 0;
return 0;
}

JL_DLLEXPORT uint8_t *jl_profile_get_data(void)
{
return (uint8_t*) bt_data_prof;
}

JL_DLLEXPORT size_t jl_profile_len_data(void)
{
return bt_size_cur;
}

JL_DLLEXPORT size_t jl_profile_maxlen_data(void)
{
return bt_size_max;
}

JL_DLLEXPORT uint64_t jl_profile_delay_nsec(void)
{
return nsecprof;
}

JL_DLLEXPORT void jl_profile_clear_data(void)
{
bt_size_cur = 0;
}

JL_DLLEXPORT int jl_profile_is_running(void)
{
return running;
}

// Any function that acquires this lock must be either a unmanaged thread
// or in the GC safe region and must NOT allocate anything through the GC
// while holding this lock.
Expand Down Expand Up @@ -426,52 +472,6 @@ void jl_critical_error(int sig, bt_context_t *context, jl_task_t *ct)
jl_gc_debug_critical_error();
}

///////////////////////
// Utility functions //
///////////////////////
JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec)
{
bt_size_max = maxsize;
nsecprof = delay_nsec;
if (bt_data_prof != NULL)
free((void*)bt_data_prof);
bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t));
if (bt_data_prof == NULL && maxsize > 0)
return -1;
bt_size_cur = 0;
return 0;
}

JL_DLLEXPORT uint8_t *jl_profile_get_data(void)
{
return (uint8_t*) bt_data_prof;
}

JL_DLLEXPORT size_t jl_profile_len_data(void)
{
return bt_size_cur;
}

JL_DLLEXPORT size_t jl_profile_maxlen_data(void)
{
return bt_size_max;
}

JL_DLLEXPORT uint64_t jl_profile_delay_nsec(void)
{
return nsecprof;
}

JL_DLLEXPORT void jl_profile_clear_data(void)
{
bt_size_cur = 0;
}

JL_DLLEXPORT int jl_profile_is_running(void)
{
return running;
}

#ifdef __cplusplus
}
#endif
8 changes: 8 additions & 0 deletions src/signals-unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,14 @@ void trigger_profile_peek(void)
jl_safe_printf("\n======================================================================================\n");
jl_safe_printf("Information request received. A stacktrace will print followed by a %.1f second profile\n", profile_peek_duration);
jl_safe_printf("======================================================================================\n");
if (bt_size_max == 0){
// If the buffer hasn't been initialized, initialize with default size
// Keep these values synchronized with Profile.default_init()
if (jl_profile_init(10000000 * jl_n_threads, 1000000) == -1){
jl_safe_printf("ERROR: could not initialize the profile buffer");
return;
}
}
bt_size_cur = 0; // clear profile buffer
if (jl_profile_start_timer() < 0)
jl_safe_printf("ERROR: Could not start profile timer\n");
Expand Down
39 changes: 33 additions & 6 deletions stdlib/Profile/src/Profile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ appended to an internal buffer of backtraces.
macro profile(ex)
return quote
try
status = start_timer()
if status < 0
error(error_codes[status])
end
start_timer()
$(esc(ex))
finally
stop_timer()
Expand Down Expand Up @@ -98,6 +95,11 @@ using keywords or in the order `(n, delay)`.
"""
function init(; n::Union{Nothing,Integer} = nothing, delay::Union{Nothing,Real} = nothing, limitwarn::Bool = true)
n_cur = ccall(:jl_profile_maxlen_data, Csize_t, ())
if n_cur == 0 && isnothing(n) && isnothing(delay)
# indicates that the buffer hasn't been initialized at all, so set the default
default_init()
n_cur = ccall(:jl_profile_maxlen_data, Csize_t, ())
end
delay_cur = ccall(:jl_profile_delay_nsec, UInt64, ())/10^9
if n === nothing && delay === nothing
nthreads = Sys.iswindows() ? 1 : Threads.nthreads() # windows only profiles the main thread
Expand Down Expand Up @@ -126,7 +128,7 @@ function init(n::Integer, delay::Real; limitwarn::Bool = true)
end
end

function __init__()
function default_init()
# init with default values
# Use a max size of 10M profile samples, and fire timer every 1ms
# (that should typically give around 100 seconds of record)
Expand All @@ -136,10 +138,25 @@ function __init__()
n = 1_000_000
delay = 0.01
else
# Keep these values synchronized with trigger_profile_peek
n = 10_000_000
delay = 0.001
end
init(n, delay, limitwarn = false)
end

# Checks whether the profile buffer has been initialized. If not, initializes it with the default size.
function check_init()
buffer_size = @ccall jl_profile_maxlen_data()::Int
if buffer_size == 0
default_init()
end
end

function __init__()
# Note: The profile buffer is no longer initialized during __init__ because Profile is in the sysimage,
# thus __init__ is called every startup. The buffer is lazily initialized the first time `@profile` is
# used, if not manually initialized before that.
@static if !Sys.iswindows()
# triggering a profile via signals is not implemented on windows
PROFILE_PRINT_COND[] = Base.AsyncCondition()
Expand Down Expand Up @@ -567,7 +584,14 @@ Julia, and examine the resulting `*.mem` files.
clear_malloc_data() = ccall(:jl_clear_malloc_data, Cvoid, ())

# C wrappers
start_timer() = ccall(:jl_profile_start_timer, Cint, ())
function start_timer()
check_init() # if the profile buffer hasn't been initialized, initialize with default size
status = ccall(:jl_profile_start_timer, Cint, ())
if status < 0
error(error_codes[status])
end
end


stop_timer() = ccall(:jl_profile_stop_timer, Cvoid, ())

Expand Down Expand Up @@ -599,6 +623,9 @@ By default metadata such as threadid and taskid is included. Set `include_meta`
"""
function fetch(;include_meta = true, limitwarn = true)
maxlen = maxlen_data()
if maxlen == 0
error("The profiling data buffer is not initialized. A profile has not been requested this session.")
end
len = len_data()
if limitwarn && is_buffer_full()
@warn """The profile data buffer is full; profiling probably terminated
Expand Down
2 changes: 2 additions & 0 deletions stdlib/Profile/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Test, Profile, Serialization, Logging
using Base.StackTraces: StackFrame

@test_throws "The profiling data buffer is not initialized. A profile has not been requested this session." Profile.print()

Profile.clear()
Profile.init()

Expand Down

2 comments on commit 72473ae

@maleadt
Copy link
Member

@maleadt maleadt commented on 72473ae Aug 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily package evaluation, I will reply here when finished:

@nanosoldier runtests(ALL, isdaily = true)

@maleadt
Copy link
Member

@maleadt maleadt commented on 72473ae Aug 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your package evaluation job has completed - possible new issues were detected. A full report can be found here.

Please sign in to comment.