Skip to content
111 changes: 74 additions & 37 deletions src/native/clr/host/os-bridge.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <ranges>
#include <cstdarg>
#include <cstdlib>

#include <host/os-bridge.hh>
#include <host/runtime-util.hh>
Expand Down Expand Up @@ -94,34 +95,40 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori
return;
}

const std::string_view trace { from };
std::string_view trace { from };
if (trace.empty ()) [[unlikely]] {
log_warn (category, "Empty stack trace passed by the managed runtime.");
return;
}

for (const auto segment : std::views::split (trace, '\n')) {
const std::string_view line { segment };
while (true) {
size_t line_end = trace.find ('\n');
size_t line_length = line_end == std::string_view::npos ? trace.length () : line_end;
std::string_view line { trace.data (), line_length };

if ((category == LOG_GREF && Logger::gref_to_logcat ()) ||
(category == LOG_LREF && Logger::lref_to_logcat ())) {
log_debug (category, "{}"sv, line);
log_debugf (category, "%.*s", static_cast<int>(line.length ()), line.data ());
}

if (to == nullptr) {
continue;
if (to != nullptr) {
fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to);
fputc ('\n', to);
fflush (to);
}

fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to);
fputc ('\n', to);
fflush (to);
if (line_end == std::string_view::npos) {
break;
}

trace.remove_prefix (line_end + 1);
}
}

void OSBridge::_monodroid_gref_log (const char *message) noexcept
{
if (Logger::gref_to_logcat ()) {
log_debug (LOG_GREF, "{}"sv, optional_string (message));
log_debugf (LOG_GREF, "%s", optional_string (message));
}

if (Logger::gref_log () == nullptr) {
Expand All @@ -133,7 +140,7 @@ void OSBridge::_monodroid_gref_log (const char *message) noexcept
}

[[gnu::always_inline, gnu::flatten]]
void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept
void OSBridge::log_it (LogCategories category, std::string_view const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept
{
log_write (category, LogLevel::Info, line);

Expand All @@ -146,13 +153,31 @@ void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to
return;
}

fwrite (line.c_str (), sizeof (std::string::value_type), line.length (), to);
fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to);
fputc ('\n', to);

_write_stack_trace (to, from, category);
fflush (to);
}

void OSBridge::log_itf (LogCategories category, FILE *to, const char *const from, bool logcat_enabled, const char *format, ...) noexcept
{
const char *safe_format = format == nullptr ? "<null>" : format;
char *line = nullptr;
va_list args;
va_start (args, format);
int length = vasprintf (&line, safe_format, args);
va_end (args);

if (length < 0) [[unlikely]] {
log_it (category, safe_format, to, from, logcat_enabled);
return;
}

log_it (category, std::string_view { line, static_cast<size_t>(length) }, to, from, logcat_enabled);
std::free (line);
}

auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int
{
int c = _monodroid_gref_inc ();
Expand All @@ -161,8 +186,12 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject
}

int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED);
const std::string log_line = std::format (
"+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_GREF,
Logger::gref_log (),
from,
Logger::gref_to_logcat (),
"+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)",
c,
wc,
reinterpret_cast<void*>(curHandle),
Expand All @@ -172,8 +201,6 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject
optional_string (threadName),
threadId
);

log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
return c;
}

Expand All @@ -185,17 +212,19 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char
}

int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED);
const std::string log_line = std::format (
"-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_GREF,
Logger::gref_log (),
from,
Logger::gref_to_logcat (),
"-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)",
c,
wc,
reinterpret_cast<void*>(handle),
type,
optional_string (threadName),
threadId
);

log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}

void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from)
Expand All @@ -206,8 +235,12 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec
}

int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED);
const std::string log_line = std::format (
"+w+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_GREF,
Logger::gref_log (),
from,
Logger::gref_to_logcat (),
"+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)",
gc,
c,
reinterpret_cast<void*>(curHandle),
Expand All @@ -217,8 +250,6 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec
optional_string (threadName),
threadId
);

log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}

void
Expand All @@ -228,16 +259,18 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c
return;
}

const std::string log_line = std::format (
"+l+ lrefc {} handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_LREF,
Logger::lref_log (),
from,
Logger::lref_to_logcat (),
"+l+ lrefc %d handle %p/%c from thread '%s'(%d)",
lrefc,
reinterpret_cast<void*>(handle),
type,
optional_string (threadName),
threadId
);

log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ());
}

void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *threadName, int threadId, const char *from)
Expand All @@ -248,17 +281,19 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha
}

int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED);
const std::string log_line = std::format (
"-w- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_GREF,
Logger::gref_log (),
from,
Logger::gref_to_logcat (),
"-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)",
gc,
c,
reinterpret_cast<void*>(handle),
type,
optional_string (threadName),
threadId
);

log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}

void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, const char *threadName, int threadId, const char *from)
Expand All @@ -267,14 +302,16 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type,
return;
}

const std::string log_line = std::format (
"-l- lrefc {} handle {:p}/{} from thread '{}'({})"sv,
log_itf (
LOG_LREF,
Logger::lref_log (),
from,
Logger::lref_to_logcat (),
"-l- lrefc %d handle %p/%c from thread '%s'(%d)",
lrefc,
reinterpret_cast<void*>(handle),
type,
optional_string (threadName),
threadId
);

log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ());
}
5 changes: 4 additions & 1 deletion src/native/clr/include/host/os-bridge.hh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <cstdarg>
#include <cstdio>
#include <string_view>

#include <jni.h>

Expand Down Expand Up @@ -57,7 +59,8 @@ namespace xamarin::android {

private:
static void _write_stack_trace (FILE *to, const char *const from, LogCategories = LOG_NONE) noexcept;
static void log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept;
static void log_it (LogCategories category, std::string_view const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept;
static void log_itf (LogCategories category, FILE *to, const char *const from, bool logcat_enabled, const char *format, ...) noexcept __attribute__ ((format (printf, 5, 6)));

private:
static inline JavaVM *jvm = nullptr;
Expand Down
7 changes: 7 additions & 0 deletions src/native/clr/include/shared/log_types.hh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cstdarg>
#include <cstdint>
#include <format>
#include <string>
Expand Down Expand Up @@ -47,6 +48,12 @@ namespace xamarin::android {
// A slightly faster alternative to other log functions as it doesn't parse the message
// for format placeholders nor it uses variable arguments
void log_write (LogCategories category, LogLevel level, const char *message) noexcept;
void log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept;
void log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4)));
void log_debugf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
void log_infof (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
void log_warnf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
void log_errorf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));

[[gnu::always_inline]]
static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept
Expand Down
21 changes: 18 additions & 3 deletions src/native/clr/shared/helpers.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <android/set_abort_message.h>

Expand All @@ -7,11 +8,24 @@

using namespace xamarin::android;

[[noreturn]] void
Helpers::abort_applicationf (LogCategories category, std::source_location sloc, const char *format, ...) noexcept
{
char *message = nullptr;
const char *safe_format = format == nullptr ? "<null>" : format;
va_list args;
va_start (args, format);
int ret = vasprintf (&message, safe_format, args);
va_end (args);

abort_application (category, ret < 0 ? safe_format : message, true, sloc);
}

[[noreturn]] void
Helpers::abort_application (LogCategories category, const char *message, bool log_location, std::source_location sloc) noexcept
{
// Log it, but also...
log_fatal (category, "{}", message);
log_write (category, LogLevel::Fatal, message);

// ...let android include it in the tombstone, debuggerd output, stack trace etc
android_set_abort_message (message);
Expand All @@ -33,9 +47,10 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo
}
}

log_fatal (
log_writef (
category,
"Abort at {}:{}:{} ('{}')",
LogLevel::Fatal,
"Abort at %s:%u:%u ('%s')",
file_name,
sloc.line (),
sloc.column (),
Expand Down
Loading
Loading