diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h index bea88a6cf1f51..279a6baf30c99 100644 --- a/lldb/include/lldb/Core/ModuleList.h +++ b/lldb/include/lldb/Core/ModuleList.h @@ -560,9 +560,9 @@ class ModuleList { static constexpr long kUseCountModuleListOrphaned = 1; private: - static bool LoadScriptingResourceInTargetForModule(Module &module, - Target &target, - Status &error); + static bool LoadScriptingResourceInTargetForModule( + Module &module, Target &target, Status &error, + std::vector &warned_script_paths); public: typedef LockingAdaptedIterable diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d3c52cfe7d8f3..eb175c0b99dbf 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -36,8 +36,10 @@ #endif #include "clang/Driver/Driver.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" @@ -1330,6 +1332,33 @@ bool ModuleList::RemoveSharedModuleIfOrphaned(const ModuleWP module_wp) { return GetSharedModuleList().RemoveIfOrphaned(module_wp); } +static void +ReportScriptLoading(Debugger &debugger, + llvm::ArrayRef warned_script_paths) { + if (warned_script_paths.empty()) + return; + + static std::once_flag s_warn_once; + + // clang-format off + debugger.ReportWarning( +R"(Found executable debug scripts. To run a script in this debug session: + + command script import "/path/to/script.py" + +To run all discovered debug scripts in this session: + + settings set target.load-script-from-symbol-file true +)", debugger.GetID(), &s_warn_once); + + debugger.ReportWarning(llvm::formatv( +R"(The following debug scripts were detected but not loaded: + + - {0} +)", llvm::join(warned_script_paths, "\n - ")), debugger.GetID()); + // clang-format on +} + static bool LoadScriptingModule(const FileSpec &scripting_fspec, ScriptInterpreter &script_interpreter, Target &target, Status &error) { @@ -1343,9 +1372,9 @@ static bool LoadScriptingModule(const FileSpec &scripting_fspec, /*module_sp*/ nullptr, /*extra_path*/ {}, target.shared_from_this()); } -bool ModuleList::LoadScriptingResourceInTargetForModule(Module &module, - Target &target, - Status &error) { +bool ModuleList::LoadScriptingResourceInTargetForModule( + Module &module, Target &target, Status &error, + std::vector &warned_script_paths) { Log *log = GetLog(LLDBLog::Modules); Debugger &debugger = target.GetDebugger(); @@ -1392,23 +1421,7 @@ bool ModuleList::LoadScriptingResourceInTargetForModule(Module &module, break; LLVM_FALLTHROUGH; case eLoadScriptFromSymFileWarn: - debugger.ReportWarning( - llvm::formatv( - // clang-format off -R"('{0}' contains {1} debug script. To run this script in this debug session: - - command script import "{2}" - -To run all discovered debug scripts in this session: - - settings set target.load-script-from-symbol-file true -)", - // clang-format on - module.GetFileSpec().GetFileNameStrippingExtension(), - trusted ? "a trusted" : "an untrusted", - scripting_fspec.GetPath()), - debugger.GetID()); - + warned_script_paths.emplace_back(scripting_fspec.GetPath()); continue; } @@ -1437,10 +1450,13 @@ bool ModuleList::LoadScriptingResourcesInTarget(Target *target, const ModuleList tmp_module_list(*this); m_modules_mutex.unlock(); + std::vector warned_script_paths; + for (auto module : tmp_module_list.ModulesNoLocking()) { if (module) { Status error; - if (!LoadScriptingResourceInTargetForModule(*module, *target, error)) { + if (!LoadScriptingResourceInTargetForModule(*module, *target, error, + warned_script_paths)) { if (error.Fail() && error.AsCString()) { error = Status::FromErrorStringWithFormat( "unable to load scripting data for " @@ -1456,6 +1472,9 @@ bool ModuleList::LoadScriptingResourcesInTarget(Target *target, } } } + + ReportScriptLoading(target->GetDebugger(), warned_script_paths); + return errors.empty(); } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 0168c7d686e37..ad2ad14684ca2 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -70,7 +70,9 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorExtras.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ThreadPool.h" #include @@ -1854,6 +1856,7 @@ void Target::ModulesDidLoad(ModuleList &module_list) { LoadTypeSummariesForModule(module_sp); LoadFormattersForModule(module_sp); } + m_breakpoint_list.UpdateBreakpoints(module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); if (m_process_sp) { diff --git a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script-name-warnings.test b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script-name-warnings.test index 07fa3c0d6e679..cb494c4436a4f 100644 --- a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script-name-warnings.test +++ b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script-name-warnings.test @@ -33,7 +33,7 @@ ## Also confirm that the warning message about auto-loading scripts is printed afterwards. -# CHECK-REMOVE: warning: 'Test-Module2' contains an untrusted debug script. To run this script in this +# CHECK-REMOVE: warning: Found executable debug scripts. To run a script in this debug session #--- main.c int main() { return 0; } diff --git a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script.test b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script.test index b65de63c7d8b7..fc28d4737422e 100644 --- a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script.test +++ b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-python-script.test @@ -23,9 +23,9 @@ # RUN: -o 'target create %t/TestModule.out' 2>&1 \ # RUN: | FileCheck %s --implicit-check-not=warning --check-prefix=CHECK-LOADED -# CHECK-WARN: warning: 'TestModule' contains an untrusted debug script. To run this script in this debug session: +# CHECK-WARN: warning: Found executable debug scripts. To run a script in this debug session: # CHECK-WARN-EMPTY: -# CHECK-WARN-NEXT:{{^}} command script import "{{.*}}/TestModule.out.dSYM/Contents/Resources/Python/TestModule.py" +# CHECK-WARN-NEXT:{{^}} command script import "/path/to/script.py" # CHECK-WARN-EMPTY: # CHECK-WARN-NEXT: To run all discovered debug scripts in this session: # CHECK-WARN-EMPTY: diff --git a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-batch-dependents.test b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-batch-dependents.test new file mode 100644 index 0000000000000..2e202dc1fb008 --- /dev/null +++ b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-batch-dependents.test @@ -0,0 +1,42 @@ +# REQUIRES: python, system-darwin +# +# Test that when load-script-from-symbol-file is set to 'warn' and multiple +# dependent modules with dSYM scripts are loaded in a single batch (via +# target create), LLDB emits a single warning listing all detected scripts. + +# RUN: split-file %s %t +# RUN: %clang_host -g -shared %t/lib.c -o %t/libTestLib1.dylib +# RUN: %clang_host -g -shared %t/lib.c -o %t/libTestLib2.dylib +# RUN: %clang_host -g %t/main.c -o %t/main.out %t/libTestLib1.dylib %t/libTestLib2.dylib +# RUN: mkdir -p %t/libTestLib1.dylib.dSYM/Contents/Resources/Python +# RUN: mkdir -p %t/libTestLib2.dylib.dSYM/Contents/Resources/Python +# RUN: cp %t/dsym_script1.py %t/libTestLib1.dylib.dSYM/Contents/Resources/Python/libTestLib1.py +# RUN: cp %t/dsym_script2.py %t/libTestLib2.dylib.dSYM/Contents/Resources/Python/libTestLib2.py +# RUN: %lldb -b \ +# RUN: -o 'settings set target.load-script-from-symbol-file warn' \ +# RUN: -o 'target create %t/main.out' 2>&1 \ +# RUN: | FileCheck %s --strict-whitespace \ +# RUN: --implicit-check-not=DSYM_SCRIPT1 --implicit-check-not=DSYM_SCRIPT2 + +# Both dependent libraries should appear in a single warning. +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-DAG:{{^}} - {{.*}}/libTestLib1.dylib.dSYM/Contents/Resources/Python/libTestLib1.py +# CHECK-DAG:{{^}} - {{.*}}/libTestLib2.dylib.dSYM/Contents/Resources/Python/libTestLib2.py + +#--- main.c +extern int lib_func(void); +int main() { return lib_func(); } + +#--- lib.c +int lib_func(void) { return 0; } + +#--- dsym_script1.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("DSYM_SCRIPT1", file=sys.stderr) + +#--- dsym_script2.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("DSYM_SCRIPT2", file=sys.stderr) diff --git a/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-multiple-modules.test b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-multiple-modules.test new file mode 100644 index 0000000000000..40a284dbea393 --- /dev/null +++ b/lldb/test/Shell/Platform/AutoLoad/Darwin/dsym-warn-multiple-modules.test @@ -0,0 +1,54 @@ +# REQUIRES: python, system-darwin +# +# Test that when load-script-from-symbol-file is set to 'warn', LLDB emits a +# warning for each dSYM script without executing them. Verify that warnings +# are emitted for all modules, not just the first. + +# RUN: split-file %s %t +# RUN: %clang_host -g %t/main.c -o %t/TestModule.out +# RUN: %clang_host -g %t/main.c -o %t/TestModule2.out +# RUN: %clang_host -g %t/main.c -o %t/TestModule3.out +# RUN: mkdir -p %t/TestModule.out.dSYM/Contents/Resources/Python +# RUN: mkdir -p %t/TestModule2.out.dSYM/Contents/Resources/Python +# RUN: mkdir -p %t/TestModule3.out.dSYM/Contents/Resources/Python + +# RUN: cp %t/dsym_script.py %t/TestModule.out.dSYM/Contents/Resources/Python/TestModule.py +# RUN: cp %t/dsym_script2.py %t/TestModule2.out.dSYM/Contents/Resources/Python/TestModule2.py +# RUN: cp %t/dsym_script3.py %t/TestModule3.out.dSYM/Contents/Resources/Python/TestModule3.py +# RUN: %lldb -b \ +# RUN: -o 'settings set target.load-script-from-symbol-file warn' \ +# RUN: -o 'target create %t/TestModule.out' \ +# RUN: -o 'target modules add %t/TestModule2.out %t/TestModule3.out' 2>&1 \ +# RUN: | FileCheck %s --strict-whitespace \ +# RUN: --implicit-check-not=DSYM_SCRIPT --implicit-check-not=DSYM_SCRIPT2 --implicit-check-not=DSYM_SCRIPT3 + +# CHECK: (lldb) target create +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/TestModule.out.dSYM/Contents/Resources/Python/TestModule.py + +# CHECK: (lldb) target modules add +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/TestModule2.out.dSYM/Contents/Resources/Python/TestModule2.py +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/TestModule3.out.dSYM/Contents/Resources/Python/TestModule3.py + +#--- main.c +int main() { return 0; } + +#--- dsym_script.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("DSYM_SCRIPT", file=sys.stderr) + +#--- dsym_script2.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("DSYM_SCRIPT2", file=sys.stderr) + +#--- dsym_script3.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("DSYM_SCRIPT3", file=sys.stderr) diff --git a/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-batch-dependents.test b/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-batch-dependents.test new file mode 100644 index 0000000000000..f46e725338131 --- /dev/null +++ b/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-batch-dependents.test @@ -0,0 +1,44 @@ +# REQUIRES: python, asserts, !system-windows +# +# Test that when load-script-from-symbol-file is set to 'warn' and multiple +# dependent modules with safe-path scripts are loaded in a single batch (via +# target create), LLDB emits a single warning listing all detected scripts. + +# RUN: split-file %s %t +# RUN: %clang_host -shared -fPIC %t/lib.c -o %t/libTestLib1.so +# RUN: %clang_host -shared -fPIC %t/lib.c -o %t/libTestLib2.so +# RUN: %clang_host %t/main.c -o %t/main.out %t/libTestLib1.so %t/libTestLib2.so \ +# RUN: -Wl,-rpath,%t +# RUN: mkdir -p %t/safe-path/libTestLib1 +# RUN: mkdir -p %t/safe-path/libTestLib2 +# RUN: cp %t/script1.py %t/safe-path/libTestLib1/libTestLib1.py +# RUN: cp %t/script2.py %t/safe-path/libTestLib2/libTestLib2.py +# RUN: %lldb -b \ +# RUN: -o 'settings set target.load-script-from-symbol-file warn' \ +# RUN: -o 'settings append testing.safe-auto-load-paths %t/safe-path' \ +# RUN: -o 'target create %t/main.out' 2>&1 \ +# RUN: | FileCheck %s --strict-whitespace \ +# RUN: --implicit-check-not=SCRIPT_LOADED1 --implicit-check-not=SCRIPT_LOADED2 + +# Both dependent libraries should appear in a single warning. +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-DAG:{{^}} - {{.*}}/safe-path/libTestLib1/libTestLib1.py +# CHECK-DAG:{{^}} - {{.*}}/safe-path/libTestLib2/libTestLib2.py + +#--- main.c +extern int lib_func(void); +int main() { return lib_func(); } + +#--- lib.c +int lib_func(void) { return 0; } + +#--- script1.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("SCRIPT_LOADED1", file=sys.stderr) + +#--- script2.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("SCRIPT_LOADED2", file=sys.stderr) diff --git a/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-multiple-modules.test b/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-multiple-modules.test new file mode 100644 index 0000000000000..10c18583eb813 --- /dev/null +++ b/lldb/test/Shell/Platform/AutoLoad/UNIX/safe-path-warn-multiple-modules.test @@ -0,0 +1,55 @@ +# REQUIRES: python, asserts, !system-windows +# +# Test that when load-script-from-symbol-file is set to 'warn', LLDB emits a +# warning for each safe-path script without executing them. Verify that warnings +# are emitted for all modules, not just the first. + +# RUN: split-file %s %t +# RUN: %clang_host %t/main.c -o %t/TestModule.out +# RUN: %clang_host %t/main.c -o %t/TestModule2.out +# RUN: %clang_host %t/main.c -o %t/TestModule3.out +# RUN: mkdir -p %t/safe-path/TestModule +# RUN: mkdir -p %t/safe-path/TestModule2 +# RUN: mkdir -p %t/safe-path/TestModule3 + +# RUN: cp %t/script.py %t/safe-path/TestModule/TestModule.py +# RUN: cp %t/script2.py %t/safe-path/TestModule2/TestModule2.py +# RUN: cp %t/script3.py %t/safe-path/TestModule3/TestModule3.py +# RUN: %lldb -b \ +# RUN: -o 'settings set target.load-script-from-symbol-file warn' \ +# RUN: -o 'settings append testing.safe-auto-load-paths %t/safe-path' \ +# RUN: -o 'target create %t/TestModule.out' \ +# RUN: -o 'target modules add %t/TestModule2.out %t/TestModule3.out' 2>&1 \ +# RUN: | FileCheck %s --strict-whitespace \ +# RUN: --implicit-check-not=SCRIPT_LOADED --implicit-check-not=SCRIPT2_LOADED --implicit-check-not=SCRIPT3_LOADED + +# CHECK: (lldb) target create +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/safe-path/TestModule/TestModule.py + +# CHECK: (lldb) target modules add +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/safe-path/TestModule2/TestModule2.py +# CHECK: warning: {{.*}}The following debug scripts were detected but not loaded: +# CHECK-EMPTY: +# CHECK-NEXT:{{^}} - {{.*}}/safe-path/TestModule3/TestModule3.py + +#--- main.c +int main() { return 0; } + +#--- script.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("SCRIPT_LOADED", file=sys.stderr) + +#--- script2.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("SCRIPT2_LOADED", file=sys.stderr) + +#--- script3.py +import sys +def __lldb_init_module(debugger, internal_dict): + print("SCRIPT3_LOADED", file=sys.stderr)