[fault-injection] Profiler::recordSample() stack walk is unprotected#680
[fault-injection] Profiler::recordSample() stack walk is unprotected#680zhengyu123 wants to merge 45 commits into
Conversation
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…iler into zgu/unprotected_vmflag
There was a problem hiding this comment.
Pull request overview
This PR hardens the native profiler’s initialization and stack-walking paths against startup-ordering issues (Agent_OnLoad) and fault/crash scenarios during sampling, and adds a regression test to prevent reintroducing the initialization regression.
Changes:
- Add setjmp/longjmp crash protection around
Profiler::recordSample()’s stack unwind to avoid unrecoverable faults during metadata reads. - Ensure HotSpot
VMStructsinitialization happens early enough for the-agentpath:(Agent_OnLoad) path by callingVM::ready()fromVM::initProfilerBridge(), and add debug test logging for the decision points. - Add a Java regression test that launches a child JVM with
-agentpath:and asserts theDebugNonSafepointsflag lookup path is taken (not the CompiledMethodLoad workaround).
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java | Adds regression test covering Agent_OnLoad initialization ordering and VM flag detection. |
| ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java | Exposes a package-visible library-path resolver for tests to supply a real -agentpath: without loading in-process. |
| ddprof-lib/src/main/cpp/vmEntry.h | Reworks _libjvm handling to a CodeCache* with an accessor using acquire semantics. |
| ddprof-lib/src/main/cpp/vmEntry.cpp | Makes VM::ready() idempotently initialize VMStructs once; calls ready() from initProfilerBridge() for Agent_OnLoad; adds test logs. |
| ddprof-lib/src/main/cpp/profiler.cpp | Wraps native+Java stack walking in recordSample() with crash protection and records an error frame on recovery; switches debug-symbol query to VM::libjvm(). |
| ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h | Adds safe offset-load helpers and uses SafeAccess for VMFlag name reads; introduces fault-injection point for offset access. |
| ddprof-lib/src/main/cpp/hotspot/vmStructs.h | Moves offset accessors to inline definitions and adds a safe-load template; updates VMFlag::name() signature. |
| ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp | Avoids repeated unsafe dereferences by caching VMFlag::name() into a local before strcmp. |
| ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp | Adds a null-check for scope.method() and renames the recovered-longjmp counter to be stackwalk-generic. |
| ddprof-lib/src/main/cpp/flightRecorder.cpp | Switches debug-symbol setting emission to use VM::libjvm(). |
| ddprof-lib/src/main/cpp/counters.h | Renames WALKVM_LONGJMP_RECOVERED to STACKWALK_LONGJMP_RECOVERED. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI Test ResultsRun: #30093470859 | Commit:
Status Overview
Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled Failed Testsglibc-aarch64/debug / 8-j9Job: View logs No detailed failure information available. Check the job logs. glibc-aarch64/debug / 11-j9Job: View logs No detailed failure information available. Check the job logs. glibc-aarch64/debug / 17-j9Job: View logs No detailed failure information available. Check the job logs. glibc-amd64/debug / 8-j9Job: View logs No detailed failure information available. Check the job logs. glibc-amd64/debug / 11-j9Job: View logs No detailed failure information available. Check the job logs. glibc-amd64/debug / 8-ibmJob: View logs No detailed failure information available. Check the job logs. glibc-amd64/debug / 17-j9Job: View logs No detailed failure information available. Check the job logs. Summary: Total: 32 | Passed: 25 | Failed: 7 Updated: 2026-07-24 13:04:32 UTC |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Benchmark Results (commit a66e913)Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126327661 Commit:
|
| Benchmark | JDK | Latest | Dev | Δ (dev vs latest) | Issues L/D |
|---|---|---|---|---|---|
| akka-uct | 21 | ✅ 10146 ms (21 iters) | ✅ 10256 ms (21 iters) | ≈ +1.1% (±11.4%) | — / — |
| akka-uct | 25 | ✅ 8891 ms (24 iters) | ✅ 8863 ms (24 iters) | ≈ -0.3% (±11.1%) | — / — |
| finagle-chirper | 21 | ✅ 5943 ms (33 iters) | ✅ 5983 ms (33 iters) | ≈ +0.7% (±25.5%) | |
| finagle-chirper | 25 | ✅ 5507 ms (36 iters) | ✅ 5417 ms (36 iters) | ≈ -1.6% (±24.3%) | |
| fj-kmeans | 21 | ✅ 2773 ms (68 iters) | ✅ 2772 ms (67 iters) | ≈ -0% (±2.7%) | — / — |
| fj-kmeans | 25 | ✅ 2823 ms (66 iters) | ✅ 2779 ms (67 iters) | ≈ -1.6% (±2.7%) | — / — |
| future-genetic | 21 | ✅ 2128 ms (87 iters) | ✅ 2093 ms (88 iters) | ≈ -1.6% (±2.7%) | — / — |
| future-genetic | 25 | ✅ 2040 ms (90 iters) | ✅ 2138 ms (87 iters) | 🔴 +4.8% | — / — |
| naive-bayes | 21 | ✅ 1323 ms (130 iters) | ✅ 1267 ms (135 iters) | ≈ -4.2% (±31.9%) | — / — |
| naive-bayes | 25 | ✅ 1017 ms (168 iters) | ✅ 1015 ms (168 iters) | ≈ -0.2% (±31.6%) | — / — |
| reactors | 21 | ✅ 16518 ms (15 iters) | ✅ 16995 ms (15 iters) | ≈ +2.9% (±6.4%) | — / — |
| reactors | 25 | ✅ 18203 ms (15 iters) | ✅ 18574 ms (15 iters) | ≈ +2% (±5.3%) | — / — |
Internal counter details (ddprof)
ddprof internal counters, latest / dev (✅ = 0, · = unavailable):
| Benchmark | JDK | Dropped rec | Dropped jvmti | Dropped trace | Skipped WC | AGCT fail | Unwind fail |
|---|---|---|---|---|---|---|---|
| akka-uct | 21 | ✅ / ✅ | ✅ / ✅ | 4 / 3 | 1921 / 1981 | ✅ / ✅ | ✅ / ✅ |
| akka-uct | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 1 | 2429 / 2360 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 21 | ✅ / ✅ | ✅ / ✅ | 2 / 4 | 8337 / 8708 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 25 | ✅ / ✅ | ✅ / ✅ | ✅ / 2 | 8615 / 8139 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 21 | ✅ / ✅ | ✅ / ✅ | 4 / 1 | 1264 / 1262 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 25 | ✅ / ✅ | ✅ / ✅ | 1 / ✅ | 1284 / 1266 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 21 | ✅ / ✅ | ✅ / ✅ | 2 / ✅ | 2972 / 2957 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 3 | 2870 / 2948 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 21 | ✅ / ✅ | ✅ / ✅ | 3 / 2 | 3511 / 3493 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 25 | ✅ / ✅ | ✅ / ✅ | 5 / 3 | 3481 / 3475 | ✅ / ✅ | ✅ / ✅ |
| reactors | 21 | ✅ / ✅ | ✅ / ✅ | 1 / ✅ | 1791 / 1687 | ✅ / ✅ | ✅ / ✅ |
| reactors | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 1 | 1829 / 1904 | ✅ / ✅ | ✅ / ✅ |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:337
- The section header comment still refers to
HotspotSupport::checkFault(), but the test now exercisesProfiler::checkFault(). Updating the comment avoids confusion when reading the test output and history.
TEST(CheckFaultGuardTest, NullThreadIsNoop) {
Profiler::checkFault(nullptr); // must not crash
Benchmark Results (commit 86238c0)Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126639928 Commit:
|
| Benchmark | JDK | Latest | Dev | Δ (dev vs latest) | Issues L/D |
|---|---|---|---|---|---|
| akka-uct | 21 | ✅ 10301 ms (21 iters) | ✅ 10332 ms (21 iters) | ≈ +0.3% (±11.6%) | — / — |
| akka-uct | 25 | ✅ 8814 ms (24 iters) | ✅ 8767 ms (24 iters) | ≈ -0.5% (±10.1%) | — / — |
| finagle-chirper | 21 | ✅ 5965 ms (33 iters) | ✅ 5977 ms (33 iters) | ≈ +0.2% (±25.4%) | |
| finagle-chirper | 25 | ✅ 5497 ms (36 iters) | ✅ 5398 ms (36 iters) | ≈ -1.8% (±24%) | |
| fj-kmeans | 21 | ✅ 2650 ms (71 iters) | ✅ 2763 ms (68 iters) | 🔴 +4.3% | — / — |
| fj-kmeans | 25 | ✅ 2827 ms (66 iters) | ✅ 2812 ms (66 iters) | ≈ -0.5% (±2.6%) | — / — |
| future-genetic | 21 | ✅ 2048 ms (90 iters) | ✅ 2071 ms (89 iters) | ≈ +1.1% (±2.6%) | — / — |
| future-genetic | 25 | ✅ 2098 ms (89 iters) | ✅ 2128 ms (87 iters) | ≈ +1.4% (±2.5%) | — / — |
| naive-bayes | 21 | ✅ 1266 ms (135 iters) | ✅ 1237 ms (139 iters) | ≈ -2.3% (±31.9%) | — / — |
| naive-bayes | 25 | ✅ 1018 ms (168 iters) | ✅ 1008 ms (170 iters) | ≈ -1% (±31.3%) | — / — |
| reactors | 21 | ✅ 16486 ms (15 iters) | ✅ 16402 ms (15 iters) | ≈ -0.5% (±7.8%) | — / — |
| reactors | 25 | ✅ 18983 ms (15 iters) | ✅ 18298 ms (15 iters) | ≈ -3.6% (±4.1%) | — / — |
Internal counter details (ddprof)
ddprof internal counters, latest / dev (✅ = 0, · = unavailable):
| Benchmark | JDK | Dropped rec | Dropped jvmti | Dropped trace | Skipped WC | AGCT fail | Unwind fail |
|---|---|---|---|---|---|---|---|
| akka-uct | 21 | ✅ / ✅ | ✅ / ✅ | 2 / 2 | 2062 / 1958 | ✅ / ✅ | ✅ / ✅ |
| akka-uct | 25 | ✅ / ✅ | ✅ / ✅ | 1 / 2 | 2047 / 2212 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 21 | ✅ / ✅ | ✅ / ✅ | 4 / 2 | 7979 / 8465 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 25 | ✅ / ✅ | ✅ / ✅ | 2 / ✅ | 8174 / 8614 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 21 | ✅ / ✅ | ✅ / ✅ | 4 / 3 | 1292 / 1267 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 1 | 1254 / 1267 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / ✅ | 2964 / 2905 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 25 | ✅ / ✅ | ✅ / ✅ | 3 / 4 | 2947 / 2881 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 21 | ✅ / ✅ | ✅ / ✅ | 4 / ✅ | 3535 / 3561 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 1 | 3499 / 3501 | ✅ / ✅ | ✅ / ✅ |
| reactors | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / 3 | 1704 / 1657 | ✅ / ✅ | ✅ / ✅ |
| reactors | 25 | ✅ / ✅ | ✅ / ✅ | ✅ / 3 | 1993 / 1778 | ✅ / ✅ | ✅ / ✅ |
Benchmark Results (commit 490b055)Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126673593 Commit:
|
| Benchmark | JDK | Latest | Dev | Δ (dev vs latest) | Issues L/D |
|---|---|---|---|---|---|
| akka-uct | 21 | ✅ 10354 ms (21 iters) | ✅ 10192 ms (21 iters) | ≈ -1.6% (±11.2%) | — / — |
| akka-uct | 25 | ✅ 8881 ms (24 iters) | ✅ 8835 ms (24 iters) | ≈ -0.5% (±9.9%) | — / — |
| finagle-chirper | 21 | ✅ 6005 ms (33 iters) | ✅ 5958 ms (33 iters) | ≈ -0.8% (±24.7%) | |
| finagle-chirper | 25 | ✅ 5462 ms (36 iters) | ✅ 5459 ms (36 iters) | ≈ -0.1% (±24.2%) | |
| fj-kmeans | 21 | ✅ 2768 ms (68 iters) | ✅ 2659 ms (70 iters) | 🟢 -3.9% | — / — |
| fj-kmeans | 25 | ✅ 2827 ms (66 iters) | ✅ 2842 ms (66 iters) | ≈ +0.5% (±2.6%) | — / — |
| future-genetic | 21 | ✅ 2049 ms (91 iters) | ✅ 2115 ms (87 iters) | 🔴 +3.2% | — / — |
| future-genetic | 25 | ✅ 2049 ms (90 iters) | ✅ 2109 ms (88 iters) | 🔴 +2.9% | — / — |
| naive-bayes | 21 | ✅ 1263 ms (136 iters) | ✅ 1255 ms (136 iters) | ≈ -0.6% (±32.5%) | — / — |
| naive-bayes | 25 | ✅ 1019 ms (168 iters) | ✅ 1019 ms (168 iters) | ≈ 0% (±31.7%) | — / — |
| reactors | 21 | ✅ 16762 ms (15 iters) | ✅ 17039 ms (15 iters) | ≈ +1.7% (±7.9%) | — / — |
| reactors | 25 | ✅ 18327 ms (15 iters) | ✅ 18649 ms (15 iters) | ≈ +1.8% (±5.2%) | — / — |
Internal counter details (ddprof)
ddprof internal counters, latest / dev (✅ = 0, · = unavailable):
| Benchmark | JDK | Dropped rec | Dropped jvmti | Dropped trace | Skipped WC | AGCT fail | Unwind fail |
|---|---|---|---|---|---|---|---|
| akka-uct | 21 | ✅ / ✅ | ✅ / ✅ | 3 / 3 | 1960 / 2024 | ✅ / ✅ | ✅ / ✅ |
| akka-uct | 25 | ✅ / ✅ | ✅ / ✅ | 3 / 1 | 2257 / 2267 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 21 | ✅ / ✅ | ✅ / ✅ | 4 / 4 | 8586 / 8261 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 25 | ✅ / ✅ | ✅ / ✅ | 1 / 2 | 8451 / 8698 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / 1 | 1282 / 1249 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 25 | ✅ / ✅ | ✅ / ✅ | 4 / 1 | 1305 / 1277 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / 2 | 3012 / 2866 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 25 | ✅ / ✅ | ✅ / ✅ | 3 / 2 | 2771 / 2856 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 21 | ✅ / ✅ | ✅ / ✅ | 3 / 8 | 3585 / 3500 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 25 | ✅ / ✅ | ✅ / ✅ | 2 / 1 | 3525 / 3516 | ✅ / ✅ | ✅ / ✅ |
| reactors | 21 | ✅ / ✅ | ✅ / ✅ | 1 / 1 | 1752 / 1584 | ✅ / ✅ | ✅ / ✅ |
| reactors | 25 | ✅ / ✅ | ✅ / ✅ | 3 / ✅ | 1862 / 1859 | ✅ / ✅ | ✅ / ✅ |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
ddprof-lib/src/main/cpp/vmEntry.cpp:581
- This header comment says the “VMStructs init below only ever runs once”, but
VM::ready()is called from initLibrary(), initProfilerBridge(), and the VMInit callback, and there is no guard aroundVMStructs::init(lib)/VMStructs::ready(). Either add a guard, or adjust the comment so it doesn’t claim single execution.
// Run late initialization when JVM is ready. May be called more than once (from
// initProfilerBridge() directly, and later from the VMInit JVMTI callback, or from
// initLibrary() followed by a JNI-triggered attach) -- the VMStructs init below only
// ever runs once.
| // truncated_local is never read after a longjmp landing (only on the | ||
| // clean path below), so it need not be volatile; the outer `truncated` | ||
| // stays false on the recovery path. |
Benchmark Results (commit 90fadfe)Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126772742 Commit:
|
| Benchmark | JDK | Latest | Dev | Δ (dev vs latest) | Issues L/D |
|---|---|---|---|---|---|
| akka-uct | 21 | ✅ 10324 ms (21 iters) | ✅ 10431 ms (21 iters) | ≈ +1% (±11.8%) | — / — |
| akka-uct | 25 | ✅ 8786 ms (24 iters) | ✅ 8931 ms (24 iters) | ≈ +1.7% (±9.9%) | — / — |
| finagle-chirper | 21 | ✅ 6030 ms (33 iters) | ✅ 6016 ms (33 iters) | ≈ -0.2% (±26.1%) | |
| finagle-chirper | 25 | ✅ 5449 ms (36 iters) | ✅ 5448 ms (36 iters) | ≈ -0% (±24.6%) | |
| fj-kmeans | 21 | ✅ 2751 ms (68 iters) | ✅ 2683 ms (70 iters) | ≈ -2.5% (±2.7%) | — / — |
| fj-kmeans | 25 | ✅ 2842 ms (66 iters) | ✅ 2814 ms (66 iters) | ≈ -1% (±2.5%) | — / — |
| future-genetic | 21 | ✅ 2076 ms (89 iters) | ✅ 2101 ms (88 iters) | ≈ +1.2% (±2.7%) | — / — |
| future-genetic | 25 | ✅ 2077 ms (89 iters) | ✅ 2011 ms (93 iters) | 🟢 -3.2% | — / — |
| naive-bayes | 21 | ✅ 1264 ms (135 iters) | ✅ 1280 ms (133 iters) | ≈ +1.3% (±32.7%) | — / — |
| naive-bayes | 25 | ✅ 1006 ms (169 iters) | ✅ 976 ms (174 iters) | ≈ -3% (±31.7%) | — / — |
| reactors | 21 | ✅ 16957 ms (15 iters) | ✅ 16446 ms (15 iters) | ≈ -3% (±6.6%) | — / — |
| reactors | 25 | ✅ 18310 ms (15 iters) | ✅ 18574 ms (15 iters) | ≈ +1.4% (±5%) | — / — |
Internal counter details (ddprof)
ddprof internal counters, latest / dev (✅ = 0, · = unavailable):
| Benchmark | JDK | Dropped rec | Dropped jvmti | Dropped trace | Skipped WC | AGCT fail | Unwind fail |
|---|---|---|---|---|---|---|---|
| akka-uct | 21 | ✅ / ✅ | ✅ / ✅ | 3 / ✅ | 2007 / 1989 | ✅ / ✅ | ✅ / ✅ |
| akka-uct | 25 | ✅ / ✅ | ✅ / ✅ | 1 / 2 | 2249 / 2195 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 21 | ✅ / ✅ | ✅ / ✅ | 6 / 2 | 8633 / 8540 | ✅ / ✅ | ✅ / ✅ |
| finagle-chirper | 25 | ✅ / ✅ | ✅ / ✅ | 1 / ✅ | 8691 / 8542 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 21 | ✅ / ✅ | ✅ / ✅ | 3 / ✅ | 1270 / 1287 | ✅ / ✅ | ✅ / ✅ |
| fj-kmeans | 25 | ✅ / ✅ | ✅ / ✅ | 3 / 2 | 1281 / 1276 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 21 | ✅ / ✅ | ✅ / ✅ | 3 / 3 | 2939 / 2945 | ✅ / ✅ | ✅ / ✅ |
| future-genetic | 25 | ✅ / ✅ | ✅ / ✅ | 1 / ✅ | 2878 / 2858 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / 6 | 3515 / 3506 | ✅ / ✅ | ✅ / ✅ |
| naive-bayes | 25 | ✅ / ✅ | ✅ / ✅ | 5 / 3 | 3447 / 3501 | ✅ / ✅ | ✅ / ✅ |
| reactors | 21 | ✅ / ✅ | ✅ / ✅ | ✅ / 1 | 1709 / 1603 | ✅ / ✅ | ✅ / ✅ |
| reactors | 25 | ✅ / ✅ | ✅ / ✅ | 1 / ✅ | 1936 / 1940 | ✅ / ✅ | ✅ / ✅ |
What does this PR do?:
Motivation:
Additional Notes:
How to test the change?:
For Datadog employees:
credentials of any kind, I've requested a security review (run the
dd:platform-security-reviewskill, or file a request via the PSEC review form).
bewairealso runs automatically on every PR.Unsure? Have a question? Request a review!