Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lldb/include/lldb/Core/ModuleList.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> &warned_script_paths);

public:
typedef LockingAdaptedIterable<std::recursive_mutex, collection>
Expand Down
61 changes: 40 additions & 21 deletions lldb/source/Core/ModuleList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -1330,6 +1332,33 @@ bool ModuleList::RemoveSharedModuleIfOrphaned(const ModuleWP module_wp) {
return GetSharedModuleList().RemoveIfOrphaned(module_wp);
}

static void
ReportScriptLoading(Debugger &debugger,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, what I had in mind for this in #190407 is a little table for the given module. Something like:

Foo contains debug script(s). To run a script in this debug session, use `command script import`

trusted   command
---------------------------------------------
yes       command script import /path/to/a.py
no        command script import /path/to/b.py

To run all discovered debug scripts in this session:

    settings set target.load-script-from-symbol-file true

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue is that this will get printed on every module script load attempt. If there's a lot of them then printing the instructions To run all discovered debug scripts... feels noisy.

But will have a think of how to best present it. I do like the table

llvm::ArrayRef<std::string> warned_script_paths) {
if (warned_script_paths.empty())
return;

static std::once_flag s_warn_once;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems (very) wrong. Shouldn't the once_flag be a member for the module it belongs to?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was to only report the first warning once per debugger session. (note there are 2 ReportWarning here)


// clang-format off

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this right before the string to keep its coverage as small as possible.

debugger.ReportWarning(
R"(Found executable debug scripts. To run a script in this debug session:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to find a good way of threading the trusted/untrusted through. Maybe passing a llvm::ArrayRef<ModuleScriptLoadInfo>, which holds that info


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) {
Expand All @@ -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<std::string> &warned_script_paths) {
Log *log = GetLog(LLDBLog::Modules);

Debugger &debugger = target.GetDebugger();
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -1437,10 +1450,13 @@ bool ModuleList::LoadScriptingResourcesInTarget(Target *target,
const ModuleList tmp_module_list(*this);
m_modules_mutex.unlock();

std::vector<std::string> 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 "
Expand All @@ -1456,6 +1472,9 @@ bool ModuleList::LoadScriptingResourcesInTarget(Target *target,
}
}
}

ReportScriptLoading(target->GetDebugger(), warned_script_paths);

return errors.empty();
}

Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <memory>
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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)