<div dir="ltr">Gentle bump.<div><br></div><div>Quoting myself for emphasis:</div><div>></div><div>> The problem seems to be that</div><div>> (1) libc++.a needs to copy strings sometimes, so it contains codegen for the copy constructor</div><div>> (2) libc++.a is compiled with -D_LIBCPP_DEBUG=0, i.e., assertions but no debug iterators</div><div>> (3) Therefore any strings created by the copy constructor inside libc++.a, don't get registered with the debug-iterators library</div><div>> (4) Therefore pretty much everything is affected by unpredictable assert-fails??</div><div>><br></div><div>> The real question is, how was this ever <i>supposed</i> to work?</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, May 1, 2021 at 8:50 PM Arthur O'Dwyer <<a href="mailto:arthur.j.odwyer@gmail.com">arthur.j.odwyer@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi folks,<div><br></div><div>I recently added a buildkite CI step that runs libc++'s test suite with -D_LIBCPP_DEBUG=1. This turns on "debug iterators," which is a thing in the library that maintains a collection of all known container instances and iterator instances, and updates the collection on every container-construction, container-destruction, container-modification, iterator-construction, iterator-destruction, or iterator-modification. This enables it to print an error message on any iterator-modification, iterator-comparison, or iterator-dereference that violates the iterator-invalidation guarantees of the Standard.</div><div><a href="https://reviews.llvm.org/D100866" target="_blank">https://reviews.llvm.org/D100866</a> was the review that added that CI step.<br></div><div>I marked a bunch of tests as "LIBCXX-DEBUG-FIXME", with the intent of fixing them all quickly. The patches for all-but-one of those FIXMEs are:</div><div><a href="https://reviews.llvm.org/D101674" target="_blank">https://reviews.llvm.org/D101674</a><br></div><div><div></div></div><div><a href="https://reviews.llvm.org/D101675" target="_blank">https://reviews.llvm.org/D101675</a><br></div><div></div><div><a href="https://reviews.llvm.org/D101676" target="_blank">https://reviews.llvm.org/D101676</a><br></div><div></div><div><a href="https://reviews.llvm.org/D101677" target="_blank">https://reviews.llvm.org/D101677</a><br></div><div></div><div><a href="https://reviews.llvm.org/D101678" target="_blank">https://reviews.llvm.org/D101678</a><br></div><div></div><div><a href="https://reviews.llvm.org/D101679" target="_blank">https://reviews.llvm.org/D101679</a><br></div><div><br></div><div></div><div>I've just tracked down the very final LIBCXX-DEBUG-FIXME. Unfortunately, it's a doozy.<br><div>The symptom is a segfault (null pointer dereference) in</div><div>libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp<br></div></div><div><br></div><div>It's a segfault instead of a _LIBCPP_ASSERT-failure, because the compiled-to-binary filesystem library was not built with _LIBCPP_ASSERT enabled. Okay, no problem, I should enable debug assertions in the library for this particular CI builder. I believe this is the patch for that:</div><div><br></div><div>```</div><div>--- a/libcxx/cmake/caches/Generic-debug-iterators.cmake<br></div>+++ b/libcxx/cmake/caches/Generic-debug-iterators.cmake<br>@@ -1,2 +1,3 @@<br> set(LIBCXX_TEST_PARAMS "debug_level=1" "additional_features=LIBCXX-DEBUG-FIXME" CACHE STRING "")<br> set(LIBCXXABI_TEST_PARAMS "${LIBCXX_TEST_PARAMS}" CACHE STRING "")<br>+set(LIBCXX_ENABLE_ASSERTIONS ON CACHE BOOL "")<div>```</div><div><br></div><div>Now the symptom is a _LIBCPP_ASSERT-failure. The problem is that we're move-constructing a string, and the source string was never registered with the debug-iterators library.</div><div><br></div><div>Reduced test case:</div><div>```</div>cat > test.cpp <<EOF<br>#include <string><br>void test_debug_function(std::__libcpp_debug_info const&) {<br>    std::abort();<br>}<br>int main(int, char**) {<br>    std::__libcpp_set_debug_function(&test_debug_function);<br>    std::string x = "1234567890123456789012345678901234";<br>    std::string y = x;  // calls into the compiled library<br>    std::string z = std::move(y);<br>}<br>EOF<br>bin/clang++ test.cpp -D_LIBCPP_DEBUG=1 lib/libc++.a lib/libc++abi.a<div>./a.out</div><div>Abort trap: 6</div><div>```</div><div><br></div><div>The problem seems to be that</div><div>(1) libc++.a needs to copy strings sometimes, so it contains codegen for the copy constructor</div><div>(2) libc++.a is compiled with -D_LIBCPP_DEBUG=0, i.e., assertions but no debug iterators</div><div>(3) Therefore any strings created by the copy constructor inside libc++.a, don't get registered with the debug-iterators library</div><div>(4) Therefore pretty much everything is affected by unpredictable assert-fails??</div><div>I'm amazed that only one test out of our entire test suite seems to be affected by this issue. I would think that pretty much everything in the debug-iterators CI should be failing because of this; I'm not sure how it's working right now.</div><div><br></div><div>The real question is, how was this ever <i>supposed</i> to work? It seems like the person-who-compiled-libc++.a and the person-compiling-their-user-code-against-libc++-headers need to agree on the setting of _LIBCPP_DEBUG:— If libc++.a has _LIBCPP_DEBUG=1 and the user code doesn't, everything explodes; and also vice versa. But this doesn't seem realistic.</div><div><br></div><div>None of the offending basic_string constructors are currently marked with _LIBCPP_INLINE_VISIBILITY. I suspect that adding _LIBCPP_INLINE_VISIBILITY could help reduce the surface area of the problem even further... but based on what I said above, I don't see how the problem <i>ever</i> gets to zero, unless we can somehow prevent libc++.a from ever constructing a string (or at least never letting strings-it-constructs leak out into the user's code where _LIBCPP_DEBUG=1 might be in effect).</div><div><br></div><div><a href="https://reviews.llvm.org/D48616" target="_blank">https://reviews.llvm.org/D48616</a> may be related — it added some non-inline constructors — but the review comments don't talk about inline or visibility at all.<br></div><div><br></div><div><div>What am I missing here? Thoughts?</div><div><br></div><div>–Arthur</div><div></div></div></div>
</blockquote></div>