Skip to content

Commit 4859e9c

Browse files
d-nettoli1Adnan Alhomssikpamnany
authored
--safe-crash-log-file flag (#140)
* --safe-crash-log-file flag * Update init.c * json escape jl_safe_printf when safe crash log file * add timestamp to json logs * port it to aarch64 darwin * fix minor warning * missing double quote * Suggestion from code review: make sig_stack_size a const in signals-unix.c Co-authored-by: Kiran Pamnany <[email protected]> * Suggestion from code review: make sig_stack size a const in signals-win.c Co-authored-by: Kiran Pamnany <[email protected]> * more suggestions from Kiran's review * more suggestions from review --------- Co-authored-by: Malte Sandstede <[email protected]> Co-authored-by: Adnan Alhomssi <[email protected]> Co-authored-by: Kiran Pamnany <[email protected]>
1 parent 31f3d1d commit 4859e9c

File tree

9 files changed

+104
-10
lines changed

9 files changed

+104
-10
lines changed

base/options.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ struct JLOptions
5757
strip_ir::Int8
5858
permalloc_pkgimg::Int8
5959
heap_size_hint::UInt64
60+
safe_crash_log_file::Ptr{UInt8}
6061
end
6162

6263
# This runs early in the sysimage != is not defined yet

src/init.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,15 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)
823823
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
824824
jl_install_default_signal_handlers();
825825

826+
#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_))
827+
if (jl_options.safe_crash_log_file != NULL) {
828+
jl_sig_fd = open(jl_options.safe_crash_log_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
829+
if (jl_sig_fd == -1) {
830+
jl_error("fatal error: could not open safe crash log file for writing");
831+
}
832+
}
833+
#endif
834+
826835
jl_gc_init();
827836

828837
arraylist_new(&jl_linkage_blobs, 0);

src/jl_uv.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "errno.h"
1616
#include <unistd.h>
1717
#include <sys/socket.h>
18+
#include <sys/time.h>
1819
#endif
1920

2021
#include "julia.h"
@@ -677,6 +678,56 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...)
677678
return c;
678679
}
679680

681+
STATIC_INLINE void print_error_msg_as_json(char *buf) JL_NOTSAFEPOINT
682+
{
683+
// Our telemetry on SPCS expects a JSON object per line
684+
// The following lines prepare the timestamp string and the JSON object
685+
struct timeval tv;
686+
struct tm* tm_info;
687+
char timestamp_buffer[50];
688+
// Get current time
689+
gettimeofday(&tv, NULL);
690+
tm_info = gmtime(&tv.tv_sec);
691+
// Format time
692+
int offset = strftime(timestamp_buffer, 25, "%Y-%m-%dT%H:%M:%S", tm_info);
693+
// Append milliseconds
694+
snprintf(timestamp_buffer + offset, 25, ".%03d", tv.tv_usec / 1000);
695+
const char *json_preamble_p1 = "\n{\"level\":\"Error\", \"timestamp\":\"";
696+
const char *json_preamble_p2 = "\", \"message\": \"";
697+
const char *json_postamble = "\"}\n";
698+
// Ignore write failures because there is nothing we can do
699+
write(jl_sig_fd, json_preamble_p1, strlen(json_preamble_p1));
700+
write(jl_sig_fd, timestamp_buffer, strlen(timestamp_buffer));
701+
write(jl_sig_fd, json_preamble_p2, strlen(json_preamble_p2));
702+
// JSON escape the input string
703+
for(size_t i = 0; i < strlen(buf); i += 1) {
704+
switch (buf[i]) {
705+
case '"':
706+
write(jl_sig_fd, "\\\"", 2);
707+
break;
708+
case '\b':
709+
write(jl_sig_fd, "\\b", 2);
710+
break;
711+
case '\n':
712+
write(jl_sig_fd, "\\n", 2);
713+
break;
714+
case '\r':
715+
write(jl_sig_fd, "\\r", 2);
716+
break;
717+
case '\t':
718+
write(jl_sig_fd, "\\t", 2);
719+
break;
720+
case '\\':
721+
write(jl_sig_fd, "\\\\", 2);
722+
break;
723+
default:
724+
write(jl_sig_fd, buf + i, 1);
725+
}
726+
}
727+
write(jl_sig_fd, json_postamble, strlen(json_postamble));
728+
fdatasync(jl_sig_fd);
729+
}
730+
680731
JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
681732
{
682733
static char buf[1000];
@@ -693,6 +744,9 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
693744
va_end(args);
694745

695746
buf[999] = '\0';
747+
if (jl_inside_signal_handler() && jl_sig_fd != 0) {
748+
print_error_msg_as_json(buf);
749+
}
696750
if (write(STDERR_FILENO, buf, strlen(buf)) < 0) {
697751
// nothing we can do; ignore the failure
698752
}

src/jloptions.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ JL_DLLEXPORT void jl_init_options(void)
9090
0, // strip-ir
9191
0, // permalloc_pkgimg
9292
0, // heap-size-hint
93+
NULL, // safe_crash_log_file
9394
};
9495
jl_options_initialized = 1;
9596
}
@@ -258,7 +259,8 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
258259
opt_strip_ir,
259260
opt_heap_size_hint,
260261
opt_gc_threads,
261-
opt_permalloc_pkgimg
262+
opt_permalloc_pkgimg,
263+
opt_safe_crash_log_file,
262264
};
263265
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:";
264266
static const struct option longopts[] = {
@@ -320,6 +322,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
320322
{ "strip-ir", no_argument, 0, opt_strip_ir },
321323
{ "permalloc-pkgimg",required_argument, 0, opt_permalloc_pkgimg },
322324
{ "heap-size-hint", required_argument, 0, opt_heap_size_hint },
325+
{ "safe-crash-log-file", required_argument, 0, opt_safe_crash_log_file },
323326
{ 0, 0, 0, 0 }
324327
};
325328

@@ -850,6 +853,11 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
850853
else
851854
jl_errorf("julia: invalid argument to --permalloc-pkgimg={yes|no} (%s)", optarg);
852855
break;
856+
case opt_safe_crash_log_file:
857+
jl_options.safe_crash_log_file = strdup(optarg);
858+
if (jl_options.safe_crash_log_file == NULL)
859+
jl_error("julia: failed to allocate memory for --safe-crash-log-file");
860+
break;
853861
default:
854862
jl_errorf("julia: unhandled option -- %c\n"
855863
"This is a bug, please report it.", c);

src/jloptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ typedef struct {
6161
int8_t strip_ir;
6262
int8_t permalloc_pkgimg;
6363
uint64_t heap_size_hint;
64+
const char *safe_crash_log_file;
6465
} jl_options_t;
6566

6667
#endif

src/julia_internal.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,32 @@ JL_CALLABLE(jl_f_opaque_closure_call);
702702
void jl_install_default_signal_handlers(void);
703703
void restore_signals(void);
704704
void jl_install_thread_signal_handler(jl_ptls_t ptls);
705+
extern const size_t sig_stack_size;
706+
STATIC_INLINE int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr)
707+
{
708+
// One guard page for signal_stack.
709+
return !((char*)ptr < (char*)ptls->signal_stack - jl_page_size ||
710+
(char*)ptr > (char*)ptls->signal_stack + sig_stack_size);
711+
}
712+
STATIC_INLINE int jl_inside_signal_handler(void)
713+
{
714+
#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_))
715+
// Read the stack pointer
716+
size_t sp;
717+
#if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
718+
__asm__ __volatile__("movq %%rsp, %0" : "=r"(sp));
719+
#elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)
720+
__asm__ __volatile__("mov %0, sp" : "=r"(sp));
721+
#endif
722+
// Check if the stack pointer is within the signal stack
723+
jl_ptls_t ptls = jl_current_task->ptls;
724+
return is_addr_on_sigstack(ptls, (void*)sp);
725+
#else
726+
return 0;
727+
#endif
728+
}
729+
// File-descriptor for safe logging on signal handling
730+
extern int jl_sig_fd;
705731

706732
JL_DLLEXPORT jl_fptr_args_t jl_get_builtin_fptr(jl_value_t *b);
707733

src/signal-handling.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ static const uint64_t GIGA = 1000000000ULL;
2828
// Timers to take samples at intervals
2929
JL_DLLEXPORT void jl_profile_stop_timer(void);
3030
JL_DLLEXPORT int jl_profile_start_timer(void);
31+
// File-descriptor for safe logging on signal handling
32+
int jl_sig_fd;
3133

3234
///////////////////////
3335
// Utility functions //

src/signals-unix.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
// 8M signal stack, same as default stack size and enough
3939
// for reasonable finalizers.
4040
// Should also be enough for parallel GC when we have it =)
41-
#define sig_stack_size (8 * 1024 * 1024)
41+
const size_t sig_stack_size = (8 * 1024 * 1024);
4242

4343
#include "julia_assert.h"
4444

@@ -91,13 +91,6 @@ static inline __attribute__((unused)) uintptr_t jl_get_rsp_from_ctx(const void *
9191
#endif
9292
}
9393

94-
static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr)
95-
{
96-
// One guard page for signal_stack.
97-
return !((char*)ptr < (char*)ptls->signal_stack - jl_page_size ||
98-
(char*)ptr > (char*)ptls->signal_stack + sig_stack_size);
99-
}
100-
10194
// Modify signal context `_ctx` so that `fptr` will execute when the signal
10295
// returns. `fptr` will execute on the signal stack, and must not return.
10396
// jl_call_in_ctx is also currently executing on that signal stack,

src/signals-win.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// Note that this file is `#include`d by "signal-handling.c"
55
#include <mmsystem.h> // hidden by LEAN_AND_MEAN
66

7-
#define sig_stack_size 131072 // 128k reserved for SEGV handling
7+
const size_t sig_stack_size = 131072; // 128k reserved for SEGV handling
88

99
// Copied from MINGW_FLOAT_H which may not be found due to a collision with the builtin gcc float.h
1010
// eventually we can probably integrate this into OpenLibm.

0 commit comments

Comments
 (0)