Hi clayborg, emaste,

In order to write applications with the lldb C++ API which support non-8-bit 
bytes targets, it is necessary to extend the API somewhat.

To use lldb's ReadMemory interface, which accepts a buffer sized by the host's 
definition of a byte, I have added the following "discovery" functions:

SBSection::GetTargetByteSize
SBTarget::GetCodeByteSize
SBTarget::GetDataByteSize

additionally I have added SBTarget::ResolveFileAddress, which resolves a file 
address to a section. With the above discovery functions, my test C++ 
application, can correctly deduce the host buffer allocation necessary, and 
decode the read data, for an address read on the target. 

I added SBTarget::ReadMemory too, so that my test application can read memory 
(straight from the object file) without a running process. This function merely 
being a wrapper on the existent Target::ReadMemory.

(As an aside I added SBTarget::GetPlatform, since I found this useful as a 
sanity check when using my test C++ application.)

For each C++ API routine I have added a test case. I've run these case on linux 
and FreeBSD to my satisfaction. I've assumed that these tests will work on 
MACH-O, so I did not decorate them with "@dwarf_test". Feel free to comment if 
this is considered to be a poor omission.

http://reviews.llvm.org/D5867

Files:
  include/lldb/API/SBSection.h
  include/lldb/API/SBTarget.h
  include/lldb/Target/Target.h
  scripts/Python/interface/SBSection.i
  scripts/Python/interface/SBTarget.i
  source/API/SBSection.cpp
  source/API/SBTarget.cpp
  source/Target/Target.cpp
  test/lldbtest.py
  test/python_api/section/Makefile
  test/python_api/section/TestSectionAPI.py
  test/python_api/section/main.c
  test/python_api/target/TestTargetAPI.py
  test/python_api/target/main.c
Index: include/lldb/API/SBSection.h
===================================================================
--- include/lldb/API/SBSection.h
+++ include/lldb/API/SBSection.h
@@ -71,6 +71,18 @@
     SectionType
     GetSectionType ();
 
+    //------------------------------------------------------------------
+    /// Return the size of a target's byte represented by this section
+    /// in numbers of host bytes. Note that certain architectures have
+    /// varying minimum addressable unit (i.e. byte) size for their 
+    /// CODE or DATA buses.
+    ///
+    /// @return
+    ///     The number of host (8-bit) bytes needed to hold a target byte
+    //------------------------------------------------------------------
+    uint32_t
+    GetTargetByteSize ();
+
     bool
     operator == (const lldb::SBSection &rhs);
 
Index: include/lldb/API/SBTarget.h
===================================================================
--- include/lldb/API/SBTarget.h
+++ include/lldb/API/SBTarget.h
@@ -22,6 +22,8 @@
 
 namespace lldb {
 
+class SBPlatform;
+
 class SBLaunchInfo
 {
 public:
@@ -309,6 +311,18 @@
     GetProcess ();
 
     //------------------------------------------------------------------
+    /// Return the platform object associated with the target.
+    ///
+    /// After return, the platform object should be checked for
+    /// validity.
+    ///
+    /// @return
+    ///     A platform object.
+    //------------------------------------------------------------------
+    lldb::SBPlatform
+    GetPlatform ();
+
+    //------------------------------------------------------------------
     /// Install any binaries that need to be installed.
     ///
     /// This function does nothing when debugging on the host system.
@@ -564,6 +578,26 @@
     GetTriple ();
 
     //------------------------------------------------------------------
+    /// Architecture data byte width accessor
+    ///
+    /// @return
+    /// The size in 8-bit (host) bytes of a minimum addressable
+    /// unit from the Architecture's data bus
+    //------------------------------------------------------------------
+    uint32_t
+    GetDataByteSize ();
+
+    //------------------------------------------------------------------
+    /// Architecture code byte width accessor
+    ///
+    /// @return
+    /// The size in 8-bit (host) bytes of a minimum addressable
+    /// unit from the Architecture's code bus
+    //------------------------------------------------------------------
+    uint32_t
+    GetCodeByteSize ();
+
+    //------------------------------------------------------------------
     /// Set the base load address for a module section.
     ///
     /// @param[in] section
@@ -728,6 +762,17 @@
     Clear ();
 
     //------------------------------------------------------------------
+    /// Resolve a current file address into a section offset address.
+    ///
+    /// @param[in] file_addr
+    ///
+    /// @return
+    ///     An SBAddress which will be valid if...
+    //------------------------------------------------------------------
+    lldb::SBAddress
+    ResolveFileAddress (lldb::addr_t file_addr);
+
+    //------------------------------------------------------------------
     /// Resolve a current load address into a section offset address.
     ///
     /// @param[in] vm_addr
@@ -772,6 +817,31 @@
     ResolveSymbolContextForAddress (const SBAddress& addr, 
                                     uint32_t resolve_scope);
 
+    //------------------------------------------------------------------
+    /// Read target memory. If a target process is running then memory  
+    /// is read from here. Otherwise the memory is read from the object
+    /// files. For a target whose bytes are sized as a multiple of host
+    /// bytes, the data read back will preserve the target's byte order.
+    ///
+    /// @param[in] addr
+    ///     A target address to read from. 
+    ///
+    /// @param[out] buf
+    ///     The buffer to read memory into. 
+    ///
+    /// @param[in] size
+    ///     The maximum number of host bytes to read in the buffer passed
+    ///     into this call
+    ///
+    /// @param[out] error
+    ///     Error information is written here if the memory read fails.
+    ///
+    /// @return
+    ///     The amount of data read in host bytes.
+    //------------------------------------------------------------------
+    size_t
+    ReadMemory (const SBAddress addr, void *buf, size_t size, lldb::SBError &error);
+
     lldb::SBBreakpoint
     BreakpointCreateByLocation (const char *file, uint32_t line);
 
Index: include/lldb/Target/Target.h
===================================================================
--- include/lldb/Target/Target.h
+++ include/lldb/Target/Target.h
@@ -1132,6 +1132,9 @@
     Error
     Install(ProcessLaunchInfo *launch_info);
     
+    bool
+    ResolveFileAddress (lldb::addr_t load_addr,
+                        Address &so_addr);
     
     bool
     ResolveLoadAddress (lldb::addr_t load_addr,
Index: scripts/Python/interface/SBSection.i
===================================================================
--- scripts/Python/interface/SBSection.i
+++ scripts/Python/interface/SBSection.i
@@ -90,6 +90,20 @@
     SectionType
     GetSectionType ();
 
+    %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Return the size of a target's byte represented by this section
+    /// in numbers of host bytes. Note that certain architectures have
+    /// varying minimum addressable unit (i.e. byte) size for their 
+    /// CODE or DATA buses.
+    ///
+    /// @return
+    ///     The number of host (8-bit) bytes needed to hold a target byte
+    //------------------------------------------------------------------
+    ") GetTargetByteSize;
+    uint32_t
+    GetTargetByteSize ();
+
     bool
     GetDescription (lldb::SBStream &description);
     
@@ -127,6 +141,9 @@
         __swig_getmethods__["type"] = GetSectionType
         if _newclass: type = property(GetSectionType, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eSectionType") that represents the type of this section (code, data, etc.).''')
 
+        __swig_getmethods__["target_byte_size"] = GetTargetByteSize
+        if _newclass: target_byte_size = property(GetTargetByteSize, None, doc='''A read only property that returns the size of a target byte represented by this section as a number of host bytes.''')
+
     %}
 
 private:
Index: scripts/Python/interface/SBTarget.i
===================================================================
--- scripts/Python/interface/SBTarget.i
+++ scripts/Python/interface/SBTarget.i
@@ -282,6 +282,21 @@
     lldb::SBProcess
     GetProcess ();
 
+
+    %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Return the platform object associated with the target.
+    ///
+    /// After return, the platform object should be checked for
+    /// validity.
+    ///
+    /// @return
+    ///     A platform object.
+    //------------------------------------------------------------------
+    ") GetPlatform;
+    lldb::SBPlatform
+    GetPlatform ();
+
     %feature("docstring", "
     //------------------------------------------------------------------
     /// Install any binaries that need to be installed.
@@ -579,6 +594,30 @@
     const char *
     GetTriple ();
 
+    %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Architecture data byte width accessor
+    ///
+    /// @return
+    /// The size in 8-bit (host) bytes of a minimum addressable
+    /// unit from the Architecture's data bus
+    //------------------------------------------------------------------
+    ") GetDataByteSize;
+    uint32_t
+    GetDataByteSize ();
+
+    %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Architecture code byte width accessor
+    ///
+    /// @return
+    /// The size in 8-bit (host) bytes of a minimum addressable
+    /// unit from the Architecture's code bus
+    //------------------------------------------------------------------
+    ") GetCodeByteSize;
+    uint32_t
+    GetCodeByteSize ();
+
     lldb::SBError
     SetSectionLoadAddress (lldb::SBSection section,
                            lldb::addr_t section_base_addr);
@@ -676,6 +715,19 @@
     void
     Clear ();
 
+     %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Resolve a current file address into a section offset address.
+    ///
+    /// @param[in] file_addr
+    ///
+    /// @return
+    ///     An SBAddress which will be valid if...
+    //------------------------------------------------------------------
+    ") ResolveFileAddress;
+    lldb::SBAddress
+    ResolveFileAddress (lldb::addr_t file_addr);
+
     lldb::SBAddress
     ResolveLoadAddress (lldb::addr_t vm_addr);
               
@@ -686,6 +738,33 @@
     ResolveSymbolContextForAddress (const SBAddress& addr, 
                                     uint32_t resolve_scope);
 
+     %feature("docstring", "
+    //------------------------------------------------------------------
+    /// Read target memory. If a target process is running then memory  
+    /// is read from here. Otherwise the memory is read from the object
+    /// files. For a target whose bytes are sized as a multiple of host
+    /// bytes, the data read back will preserve the target's byte order.
+    ///
+    /// @param[in] addr
+    ///     A target address to read from. 
+    ///
+    /// @param[out] buf
+    ///     The buffer to read memory into. 
+    ///
+    /// @param[in] size
+    ///     The maximum number of host bytes to read in the buffer passed
+    ///     into this call
+    ///
+    /// @param[out] error
+    ///     Error information is written here if the memory read fails.
+    ///
+    /// @return
+    ///     The amount of data read in host bytes.
+    //------------------------------------------------------------------
+    ") ReadMemory;
+    size_t
+    ReadMemory (const SBAddress addr, void *buf, size_t size, lldb::SBError &error);
+
     lldb::SBBreakpoint
     BreakpointCreateByLocation (const char *file, uint32_t line);
 
@@ -904,6 +983,15 @@
         
         __swig_getmethods__["triple"] = GetTriple
         if _newclass: triple = property(GetTriple, None, doc='''A read only property that returns the target triple (arch-vendor-os) for this target as a string.''')
+
+        __swig_getmethods__["data_byte_size"] = GetDataByteSize
+        if _newclass: addr_size = property(GetDataByteSize, None, doc='''A read only property that returns the size in host bytes of a byte in the data address space for this target.''')
+
+        __swig_getmethods__["code_byte_size"] = GetCodeByteSize
+        if _newclass: addr_size = property(GetCodeByteSize, None, doc='''A read only property that returns the size in host bytes of a byte in the code address space for this target.''')
+
+        __swig_getmethods__["platform"] = GetPlatform
+        if _newclass: platform = property(GetPlatform, None, doc='''A read only property that returns the platform associated with with this target.''')
     %}
 
 };
Index: source/API/SBSection.cpp
===================================================================
--- source/API/SBSection.cpp
+++ source/API/SBSection.cpp
@@ -250,6 +250,14 @@
     return eSectionTypeInvalid;
 }
 
+uint32_t
+SBSection::GetTargetByteSize ()
+{
+    SectionSP section_sp (GetSP());
+    if (section_sp.get())
+        return section_sp->GetTargetByteSize();
+    return 0;
+}
 
 bool
 SBSection::operator == (const SBSection &rhs)
Index: source/API/SBTarget.cpp
===================================================================
--- source/API/SBTarget.cpp
+++ source/API/SBTarget.cpp
@@ -584,6 +584,19 @@
     return sb_process;
 }
 
+SBPlatform
+SBTarget::GetPlatform ()
+{
+    TargetSP target_sp(GetSP());
+    if (!target_sp)
+        return SBPlatform();
+
+    SBPlatform platform;
+    platform.m_opaque_sp = target_sp->GetPlatform();
+
+    return platform;
+}
+
 SBDebugger
 SBTarget::GetDebugger () const
 {
@@ -1228,6 +1241,22 @@
     return sb_addr;
 }
 
+lldb::SBAddress
+SBTarget::ResolveFileAddress (lldb::addr_t file_addr)
+{
+    lldb::SBAddress sb_addr;
+    Address &addr = sb_addr.ref();
+    TargetSP target_sp(GetSP());
+    if (target_sp)
+    {
+        Mutex::Locker api_locker (target_sp->GetAPIMutex());
+        if (target_sp->ResolveFileAddress (file_addr, addr))
+            return sb_addr;
+    }
+
+    addr.SetRawAddress(file_addr);
+    return sb_addr;
+}
 
 lldb::SBAddress
 SBTarget::ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr)
@@ -1262,6 +1291,29 @@
     return sc;
 }
 
+size_t
+SBTarget::ReadMemory (const SBAddress addr,
+                      void *buf,
+                      size_t size,
+                      lldb::SBError &error)
+{
+    SBError sb_error;
+    size_t bytes_read = 0;
+    TargetSP target_sp(GetSP());
+    if (target_sp)
+    {
+        Mutex::Locker api_locker (target_sp->GetAPIMutex());
+        lldb_private::Address addr_priv(addr.GetFileAddress(), NULL);
+        lldb_private::Error err_priv;    
+        bytes_read = target_sp->ReadMemory(addr_priv, false, buf, size, err_priv);
+        if(err_priv.Fail())
+        {
+            sb_error.SetError(err_priv.GetError(), err_priv.GetType());
+        }
+    }
+
+    return bytes_read;
+}
 
 SBBreakpoint
 SBTarget::BreakpointCreateByLocation (const char *file,
@@ -2058,6 +2110,28 @@
 }
 
 uint32_t
+SBTarget::GetDataByteSize ()
+{
+    TargetSP target_sp(GetSP());
+    if (target_sp)
+    {
+        return target_sp->GetArchitecture().GetDataByteSize() ;
+    }
+    return 0;
+}
+
+uint32_t
+SBTarget::GetCodeByteSize ()
+{
+    TargetSP target_sp(GetSP());
+    if (target_sp)
+    {
+        return target_sp->GetArchitecture().GetCodeByteSize() ;
+    }
+    return 0;
+}
+
+uint32_t
 SBTarget::GetAddressByteSize()
 {
     TargetSP target_sp(GetSP());
Index: source/Target/Target.cpp
===================================================================
--- source/Target/Target.cpp
+++ source/Target/Target.cpp
@@ -2283,6 +2283,12 @@
 }
 
 bool
+Target::ResolveFileAddress (lldb::addr_t file_addr, Address &resolved_addr)
+{
+    return m_images.ResolveFileAddress(file_addr, resolved_addr);
+}
+
+bool
 Target::SetSectionLoadAddress (const SectionSP &section_sp, addr_t new_section_load_addr, bool warn_multiple)
 {
     const addr_t old_section_load_addr = m_section_load_history.GetSectionLoadAddress (SectionLoadHistory::eStopIDNow, section_sp);
Index: test/lldbtest.py
===================================================================
--- test/lldbtest.py
+++ test/lldbtest.py
@@ -137,6 +137,8 @@
 
 VALID_TARGET = "Got a valid target"
 
+VALID_PLATFORM = "Got a valid platform"
+
 VALID_TYPE = "Got a valid type"
 
 VALID_VARIABLE = "Got a valid variable"
Index: test/python_api/section/Makefile
===================================================================
--- test/python_api/section/Makefile
+++ test/python_api/section/Makefile
@@ -0,0 +1,5 @@
+LEVEL = ../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
Index: test/python_api/section/TestSectionAPI.py
===================================================================
--- test/python_api/section/TestSectionAPI.py
+++ test/python_api/section/TestSectionAPI.py
@@ -0,0 +1,46 @@
+"""
+Test SBSection APIs.
+"""
+
+import unittest2
+from lldbtest import *
+
+class SectionAPITestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @python_api_test
+    def test_get_target_byte_size(self):
+        target = self.create_simple_default_target()
+
+        # find the .data section of the main module            
+        data_section = self.find_data_section(target)
+
+        self.assertEquals(data_section.target_byte_size, 1)
+
+    def create_simple_default_target(self):
+        d = {'EXE': 'c.out'}
+        self.buildDefault(dictionary=d)
+        self.setTearDownCleanup(dictionary=d)
+
+        exe = os.path.join(os.getcwd(), 'c.out')
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+        return target
+
+    def find_data_section(self, target):
+        mod = target.GetModuleAtIndex(0)
+        data_section = None
+        for s in mod.sections:
+            if ".data" == s.name:
+                data_section = s
+                break
+
+        self.assertIsNotNone(data_section)
+        return data_section
+        
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()
Index: test/python_api/section/main.c
===================================================================
--- test/python_api/section/main.c
+++ test/python_api/section/main.c
@@ -0,0 +1,28 @@
+//===-- main.c --------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+#include <string.h>
+
+// This simple program is to test the lldb Python API SBSection. It includes
+// somes global data, and so the build process produces a DATA section, which 
+// the test code can use to query for the target byte size
+
+char my_global_var_of_char_type = 'X';
+
+int main (int argc, char const *argv[])
+{
+    // this code just "does something" with the global so that it is not
+    // optimised away
+    if (argc > 1 && strlen(argv[1]))
+    {
+        my_global_var_of_char_type += argv[1][0];
+    }
+
+    return my_global_var_of_char_type;
+}
Index: test/python_api/target/TestTargetAPI.py
===================================================================
--- test/python_api/target/TestTargetAPI.py
+++ test/python_api/target/TestTargetAPI.py
@@ -104,12 +104,89 @@
         self.buildDwarf()
         self.resolve_symbol_context_with_address()
 
+    @python_api_test
+    def test_get_platform(self):
+        target = self.create_simple_default_target()
+        platform = target.platform
+        self.assertTrue(platform, VALID_PLATFORM)
+
+    @python_api_test
+    def test_get_data_byte_size(self):
+        target = self.create_simple_default_target()
+        self.assertEquals(target.data_byte_size, 1)
+
+    @python_api_test
+    def test_get_code_byte_size(self):
+        target = self.create_simple_default_target()
+        self.assertEquals(target.code_byte_size, 1)
+
+    @python_api_test
+    def test_resolve_file_address(self):
+        target = self.create_simple_default_target()
+
+        # find the file address in the .data section of the main
+        # module            
+        data_section = self.find_data_section(target)
+        data_section_addr = data_section.file_addr
+
+        # resolve the above address, and compare the address produced
+        # by the resolution against the original address/section       
+        res_file_addr = target.ResolveFileAddress(data_section_addr)
+        self.assertTrue(res_file_addr.IsValid())
+
+        self.assertEquals(data_section_addr, res_file_addr.file_addr) 
+
+        data_section2 = res_file_addr.section
+        self.assertIsNotNone(data_section2)
+        self.assertEquals(data_section.name, data_section2.name) 
+
+    @python_api_test
+    def test_read_memory(self):
+        target = self.create_simple_default_target()
+
+        breakpoint = target.BreakpointCreateByLocation("main.c", self.line_main)
+        self.assertTrue(breakpoint, VALID_BREAKPOINT)
+
+        # Launch the process, and do not stop at the entry point.
+        process = target.LaunchSimple (None, None, self.get_process_working_directory())
+
+        # find the file address in the .data section of the main
+        # module            
+        data_section = self.find_data_section(target)
+        data_section_addr = data_section.file_addr
+        a = target.ResolveFileAddress(data_section_addr)
+
+        content = target.ReadMemory(a, 1, lldb.SBError())
+        self.assertEquals(len(content), 1)
+
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
         # Find the line number to of function 'c'.
         self.line1 = line_number('main.c', '// Find the line number for breakpoint 1 here.')
         self.line2 = line_number('main.c', '// Find the line number for breakpoint 2 here.')
+        self.line_main = line_number("main.c", "// Set a break at entry to main.")
+
+    def create_simple_default_target(self):
+        d = {'EXE': 'c.out'}
+        self.buildDefault(dictionary=d)
+        self.setTearDownCleanup(dictionary=d)
+
+        exe = os.path.join(os.getcwd(), 'c.out')
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+        return target
+
+    def find_data_section(self, target):
+        mod = target.GetModuleAtIndex(0)
+        data_section = None
+        for s in mod.sections:
+            if ".data" == s.name:
+                data_section = s
+                break
+
+        self.assertIsNotNone(data_section)
+        return data_section
 
     def find_global_variables(self, exe_name):
         """Exercise SBTaget.FindGlobalVariables() API."""
Index: test/python_api/target/main.c
===================================================================
--- test/python_api/target/main.c
+++ test/python_api/target/main.c
@@ -46,6 +46,7 @@
 
 int main (int argc, char const *argv[])
 {
+    // Set a break at entry to main.
     int A1 = a(1);  // a(1) -> b(1) -> c(1)
     printf("a(1) returns %d\n", A1);
     
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits

Reply via email to