llvm-project/lldb/source/API
Raphael Isemann 18dbe0f954 [lldb] Prevent that LLDB randomly crashes in CommandLineParser::addOption by initializing LLVM's command line parser
Since quite a while Apple's LLDB fork (that contains the Swift debugging
support) is randomly crashing in `CommandLineParser::addOption` with an error
such as `CommandLine Error: Option 'h' registered more than once!`

The backtrace of the crashing thread is shown below. There are also usually many
other threads also performing similar clang::FrontendActions which are all
trying to generate (usually outdated) Clang modules which are used by Swift for
various reasons.

```
[  6] LLDB`CommandLineParser::addOption(llvm:🆑:Option*, llvm:🆑:SubCommand*) + 856
[  7] LLDB`CommandLineParser::addOption(llvm:🆑:Option*, llvm:🆑:SubCommand*) + 733
[  8] LLDB`CommandLineParser::addOption(llvm:🆑:Option*, bool) + 184
[  9] LLDB`llvm:🆑:ParseCommandLineOptions(...) [inlined] ::CommandLineParser::ParseCommandLineOptions(... + 1279
[  9] LLDB`llvm:🆑:ParseCommandLineOptions(...) + 497
[ 10] LLDB`setCommandLineOpts(clang::CodeGenOptions const&) + 416
[ 11] LLDB`EmitAssemblyHelper::EmitAssemblyWithNewPassManager(...) + 98
[ 12] LLDB`clang::EmitBackendOutput(...) + 4580
[ 13] LLDB`PCHContainerGenerator::HandleTranslationUnit(clang::ASTContext&) + 871
[ 14] LLDB`clang::MultiplexConsumer::HandleTranslationUnit(clang::ASTContext&) + 43
[ 15] LLDB`clang::ParseAST(clang::Sema&, bool, bool) + 579
[ 16] LLDB`clang::FrontendAction::Execute() + 74
[ 17] LLDB`clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 1808
```

The underlying reason for the crash is that the CommandLine code in LLVM isn't
thread-safe and will never be thread-safe with its current architecture. The way
LLVM's CommandLine logic works is that all parts of the LLVM can provide command
line arguments by defining `cl::opt` global variables and their constructors
(which are invoked during static initialisation) register the variable in LLVM's
CommandLineParser (which is also just a global variable). At some later point
after static initialization we actually try to parse command line arguments and
we ask the CommandLineParser to parse our `argv`.  The CommandLineParser then
lazily constructs it's internal parsing state in a non-thread-safe way (this is
where the crash happens), parses the provided command line and then goes back to
the respective `cl::opt` global variables and sets their values according to the
parse result.

As all of this is based on global state, this whole mechanism isn't thread-safe
so the only time to ever use it is when we know we only have one active thread
dealing with LLVM logic. That's why nearly all callers of
`llvm:🆑:ParseCommandLineOptions` are at the top of the `main` function of the
some LLVM-based tool. One of the few exceptions to this rule is in the
`setCommandLineOpts` function in `BackendUtil.cpp` which is in our backtrace:

```
static void setCommandLineOpts(const CodeGenOptions &CodeGenOpts) {
  SmallVector<const char *, 16> BackendArgs;
  BackendArgs.push_back("clang"); // Fake program name.
  if (!CodeGenOpts.DebugPass.empty()) {
    BackendArgs.push_back("-debug-pass");
    BackendArgs.push_back(CodeGenOpts.DebugPass.c_str());
  }
  if (!CodeGenOpts.LimitFloatPrecision.empty()) {
    BackendArgs.push_back("-limit-float-precision");
    BackendArgs.push_back(CodeGenOpts.LimitFloatPrecision.c_str());
  }
  BackendArgs.push_back(nullptr);
  llvm:🆑:ParseCommandLineOptions(BackendArgs.size() - 1,
                                    BackendArgs.data());
}
```

This is trying to set `cl::opt` variables in the LLVM backend to their right
value as the passed via CodeGenOptions by invoking the CommandLine parser. As
this is just in some generic Clang CodeGen code (where we allow having multiple
threads) this is code is clearly wrong. If we're unlucky it either overwrites
the value of the global variables or it causes the CommandLine parser to crash.

So the next question is why is this only crashing in LLDB? The main reason seems
to be that easiest way to crash this code is to concurrently enter the initial
CommandLineParser construction where it tries to collect all the registered
`cl::opt` options and checks for sanity:

```
      // If it's a DefaultOption, check to make sure it isn't already there.
      if (O->isDefaultOption() &&
          SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end())
        return;

      // Add argument to the argument map!
      if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) {
        errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr
               << "' registered more than once!\n";
        HadErrors = true;
      }
```

The `OptionsMap` here is global variable and if we end up in this code with two
threads at once then two threads at the same time can register an option (such
as 'h') when they pass the first `if` and then we fail with the sanity check in
the second `if`.

After this sanity check and initial setup code the only remaining work is just
parsing the provided CommandLine which isn't thread-safe but at least doesn't
crash in all my attempts at breaking it (as it's usually just reading from the
already generated parser state but not further modifying it). The exception to
this is probably that once people actually specify the options in the code
snippet above we might run into some new interesting ways to crash everything.

To go back to why it's only affecting LLDB: Nearly all LLVM tools I could find
(even if they are using threads) seem to call the CommandLine parser at the
start so they all execute the initial parser setup at a point where there is
only one thread. So once the code above is executed they are mostly safe from
the sanity check crashes. We even have some shady code for the gtest `main` in
`TestMain.cpp` which is why this also doesn't affect unit tests.

The only exception to this rule is ... *drum roll* ... LLDB! it's not using that
CommandLine library for parsing options so it also never ends up calling it in
`main`. So when we end up in the `FrontendAction` code from the backtrace we are
already very deep in some LLDB logic and usually already have several threads.
In a situation where Swift decides to compile a large amount of Clang modules in
parallel we then end up entering this code via several threads. If several
threads reach this code at the same time we end up in the situation where the
sanity-checking code of CommandLine crashes. I have a very reliable way of
demonstrating the whole thing in D99650 (just run the unit test several times,
it usually crashes after 3-4 attempts).

We have several ways to fix this:

1. Make the whole CommandLine mechanism in LLVM thread-safe.

2. Get rid of `setCommandLineOpts` in `BackendUtil.cpp` and other callers of the
command line parsing in generic Clang code.

3. Initialise the CommandLine library in a safe point in LLDB.

Option 1 is just a lot of work and I'm not even sure where to start. The whole
mechanism is based on global variables and global state and this seems like a
humongous task.

Option 2 is probably the best thing we can do in the near future. There are only
two callers of the command line parser in generic Clang code. The one in
`BackendUtils.cpp` looks like it can be replaced with some reasonable
refactoring (as it only deals with two specific options). There is another one
in `ExecuteCompilerInvocation` which deals with forwarding the generic `-mllvm`
options to the backend which seems like it will just end up requiring us to do
Option 1.

Option 3 is what this patch is doing. We just parse some dummy command line
invocation in a point of the LLDB execution where we only have one thread that
is dealing with LLVM/Clang stuff. This way we are at least prevent the frequent
crashes for users as parsing the dummy command line invocation will set up the
initial parser state safely.

Fixes rdar://70989856

Reviewed By: mib, JDevlieghere

Differential Revision: https://reviews.llvm.org/D99652
2021-04-01 20:17:54 +02:00
..
CMakeLists.txt [lldb] Symlink the Clang resource directory to the LLDB build directory in standalone builds 2020-10-06 09:28:39 +02:00
SBAddress.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBAttachInfo.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBBlock.cpp [lldb][NFCI] Remove unused LanguageType parameters 2020-01-30 21:57:23 -08:00
SBBreakpoint.cpp Add an SB API to get the SBTarget from an SBBreakpoint 2020-10-15 14:28:44 -07:00
SBBreakpointLocation.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBBreakpointName.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBBreakpointOptionCommon.cpp [lldb][NFC] Fix all formatting errors in .cpp file headers 2020-01-24 08:52:55 +01:00
SBBreakpointOptionCommon.h [lldb] Update header guards to be consistent and compliant with LLVM (NFC) 2020-02-17 23:15:40 -08:00
SBBroadcaster.cpp [lldb][NFC] Fix all formatting errors in .cpp file headers 2020-01-24 08:52:55 +01:00
SBCommandInterpreter.cpp Reland "[lldb] Make CommandInterpreter's execution context the same as debugger's one" 2021-02-08 15:09:09 +03:00
SBCommandInterpreterRunOptions.cpp [lldb/API] Add CommandInterpreter::{Get,Set}PrintErrors to SBAPI (NFC) 2021-03-05 19:33:33 +01:00
SBCommandReturnObject.cpp [lldb/Interpreter] Support color in CommandReturnObject 2020-06-09 10:45:45 -07:00
SBCommunication.cpp [lldb] Change Communication::SetConnection to take a unique_ptr 2020-04-02 14:42:25 +02:00
SBCompileUnit.cpp [lldb][NFC] Fix all formatting errors in .cpp file headers 2020-01-24 08:52:55 +01:00
SBData.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBDebugger.cpp Add a progress class that can track long running operations in LLDB. 2021-03-24 12:58:13 -07:00
SBDeclaration.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBEnvironment.cpp [lldb/Reproducers] Add instrumentation to SBEnvironment 2020-05-20 13:02:20 -07:00
SBError.cpp Fix SBError::SetErrorToGenericError 2020-10-26 15:44:38 +01:00
SBEvent.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBExecutionContext.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBExpressionOptions.cpp [lldb/API] Add missing LLDB_REGISTER_METHOD macros 2020-04-06 16:09:40 -07:00
SBFile.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBFileSpec.cpp [lldb/Reproducers] Fix passive replay for (char*, size_t) functions. 2020-04-20 13:26:11 -07:00
SBFileSpecList.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBFrame.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBFunction.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBHostOS.cpp [lldb] Provide GetHomeDirectory wrapper in Host::FileSystem (NFC) 2020-08-20 14:07:05 -07:00
SBInstruction.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBInstructionList.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBLanguageRuntime.cpp [lldb][NFC] Fix all formatting errors in .cpp file headers 2020-01-24 08:52:55 +01:00
SBLaunchInfo.cpp [lldb/Commands] Add command options for ScriptedProcess to ProcessLaunch 2021-03-23 18:24:47 +01:00
SBLineEntry.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBListener.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBMemoryRegionInfo.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBMemoryRegionInfoList.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBModule.cpp [lldb] Add missing LLDB_REGISTER for GarbageCollectAllocatedModules 2020-08-17 10:14:41 -07:00
SBModuleSpec.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBPlatform.cpp [lldb] Add missing LLDB_REGISTER_CONSTRUCTOR in SBPlatform 2020-09-10 18:50:02 -07:00
SBProcess.cpp [lldb/Reproducers] Fix passive replay for (char*, size_t) functions. 2020-04-20 13:26:11 -07:00
SBProcessInfo.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBQueue.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBQueueItem.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBReproducer.cpp [lldb] Move copying of files into reproducer out of process 2020-10-23 12:33:54 -07:00
SBReproducerPrivate.h [lldb] Extract reproducer providers & co into their own header. 2020-08-22 10:04:27 -07:00
SBSection.cpp [lldb] Switch Section-dumping code to raw_ostream 2020-05-14 11:59:18 +02:00
SBSourceManager.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBStream.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBStringList.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBStructuredData.cpp [lldb/Reproducers] Fix passive replay for (char*, size_t) functions. 2020-04-20 13:26:11 -07:00
SBSymbol.cpp [lldb] Pass reference instead of pointer in protected SBAddress methods. 2020-09-25 11:47:05 -07:00
SBSymbolContext.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBSymbolContextList.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBTarget.cpp [lldb][NFC] Delete unused AddressResolverName 2021-03-03 13:30:02 +01:00
SBThread.cpp [trace][intel-pt] Implement trace start and trace stop 2021-03-30 17:31:37 -07:00
SBThreadCollection.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBThreadPlan.cpp Add a setting to force stepping to always run all threads. 2020-08-07 14:47:31 -07:00
SBTrace.cpp [trace][intel-pt] Implement trace start and trace stop 2021-03-30 17:31:37 -07:00
SBTraceOptions.cpp [lldb][NFC] Fix all formatting errors in .cpp file headers 2020-01-24 08:52:55 +01:00
SBType.cpp [lldb] Add SBType::GetEnumerationIntegerType method 2020-12-22 10:08:22 -08:00
SBTypeCategory.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBTypeEnumMember.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBTypeFilter.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBTypeFormat.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBTypeNameSpecifier.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBTypeSummary.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBTypeSynthetic.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBUnixSignals.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SBValue.cpp [lldb] Remove redundant ctor call (NFC) 2020-09-29 10:05:12 -07:00
SBValueList.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBVariablesOptions.cpp [lldb/API] Use std::make_unique<> (NFC) 2020-06-24 16:29:30 -07:00
SBWatchpoint.cpp [lldb] Replace empty ctor en dtor bodies with =default (NFC) 2020-02-17 22:58:26 -08:00
SystemInitializerFull.cpp [lldb] Prevent that LLDB randomly crashes in CommandLineParser::addOption by initializing LLVM's command line parser 2021-04-01 20:17:54 +02:00
SystemInitializerFull.h [lldb] Update header guards to be consistent and compliant with LLVM (NFC) 2020-02-17 23:15:40 -08:00
Utils.h [lldb] Update header guards to be consistent and compliant with LLVM (NFC) 2020-02-17 23:15:40 -08:00
liblldb-private.exports
liblldb.exports
liblldb.xcode.exports