diff --git a/lldb/bindings/headers.swig b/lldb/bindings/headers.swig --- a/lldb/bindings/headers.swig +++ b/lldb/bindings/headers.swig @@ -21,6 +21,7 @@ #include "lldb/API/SBData.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBEnvironment.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" diff --git a/lldb/bindings/interface/SBEnvironment.i b/lldb/bindings/interface/SBEnvironment.i new file mode 100644 --- /dev/null +++ b/lldb/bindings/interface/SBEnvironment.i @@ -0,0 +1,40 @@ +//===-- SWIG Interface for SBEnvironment-------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents the environment of a certain process. + +Example: + for entry in lldb.debugger.GetSelectedTarget().GetEnvironment().GetEntries(): + print(entry) + +") SBEnvironment; +class SBEnvironment { +public: + SBEnvironment (); + + SBEnvironment (const lldb::SBEnvironment &rhs); + + ~SBEnvironment(); + + size_t GetNumValues(); + + const char *Get(const char *name); + + const char *GetNameAtIndex(size_t index); + + const char *GetValueAtIndex(size_t index); + + bool Set(const char *name, const char *value, bool overwrite); + + bool Unset(const char *name); +}; + +} // namespace lldb diff --git a/lldb/bindings/interface/SBPlatform.i b/lldb/bindings/interface/SBPlatform.i --- a/lldb/bindings/interface/SBPlatform.i +++ b/lldb/bindings/interface/SBPlatform.i @@ -194,6 +194,9 @@ lldb::SBUnixSignals GetUnixSignals(); + lldb::SBEnvironment + GetEnvironment(); + }; } // namespace lldb diff --git a/lldb/bindings/interface/SBTarget.i b/lldb/bindings/interface/SBTarget.i --- a/lldb/bindings/interface/SBTarget.i +++ b/lldb/bindings/interface/SBTarget.i @@ -677,6 +677,9 @@ lldb::SBBreakpoint BreakpointCreateByAddress (addr_t address); + lldb::SBEnvironment + GetEnvironment(); + lldb::SBBreakpoint BreakpointCreateBySBAddress (SBAddress &sb_address); diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig --- a/lldb/bindings/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -29,6 +29,7 @@ %include "./interface/SBDebugger.i" %include "./interface/SBDeclaration.i" %include "./interface/SBError.i" +%include "./interface/SBEnvironment.i" %include "./interface/SBEvent.i" %include "./interface/SBExecutionContext.i" %include "./interface/SBExpressionOptions.i" diff --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h --- a/lldb/include/lldb/API/LLDB.h +++ b/lldb/include/lldb/API/LLDB.h @@ -24,6 +24,7 @@ #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBDefines.h" +#include "lldb/API/SBEnvironment.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -35,6 +35,7 @@ class LLDB_API SBData; class LLDB_API SBDebugger; class LLDB_API SBDeclaration; +class LLDB_API SBEnvironment; class LLDB_API SBError; class LLDB_API SBEvent; class LLDB_API SBEventList; diff --git a/lldb/include/lldb/API/SBEnvironment.h b/lldb/include/lldb/API/SBEnvironment.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/API/SBEnvironment.h @@ -0,0 +1,67 @@ +//===-- SBEnvironment.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SBENVIRONMENT_H +#define LLDB_API_SBENVIRONMENT_H + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class LLDB_API SBEnvironment { +public: + SBEnvironment(); + + SBEnvironment(const lldb::SBEnvironment &rhs); + + ~SBEnvironment(); + + const lldb::SBEnvironment &operator=(const lldb::SBEnvironment &rhs); + + /// Return the value of a given environment variable. + /// + /// \return + /// The value of the enviroment variable or null if not present. + const char *Get(const char *name); + + /// \return + /// The name at the given index or null if the index is invalid. + const char *GetNameAtIndex(size_t index); + + /// \return + /// The value at the given index or null if the index is invalid. + const char *GetValueAtIndex(size_t index); + + size_t GetNumValues(); + + /// Set the value of a given environment variable. + /// If the variable exists, its value is updated only if overwrite is true. + /// + /// \return + /// Return whether the variable was added or modified. + bool Set(const char *name, const char *value, bool overwrite); + + /// Unset an environment variable if exists. + /// + /// \return + /// Return whether a variable was actually unset. + bool Unset(const char *name); + +protected: + friend class SBPlatform; + friend class SBTarget; + + SBEnvironment(const lldb_private::Environment &rhs); + +private: + std::unique_ptr m_opaque_up; +}; + +} // namespace lldb + +#endif // LLDB_API_SBENVIRONMENT_H diff --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h --- a/lldb/include/lldb/API/SBPlatform.h +++ b/lldb/include/lldb/API/SBPlatform.h @@ -154,6 +154,14 @@ SBUnixSignals GetUnixSignals() const; + /// Return the environment variables of the remote platform connection + /// process. + /// + /// \return + /// An lldb::SBEnvironment object which is a copy of the platform's + /// enviroment. + SBEnvironment GetEnvironment(); + protected: friend class SBDebugger; friend class SBTarget; diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -94,6 +94,15 @@ /// A platform object. lldb::SBPlatform GetPlatform(); + /// Return the environment variables that would be used to launch a new + /// process. + /// + /// \return + /// An lldb::SBEnvironment object which is a copy of the target's + /// environment. + + SBEnvironment GetEnvironment(); + /// Install any binaries that need to be installed. /// /// This function does nothing when debugging on the host system. diff --git a/lldb/include/lldb/Utility/Environment.h b/lldb/include/lldb/Utility/Environment.h --- a/lldb/include/lldb/Utility/Environment.h +++ b/lldb/include/lldb/Utility/Environment.h @@ -50,6 +50,7 @@ using Base::erase; using Base::find; using Base::insert; + using Base::insert_or_assign; using Base::lookup; using Base::size; using Base::try_emplace; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -76,6 +76,7 @@ class DynamicLoader; class Editline; class EmulateInstruction; +class Environment; class EvaluateExpressionOptions; class Event; class EventData; diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -35,6 +35,7 @@ SBData.cpp SBDebugger.cpp SBDeclaration.cpp + SBEnvironment.cpp SBError.cpp SBEvent.cpp SBExecutionContext.cpp diff --git a/lldb/source/API/SBEnvironment.cpp b/lldb/source/API/SBEnvironment.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/API/SBEnvironment.cpp @@ -0,0 +1,111 @@ +//===-- SBEnvironment.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBEnvironment.h" +#include "SBReproducerPrivate.h" +#include "Utils.h" +#include "lldb/API/SBStringList.h" +#include "lldb/Utility/Environment.h" + +using namespace lldb; +using namespace lldb_private; + +SBEnvironment::SBEnvironment() : m_opaque_up(new Environment()) { + LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBEnvironment); +} + +SBEnvironment::SBEnvironment(const SBEnvironment &rhs) + : m_opaque_up(new Environment()) { + LLDB_RECORD_CONSTRUCTOR(SBEnvironment, (const lldb::SBEnvironment &), rhs); + + m_opaque_up = clone(rhs.m_opaque_up); +} + +SBEnvironment::SBEnvironment(const Environment &rhs) + : m_opaque_up(new Environment(rhs)) {} + +SBEnvironment::~SBEnvironment() = default; + +const SBEnvironment &SBEnvironment::operator=(const SBEnvironment &rhs) { + LLDB_RECORD_METHOD(const lldb::SBEnvironment &, + SBEnvironment, operator=,(const lldb::SBEnvironment &), + rhs); + + if (this != &rhs) + m_opaque_up = clone(rhs.m_opaque_up); + return LLDB_RECORD_RESULT(*this); +} + +size_t SBEnvironment::GetNumValues() { + LLDB_RECORD_METHOD_NO_ARGS(size_t, SBEnvironment, GetNumValues); + return LLDB_RECORD_RESULT(m_opaque_up->size()); +} + +const char *SBEnvironment::Get(const char *name) { + LLDB_RECORD_METHOD(const char *, SBEnvironment, Get, (const char *), name); + if (m_opaque_up->find(name) == m_opaque_up->end()) + return LLDB_RECORD_RESULT(nullptr); + return LLDB_RECORD_RESULT( + ConstString(m_opaque_up->lookup(name)).AsCString("")); +} + +const char *SBEnvironment::GetNameAtIndex(size_t index) { + LLDB_RECORD_METHOD(const char *, SBEnvironment, GetNameAtIndex, (size_t), + index); + if (index < 0 || index >= GetNumValues()) + return LLDB_RECORD_RESULT(nullptr); + return LLDB_RECORD_RESULT( + ConstString(std::next(m_opaque_up->begin(), index)->first()) + .AsCString("")); +} + +const char *SBEnvironment::GetValueAtIndex(size_t index) { + LLDB_RECORD_METHOD(const char *, SBEnvironment, GetValueAtIndex, (size_t), + index); + if (index < 0 || index >= GetNumValues()) + return LLDB_RECORD_RESULT(nullptr); + return LLDB_RECORD_RESULT( + ConstString(std::next(m_opaque_up->begin(), index)->second) + .AsCString("")); +} + +bool SBEnvironment::Set(const char *name, const char *value, bool overwrite) { + LLDB_RECORD_METHOD(bool, SBEnvironment, Set, + (const char *, const char *, bool), name, value, + overwrite); + if (overwrite || m_opaque_up->find(name) == m_opaque_up->end()) { + m_opaque_up->insert_or_assign(name, std::string(value)); + return LLDB_RECORD_RESULT(true); + } + return LLDB_RECORD_RESULT(false); +} + +bool SBEnvironment::Unset(const char *name) { + LLDB_RECORD_METHOD(bool, SBEnvironment, Unset, (const char *), name); + return LLDB_RECORD_RESULT(m_opaque_up->erase(name)); +} + +namespace lldb_private { +namespace repro { + +template <> void RegisterMethods(Registry &R) { + LLDB_REGISTER_CONSTRUCTOR(SBEnvironment, ()); + LLDB_REGISTER_CONSTRUCTOR(SBEnvironment, (const lldb::SBEnvironment &)); + LLDB_REGISTER_METHOD(const lldb::SBEnvironment &, + SBEnvironment, operator=,(const lldb::SBEnvironment &)); + LLDB_REGISTER_METHOD(size_t, SBEnvironment, GetNumValues, ()); + LLDB_REGISTER_METHOD(const char *, SBEnvironment, GetNameAtIndex, (size_t)); + LLDB_REGISTER_METHOD(const char *, SBEnvironment, GetValueAtIndex, (size_t)); + LLDB_REGISTER_METHOD(const char *, SBEnvironment, Get, (const char *)); + LLDB_REGISTER_METHOD(bool, SBEnvironment, Set, + (const char *, const char *, bool)); + LLDB_REGISTER_METHOD(bool, SBEnvironment, Unset, (const char *)); +} + +} // namespace repro +} // namespace lldb_private diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp --- a/lldb/source/API/SBPlatform.cpp +++ b/lldb/source/API/SBPlatform.cpp @@ -8,9 +8,11 @@ #include "lldb/API/SBPlatform.h" #include "SBReproducerPrivate.h" +#include "lldb/API/SBEnvironment.h" #include "lldb/API/SBError.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBLaunchInfo.h" +#include "lldb/API/SBPlatform.h" #include "lldb/API/SBUnixSignals.h" #include "lldb/Host/File.h" #include "lldb/Target/Platform.h" @@ -649,6 +651,17 @@ return LLDB_RECORD_RESULT(SBUnixSignals()); } +SBEnvironment SBPlatform::GetEnvironment() { + LLDB_RECORD_METHOD_NO_ARGS(lldb::SBEnvironment, SBPlatform, GetEnvironment); + PlatformSP platform_sp(GetSP()); + + if (platform_sp) { + return LLDB_RECORD_RESULT(SBEnvironment(platform_sp->GetEnvironment())); + } + + return LLDB_RECORD_RESULT(SBEnvironment()); +} + namespace lldb_private { namespace repro { @@ -740,6 +753,7 @@ (const char *)); LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, SetFilePermissions, (const char *, uint32_t)); + LLDB_REGISTER_METHOD(lldb::SBEnvironment, SBPlatform, GetEnvironment, ()); LLDB_REGISTER_METHOD_CONST(lldb::SBUnixSignals, SBPlatform, GetUnixSignals, ()); } diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -13,6 +13,7 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEnvironment.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" @@ -2388,6 +2389,17 @@ m_opaque_sp->SetProcessLaunchInfo(launch_info.ref()); } +SBEnvironment SBTarget::GetEnvironment() { + LLDB_RECORD_METHOD_NO_ARGS(lldb::SBEnvironment, SBTarget, GetEnvironment); + TargetSP target_sp(GetSP()); + + if (target_sp) { + return LLDB_RECORD_RESULT(SBEnvironment(target_sp->GetEnvironment())); + } + + return LLDB_RECORD_RESULT(SBEnvironment()); +} + namespace lldb_private { namespace repro { @@ -2643,6 +2655,7 @@ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, GetInstructionsWithFlavor, (lldb::addr_t, const char *, const void *, size_t)); + LLDB_REGISTER_METHOD(lldb::SBEnvironment, SBTarget, GetEnvironment, ()); } } diff --git a/lldb/test/API/python_api/sbenvironment/TestSBEnvironment.py b/lldb/test/API/python_api/sbenvironment/TestSBEnvironment.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/python_api/sbenvironment/TestSBEnvironment.py @@ -0,0 +1,86 @@ +"""Test the SBEnvironment APIs.""" + + + +from math import fabs +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +def forEachEntry(env, callback): + for i in range(0, env.GetNumValues()): + name = env.GetNameAtIndex(i) + value = env.GetValueAtIndex(i) + callback(name, value) + + +class SBEnvironmentAPICase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + + @add_test_categories(['pyapi']) + def test_platform_environment(self): + env = self.dbg.GetSelectedPlatform().GetEnvironment() + # We assume at least PATH is set + self.assertNotEqual(env.Get("PATH"), None) + + + @add_test_categories(['pyapi']) + def test_target_environment(self): + env = self.dbg.GetSelectedTarget().GetEnvironment() + # There is no target, so env should be empty + self.assertEqual(env.GetNumValues(), 0) + self.assertEqual(env.Get("PATH"), None) + + target = self.dbg.CreateTarget("") + env = target.GetEnvironment() + path = env.Get("PATH") + # Now there's a target, so at least PATH should exist + self.assertNotEqual(path, None) + + # Make sure we are getting a copy by modifying the env we just got + env.Set("PATH", "#" + path, overwrite=True) + self.assertEqual(target.GetEnvironment().Get("PATH"), path) + + @add_test_categories(['pyapi']) + def test_creating_and_modifying_environment(self): + env = lldb.SBEnvironment() + + self.assertEqual(env.Get("FOO"), None) + self.assertEqual(env.Get("BAR"), None) + + # We also test empty values + self.assertTrue(env.Set("FOO", "", overwrite=False)) + env.Set("BAR", "foo", overwrite=False) + + self.assertEqual(env.Get("FOO"), "") + self.assertEqual(env.Get("BAR"), "foo") + + self.assertEqual(env.GetNumValues(), 2) + + forEachEntry( + env, + lambda name, value: + self.assertIn(name + '=' + value, ["FOO=", "BAR=foo"]) + ) + + # Make sure modifications work + self.assertFalse(env.Set("FOO", "bar", overwrite=False)) + self.assertEqual(env.Get("FOO"), "") + + self.assertTrue(env.Set("FOO", "bar", overwrite=True)) + self.assertEqual(env.Get("FOO"), "bar") + + forEachEntry( + env, + lambda name, value: + self.assertIn(name + '=' + value, ["FOO=bar", "BAR=foo"]) + ) + + # Make sure we can unset + self.assertTrue(env.Unset("FOO")) + self.assertFalse(env.Unset("FOO")) + self.assertEqual(env.Get("FOO"), None) \ No newline at end of file