[NativeAOT] Migrate GC bridge logging to printf#12211
Conversation
Convert the remaining easy formatted GC bridge diagnostics reachable by NativeAOT to the canonical shared printf logging APIs while preserving gating, no-check behavior, formatting, and null handling. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4aed776f-8743-45ba-b96b-079253595056
There was a problem hiding this comment.
Pull request overview
This PR continues the NativeAOT/CoreCLR native-host logging migration by converting the remaining GC-bridge formatted log sites in src/native/clr/host/gc-bridge.cc from std::format-style logging to the shared printf-style logging helpers introduced earlier in the series.
Changes:
- Replace
log_info(...)/log_info_nocheck_fmt(...)formatted calls withlog_infof(...)andlog_writef(...). - Preserve existing GC-spew/category gating (
Logger::gc_spew_enabled()andUtil::should_log()), including the explicit “no-check” write after the gate. - Use
PRIxPTR+uintptr_tfor handle-width hex formatting and add<cinttypes>for the format macro.
…gging-next' into jonathanpeppers-finish-gc-bridge-printf
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
✅ LGTM — clean, behavior-preserving migration
Reviewed the migration of the five remaining NativeAOT-reachable formatted logging calls in src/native/clr/host/gc-bridge.cc from the std::format-style APIs to the printf-style shared APIs.
Correctness verified against the API definitions:
| Old call | New call | Gating behavior |
|---|---|---|
log_info (macro → DO_LOG_FMT) |
log_infof |
Both check log_categories & category before writing — ✅ equivalent |
log_info_nocheck_fmt |
log_writef (..., LogLevel::Info, ...) |
Both write unconditionally — ✅ equivalent, and correctly placed after the explicit Util::should_log (LOG_GC) gate |
Format specifier mapping is correct:
{}onsize_t→%zu✅{:#x}on handle →0x%" PRIxPTR "with<cinttypes>added ✅ (and improved by usinguintptr_t— see inline){}onchar*→%swithoptional_string()guard ✅ (a real nullptr-crash fix — see inline)
Positive callouts:
- The
LOG_GCcategory,gc_spew_enabled()/Util::should_log()gates, message text, and control flow are all preserved exactly as the PR description claims — verified against the full source. <cinttypes>(the C++ header, per repo convention) is the correct include forPRIxPTR.- Two of the changes (
optional_stringguard and signed→unsigned handle) are genuine robustness/correctness improvements over the pre-migration code, not just mechanical swaps.
Notes:
- CI on the
dnceng-publicpipeline was stillpendingat review time (no checks reported yet) — this is not a blocker but should be green before merge, since the change is native code that only the build validates. - As the PR states, the three
std::formatcalls inbridge-processing.ccfeeding_monodroid_gref_logare intentionally out of scope.
No blocking issues. Two 💡 suggestions posted inline highlighting the subtle improvements for future reference.
Generated by Android PR Reviewer for #12211 · 92.3 AIC · ⌖ 18.9 AIC · ⊞ 6.8K
Comment /review to run again
| @@ -161,10 +162,10 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept | |||
| jclass java_class = env->GetObjectClass (handle); | |||
| if (java_class != nullptr) { | |||
| char *class_name = Host::get_java_class_name_for_TypeManager (java_class); | |||
There was a problem hiding this comment.
🤖 💡 Native memory — Good, necessary change: wrapping class_name in optional_string() guards the %s conversion. Host::get_java_class_name_for_TypeManager() genuinely returns nullptr on several paths (null/failed Class.getName, OOM on UTF8 conversion — see host-shared.cc), and passing a raw nullptr to a printf %s is undefined behavior (crash on most C libraries), whereas the old std::format {} on a char* silently formatted it. This migration is more robust than the code it replaces, not just equivalent.
Rule: Guard printf %s against nullptr (repo optional_string convention)
| if (java_class != nullptr) { | ||
| char *class_name = Host::get_java_class_name_for_TypeManager (java_class); | ||
| log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast<intptr_t> (handle), class_name); | ||
| log_infof (LOG_GC, "gref 0x%" PRIxPTR " [%s]", reinterpret_cast<uintptr_t> (handle), optional_string (class_name)); |
There was a problem hiding this comment.
🤖 💡 Native C++ — Nice correctness improvement: switching the handle from intptr_t to uintptr_t here means large 64-bit handle addresses (high bit set) now format as plain 0x... hex. The old std::format {:#x} on a signed intptr_t would render negative values as -0x..., which is misleading for a pointer/gref. The explicit 0x prefix + %" PRIxPTR " correctly mirrors the previous {:#x} output for typical values.
Rule: Use unsigned width-matched types for pointer hex formatting
Dependency
Depends on #12210 and targets its
jonathanpeppers-native-printf-logging-nextbranch.Scope
Migrates the five remaining easy NativeAOT-reachable formatted logging calls in
src/native/clr/host/gc-bridge.ccto the canonical shared printf APIs introduced by #12155.Behavior preservation
LOG_GCcategory, existinggc_spew_enabled()andUtil::should_log()gates, and the no-check write after the explicit gate.%zuforsize_tvalues andPRIxPTRfor handle-width hexadecimal values, including the explicit0xprefix corresponding to{:#x}.optional_string().Non-goals
Does not migrate the three nearby
std::formatcalls inbridge-processing.ccthat feedOSBridge::_monodroid_gref_log; those require a separate API design.Validation
dotnet build src\native\native-nativeaot.csproj -p:AndroidSupportedTargetJitAbis=arm64-v8a -p:AndroidSupportedTargetAotAbis=arm64 -v:minimaldotnet build src\native\native-clr.csproj -p:AndroidSupportedTargetJitAbis=arm64-v8a -p:AndroidSupportedTargetAotAbis=arm64 -v:minimalgit diff --check