[PATCH] D69959: [C-index] Fix test when using Debug target & MSVC STL
Alexandre Ganea via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 7 11:52:56 PST 2019
aganea created this revision.
aganea added reviewers: STL_MSFT, rnk, dexonsmith.
aganea added a project: clang.
Herald added subscribers: cfe-commits, arphaman.
This is an attempt to fix `clang/test/Index/crash-recovery-modules.m` when compiling a build with the MSVC STL & _ITERATOR_DEBUG_LEVEL == 2 (meaning a DEBUG build)
The problem is related to the STL implementation, not the compiler.
I do run sometimes the Debug build with LLVM_ENABLE_ASSERTIONS, it brings up interesting issues (see rG007d173e2e0c29903bc17a9d5108f531a6f2ea4d <https://reviews.llvm.org/rG007d173e2e0c29903bc17a9d5108f531a6f2ea4d>)
The problem
-----------
Before, the test used to freeze with this callstack:
F10658035: freeze_crash-recovery-modules_debug.PNG <https://reviews.llvm.org/F10658035>
A lock was taken by another thread that died since.
The test forcibly triggers a crash with the help of `#pragma clang __debug crash`. This would hit `clang/lib/Lex/Pragma.cpp, L1040`.
However the call was made through a `CrashRecoveryContext`, which is essentially a try/catch, so we get back at `clang/lib/Frontend/CompilerInstance.cpp, L1152`:
F10658114: freeze_crash-recovery-modules_debug 2.PNG <https://reviews.llvm.org/F10658114>
However, later on at the end of this function, `CompilerInvocation` is destroyed, which in turn triggers destruction of `FrontendOptions`, which eventually triggers the destruction of its `Inputs` member.
The problem here is that `Inputs` was being iterated just before the #pragma crash occurred. This left STL's internal linked list (for the `Inputs` vector) in a stale state, pointing to a stack variable that doesn't exist anymore (now that we got out of `CrashRecoveryContext`). When `Inputs` is being destroyed, the code in `C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.16.27023\include\xutility` tried to clear that linked list. But now we have nodes pointing to stack memory pages that were already un-commited.
F10658173: freeze_crash-recovery-modules_debug 3.PNG <https://reviews.llvm.org/F10658173>
All this ends up to a second crash which happens to leave the _LOCK_DEBUG mutex locked. The crash itself is protected by another `CrashRecoveryContext` invoked by `clang/tools/libclang/CIndex.cpp, L3624` so the program doesn't crash. But it now ends in a stale state, and the _LOCK_DEBUG mutex is locked forever, which leads to the freeze in the top screenshot.
The fix
-------
Ideally, this would require some kind of TLS or stack frame mechanism in the MSVC STL, to unwind the linked list in case of a crash.
Our patch takes a shortcut and does that manually, by simply saving the state of the vector's debug iterator linked list, and restoring it after (a possible) crash.
I could disable the test with something like `UNSUPPORTED: windows-msvc, debug` (we don't have debug) but I thought this test is valuable.
Please let me know what approach you would like me to take. If that's the way to go, I'll move these two functions (`Save/ApplyIteratorDebug`) to a more appropriate place.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D69959
Files:
clang/lib/Frontend/CompilerInstance.cpp
Index: clang/lib/Frontend/CompilerInstance.cpp
===================================================================
--- clang/lib/Frontend/CompilerInstance.cpp
+++ clang/lib/Frontend/CompilerInstance.cpp
@@ -1031,6 +1031,39 @@
return LangOpts.CPlusPlus ? Language::CXX : Language::C;
}
+template <typename T>
+void SaveIteratorDebug(T &Container, SmallVectorImpl<void *> &Saver) {
+#if _ITERATOR_DEBUG_LEVEL == 2
+ std::_Container_proxy *C = Container._Myproxy();
+ if (!C)
+ return;
+ std::_Lockit _Lock(_LOCK_DEBUG);
+ for (std::_Iterator_base12 *P = C->_Myfirstiter; P != nullptr;
+ P = P->_Mynextiter) {
+ Saver.push_back(P);
+ }
+#endif
+}
+
+// NOTE: This doesn't try to clear out the nodes that weren't there before,
+// because they might have an address further down the (old) stack, and the OS
+// might have freed those virtual pages already, which would cause a GPF.
+template <typename T>
+void ApplyIteratorDebug(T &Container, const SmallVectorImpl<void *> &Saver) {
+#if _ITERATOR_DEBUG_LEVEL == 2
+ std::_Container_proxy *C = Container._Myproxy();
+ if (!C)
+ return;
+ std::_Lockit _Lock(_LOCK_DEBUG);
+ std::_Iterator_base12 **P = &C->_Myfirstiter;
+ for (auto S : Saver) {
+ *P = (std::_Iterator_base12 *)S;
+ P = &(*P)->_Mynextiter;
+ }
+ *P = nullptr;
+#endif
+}
+
/// Compile a module file for the given module, using the options
/// provided by the importing compiler instance. Returns true if the module
/// was built without errors.
@@ -1139,6 +1172,9 @@
PreBuildStep(Instance);
+ SmallVector<void*, 4> Iters;
+ SaveIteratorDebug(Instance.getFrontendOpts().Inputs, Iters);
+
// Execute the action to actually build the module in-place. Use a separate
// thread so that we get a stack large enough.
llvm::CrashRecoveryContext CRC;
@@ -1149,6 +1185,8 @@
},
DesiredStackSize);
+ ApplyIteratorDebug(Instance.getFrontendOpts().Inputs, Iters);
+
PostBuildStep(Instance);
ImportingInstance.getDiagnostics().Report(ImportLoc,
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D69959.228276.patch
Type: text/x-patch
Size: 2027 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20191107/2324601e/attachment.bin>
More information about the cfe-commits
mailing list