diff options
Diffstat (limited to 'source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp')
| -rw-r--r-- | source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp | 3171 | 
1 files changed, 2951 insertions, 220 deletions
| diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp index 2490cf31409b6..149244df30c22 100644 --- a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -7,6 +7,10 @@  //  //===----------------------------------------------------------------------===// +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes  #include "RenderScriptRuntime.h"  #include "lldb/Core/ConstString.h" @@ -14,10 +18,15 @@  #include "lldb/Core/Error.h"  #include "lldb/Core/Log.h"  #include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" +#include "lldb/Host/StringConvert.h"  #include "lldb/Symbol/Symbol.h"  #include "lldb/Symbol/Type.h"  #include "lldb/Target/Process.h"  #include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h"  #include "lldb/Interpreter/Args.h"  #include "lldb/Interpreter/Options.h"  #include "lldb/Interpreter/CommandInterpreter.h" @@ -25,11 +34,350 @@  #include "lldb/Interpreter/CommandObjectMultiword.h"  #include "lldb/Breakpoint/StoppointCallbackContext.h"  #include "lldb/Target/RegisterContext.h" - +#include "lldb/Expression/UserExpression.h"  #include "lldb/Symbol/VariableList.h"  using namespace lldb;  using namespace lldb_private; +using namespace lldb_renderscript; + +namespace { + +// The empirical_type adds a basic level of validation to arbitrary data +// allowing us to track if data has been discovered and stored or not. +// An empirical_type will be marked as valid only if it has been explicitly assigned to. +template <typename type_t> +class empirical_type +{ +public: +    // Ctor. Contents is invalid when constructed. +    empirical_type() +        : valid(false) +    {} + +    // Return true and copy contents to out if valid, else return false. +    bool get(type_t& out) const +    { +        if (valid) +            out = data; +        return valid; +    } + +    // Return a pointer to the contents or nullptr if it was not valid. +    const type_t* get() const +    { +        return valid ? &data : nullptr; +    } + +    // Assign data explicitly. +    void set(const type_t in) +    { +        data = in; +        valid = true; +    } + +    // Mark contents as invalid. +    void invalidate() +    { +        valid = false; +    } + +    // Returns true if this type contains valid data. +    bool isValid() const +    { +        return valid; +    } + +    // Assignment operator. +    empirical_type<type_t>& operator = (const type_t in) +    { +        set(in); +        return *this; +    } + +    // Dereference operator returns contents. +    // Warning: Will assert if not valid so use only when you know data is valid. +    const type_t& operator * () const +    { +        assert(valid); +        return data; +    } + +protected: +    bool valid; +    type_t data; +}; + +} // anonymous namespace + +// The ScriptDetails class collects data associated with a single script instance. +struct RenderScriptRuntime::ScriptDetails +{ +    ~ScriptDetails() = default; + +    enum ScriptType +    { +        eScript, +        eScriptC +    }; + +    // The derived type of the script. +    empirical_type<ScriptType> type; +    // The name of the original source file. +    empirical_type<std::string> resName; +    // Path to script .so file on the device. +    empirical_type<std::string> scriptDyLib; +    // Directory where kernel objects are cached on device. +    empirical_type<std::string> cacheDir; +    // Pointer to the context which owns this script. +    empirical_type<lldb::addr_t> context; +    // Pointer to the script object itself. +    empirical_type<lldb::addr_t> script; +}; + +// This Element class represents the Element object in RS, +// defining the type associated with an Allocation. +struct RenderScriptRuntime::Element +{ +    // Taken from rsDefines.h +    enum DataKind +    { +        RS_KIND_USER, +        RS_KIND_PIXEL_L = 7, +        RS_KIND_PIXEL_A, +        RS_KIND_PIXEL_LA, +        RS_KIND_PIXEL_RGB, +        RS_KIND_PIXEL_RGBA, +        RS_KIND_PIXEL_DEPTH, +        RS_KIND_PIXEL_YUV, +        RS_KIND_INVALID = 100 +    }; + +    // Taken from rsDefines.h +    enum DataType +    { +        RS_TYPE_NONE = 0, +        RS_TYPE_FLOAT_16, +        RS_TYPE_FLOAT_32, +        RS_TYPE_FLOAT_64, +        RS_TYPE_SIGNED_8, +        RS_TYPE_SIGNED_16, +        RS_TYPE_SIGNED_32, +        RS_TYPE_SIGNED_64, +        RS_TYPE_UNSIGNED_8, +        RS_TYPE_UNSIGNED_16, +        RS_TYPE_UNSIGNED_32, +        RS_TYPE_UNSIGNED_64, +        RS_TYPE_BOOLEAN, + +        RS_TYPE_UNSIGNED_5_6_5, +        RS_TYPE_UNSIGNED_5_5_5_1, +        RS_TYPE_UNSIGNED_4_4_4_4, + +        RS_TYPE_MATRIX_4X4, +        RS_TYPE_MATRIX_3X3, +        RS_TYPE_MATRIX_2X2, + +        RS_TYPE_ELEMENT = 1000, +        RS_TYPE_TYPE, +        RS_TYPE_ALLOCATION, +        RS_TYPE_SAMPLER, +        RS_TYPE_SCRIPT, +        RS_TYPE_MESH, +        RS_TYPE_PROGRAM_FRAGMENT, +        RS_TYPE_PROGRAM_VERTEX, +        RS_TYPE_PROGRAM_RASTER, +        RS_TYPE_PROGRAM_STORE, +        RS_TYPE_FONT, + +        RS_TYPE_INVALID = 10000 +    }; + +    std::vector<Element> children;                       // Child Element fields for structs +    empirical_type<lldb::addr_t> element_ptr;            // Pointer to the RS Element of the Type +    empirical_type<DataType> type;                       // Type of each data pointer stored by the allocation +    empirical_type<DataKind> type_kind;                  // Defines pixel type if Allocation is created from an image +    empirical_type<uint32_t> type_vec_size;              // Vector size of each data point, e.g '4' for uchar4 +    empirical_type<uint32_t> field_count;                // Number of Subelements +    empirical_type<uint32_t> datum_size;                 // Size of a single Element with padding +    empirical_type<uint32_t> padding;                    // Number of padding bytes +    empirical_type<uint32_t> array_size;                 // Number of items in array, only needed for strucrs +    ConstString type_name;                               // Name of type, only needed for structs + +    static const ConstString &GetFallbackStructName();   // Print this as the type name of a struct Element +                                                         // If we can't resolve the actual struct name + +    bool shouldRefresh() const +    { +        const bool valid_ptr = element_ptr.isValid() && *element_ptr.get() != 0x0; +        const bool valid_type = type.isValid() && type_vec_size.isValid() && type_kind.isValid(); +        return !valid_ptr || !valid_type || !datum_size.isValid(); +    } +}; + +// This AllocationDetails class collects data associated with a single +// allocation instance. +struct RenderScriptRuntime::AllocationDetails +{ +    struct Dimension +    { +        uint32_t dim_1; +        uint32_t dim_2; +        uint32_t dim_3; +        uint32_t cubeMap; + +        Dimension() +        { +             dim_1 = 0; +             dim_2 = 0; +             dim_3 = 0; +             cubeMap = 0; +        } +    }; + +    // Header for reading and writing allocation contents +    // to a binary file. +    struct FileHeader +    { +        uint8_t ident[4];      // ASCII 'RSAD' identifying the file +        uint16_t hdr_size;     // Header size in bytes, for backwards compatability +        uint16_t type;         // DataType enum +        uint32_t kind;         // DataKind enum +        uint32_t dims[3];      // Dimensions +        uint32_t element_size; // Size of a single element, including padding +    }; + +    // Monotonically increasing from 1 +    static unsigned int ID; + +    // Maps Allocation DataType enum and vector size to printable strings +    // using mapping from RenderScript numerical types summary documentation +    static const char* RsDataTypeToString[][4]; + +    // Maps Allocation DataKind enum to printable strings +    static const char* RsDataKindToString[]; + +    // Maps allocation types to format sizes for printing. +    static const unsigned int RSTypeToFormat[][3]; + +    // Give each allocation an ID as a way +    // for commands to reference it. +    const unsigned int id; + +    RenderScriptRuntime::Element element;     // Allocation Element type +    empirical_type<Dimension> dimension;      // Dimensions of the Allocation +    empirical_type<lldb::addr_t> address;     // Pointer to address of the RS Allocation +    empirical_type<lldb::addr_t> data_ptr;    // Pointer to the data held by the Allocation +    empirical_type<lldb::addr_t> type_ptr;    // Pointer to the RS Type of the Allocation +    empirical_type<lldb::addr_t> context;     // Pointer to the RS Context of the Allocation +    empirical_type<uint32_t> size;            // Size of the allocation +    empirical_type<uint32_t> stride;          // Stride between rows of the allocation + +    // Give each allocation an id, so we can reference it in user commands. +    AllocationDetails(): id(ID++) +    { +    } + +    bool shouldRefresh() const +    { +        bool valid_ptrs = data_ptr.isValid() && *data_ptr.get() != 0x0; +        valid_ptrs = valid_ptrs && type_ptr.isValid() && *type_ptr.get() != 0x0; +        return !valid_ptrs || !dimension.isValid() || !size.isValid() || element.shouldRefresh(); +    } +}; + + +const ConstString & +RenderScriptRuntime::Element::GetFallbackStructName() +{ +    static const ConstString FallbackStructName("struct"); +    return FallbackStructName; +} + +unsigned int RenderScriptRuntime::AllocationDetails::ID = 1; + +const char* RenderScriptRuntime::AllocationDetails::RsDataKindToString[] = +{ +   "User", +   "Undefined", "Undefined", "Undefined", // Enum jumps from 0 to 7 +   "Undefined", "Undefined", "Undefined", +   "L Pixel", +   "A Pixel", +   "LA Pixel", +   "RGB Pixel", +   "RGBA Pixel", +   "Pixel Depth", +   "YUV Pixel" +}; + +const char* RenderScriptRuntime::AllocationDetails::RsDataTypeToString[][4] = +{ +    {"None", "None", "None", "None"}, +    {"half", "half2", "half3", "half4"}, +    {"float", "float2", "float3", "float4"}, +    {"double", "double2", "double3", "double4"}, +    {"char", "char2", "char3", "char4"}, +    {"short", "short2", "short3", "short4"}, +    {"int", "int2", "int3", "int4"}, +    {"long", "long2", "long3", "long4"}, +    {"uchar", "uchar2", "uchar3", "uchar4"}, +    {"ushort", "ushort2", "ushort3", "ushort4"}, +    {"uint", "uint2", "uint3", "uint4"}, +    {"ulong", "ulong2", "ulong3", "ulong4"}, +    {"bool", "bool2", "bool3", "bool4"}, +    {"packed_565", "packed_565", "packed_565", "packed_565"}, +    {"packed_5551", "packed_5551", "packed_5551", "packed_5551"}, +    {"packed_4444", "packed_4444", "packed_4444", "packed_4444"}, +    {"rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4"}, +    {"rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3"}, +    {"rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2"}, + +    // Handlers +    {"RS Element", "RS Element", "RS Element", "RS Element"}, +    {"RS Type", "RS Type", "RS Type", "RS Type"}, +    {"RS Allocation", "RS Allocation", "RS Allocation", "RS Allocation"}, +    {"RS Sampler", "RS Sampler", "RS Sampler", "RS Sampler"}, +    {"RS Script", "RS Script", "RS Script", "RS Script"}, + +    // Deprecated +    {"RS Mesh", "RS Mesh", "RS Mesh", "RS Mesh"}, +    {"RS Program Fragment", "RS Program Fragment", "RS Program Fragment", "RS Program Fragment"}, +    {"RS Program Vertex", "RS Program Vertex", "RS Program Vertex", "RS Program Vertex"}, +    {"RS Program Raster", "RS Program Raster", "RS Program Raster", "RS Program Raster"}, +    {"RS Program Store", "RS Program Store", "RS Program Store", "RS Program Store"}, +    {"RS Font", "RS Font", "RS Font", "RS Font"} +}; + +// Used as an index into the RSTypeToFormat array elements +enum TypeToFormatIndex { +   eFormatSingle = 0, +   eFormatVector, +   eElementSize +}; + +// { format enum of single element, format enum of element vector, size of element} +const unsigned int RenderScriptRuntime::AllocationDetails::RSTypeToFormat[][3] = +{ +    {eFormatHex, eFormatHex, 1}, // RS_TYPE_NONE +    {eFormatFloat, eFormatVectorOfFloat16, 2}, // RS_TYPE_FLOAT_16 +    {eFormatFloat, eFormatVectorOfFloat32, sizeof(float)}, // RS_TYPE_FLOAT_32 +    {eFormatFloat, eFormatVectorOfFloat64, sizeof(double)}, // RS_TYPE_FLOAT_64 +    {eFormatDecimal, eFormatVectorOfSInt8, sizeof(int8_t)}, // RS_TYPE_SIGNED_8 +    {eFormatDecimal, eFormatVectorOfSInt16, sizeof(int16_t)}, // RS_TYPE_SIGNED_16 +    {eFormatDecimal, eFormatVectorOfSInt32, sizeof(int32_t)}, // RS_TYPE_SIGNED_32 +    {eFormatDecimal, eFormatVectorOfSInt64, sizeof(int64_t)}, // RS_TYPE_SIGNED_64 +    {eFormatDecimal, eFormatVectorOfUInt8, sizeof(uint8_t)}, // RS_TYPE_UNSIGNED_8 +    {eFormatDecimal, eFormatVectorOfUInt16, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_16 +    {eFormatDecimal, eFormatVectorOfUInt32, sizeof(uint32_t)}, // RS_TYPE_UNSIGNED_32 +    {eFormatDecimal, eFormatVectorOfUInt64, sizeof(uint64_t)}, // RS_TYPE_UNSIGNED_64 +    {eFormatBoolean, eFormatBoolean, 1}, // RS_TYPE_BOOL +    {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_5_6_5 +    {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_5_5_5_1 +    {eFormatHex, eFormatHex, sizeof(uint16_t)}, // RS_TYPE_UNSIGNED_4_4_4_4 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 16}, // RS_TYPE_MATRIX_4X4 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 9}, // RS_TYPE_MATRIX_3X3 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 4} // RS_TYPE_MATRIX_2X2 +};  //------------------------------------------------------------------  // Static Functions @@ -44,6 +392,47 @@ RenderScriptRuntime::CreateInstance(Process *process, lldb::LanguageType languag          return NULL;  } +// Callback with a module to search for matching symbols. +// We first check that the module contains RS kernels. +// Then look for a symbol which matches our kernel name. +// The breakpoint address is finally set using the address of this symbol. +Searcher::CallbackReturn +RSBreakpointResolver::SearchCallback(SearchFilter &filter, +                                     SymbolContext &context, +                                     Address*, +                                     bool) +{ +    ModuleSP module = context.module_sp; + +    if (!module) +        return Searcher::eCallbackReturnContinue; + +    // Is this a module containing renderscript kernels? +    if (nullptr == module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"), eSymbolTypeData)) +        return Searcher::eCallbackReturnContinue; + +    // Attempt to set a breakpoint on the kernel name symbol within the module library. +    // If it's not found, it's likely debug info is unavailable - try to set a +    // breakpoint on <name>.expand. + +    const Symbol* kernel_sym = module->FindFirstSymbolWithNameAndType(m_kernel_name, eSymbolTypeCode); +    if (!kernel_sym) +    { +        std::string kernel_name_expanded(m_kernel_name.AsCString()); +        kernel_name_expanded.append(".expand"); +        kernel_sym = module->FindFirstSymbolWithNameAndType(ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode); +    } + +    if (kernel_sym) +    { +        Address bp_addr = kernel_sym->GetAddress(); +        if (filter.AddressPasses(bp_addr)) +            m_breakpoint->AddLocation(bp_addr); +    } + +    return Searcher::eCallbackReturnContinue; +} +  void  RenderScriptRuntime::Initialize()  { @@ -88,7 +477,7 @@ RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp)              return eModuleKindDriver;          } -        const ConstString rs_cpureflib("libRSCPURef.so"); +        const ConstString rs_cpureflib("libRSCpuRef.so");          if (module_sp->GetFileSpec().GetFilename() == rs_cpureflib)          {              return eModuleKindImpl; @@ -104,7 +493,6 @@ RenderScriptRuntime::IsRenderScriptModule(const lldb::ModuleSP &module_sp)      return GetModuleKind(module_sp) != eModuleKindIgnored;  } -  void   RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list )  { @@ -121,7 +509,6 @@ RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list )      }  } -  //------------------------------------------------------------------  // PluginInterface protocol  //------------------------------------------------------------------ @@ -145,11 +532,19 @@ RenderScriptRuntime::IsVTableName(const char *name)  bool  RenderScriptRuntime::GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, -                                              TypeAndOrName &class_type_or_name, Address &address) +                                              TypeAndOrName &class_type_or_name, Address &address, +                                              Value::ValueType &value_type)  {      return false;  } +TypeAndOrName +RenderScriptRuntime::FixUpDynamicType (const TypeAndOrName& type_and_or_name, +                                       ValueObject& static_value) +{ +    return type_and_or_name; +} +  bool  RenderScriptRuntime::CouldHaveDynamicValue(ValueObject &in_value)  { @@ -163,22 +558,78 @@ RenderScriptRuntime::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bo      return resolver_sp;  } -  const RenderScriptRuntime::HookDefn RenderScriptRuntime::s_runtimeHookDefns[] =  {      //rsdScript -    {"rsdScriptInit", "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_7ScriptCEPKcS7_PKhjj", 0, RenderScriptRuntime::eModuleKindDriver, &lldb_private::RenderScriptRuntime::CaptureScriptInit1}, -    {"rsdScriptInvokeForEach", "_Z22rsdScriptInvokeForEachPKN7android12renderscript7ContextEPNS0_6ScriptEjPKNS0_10AllocationEPS6_PKvjPK12RsScriptCall", 0, RenderScriptRuntime::eModuleKindDriver, nullptr}, -    {"rsdScriptInvokeForEachMulti", "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall", 0, RenderScriptRuntime::eModuleKindDriver, nullptr}, -    {"rsdScriptInvokeFunction", "_Z23rsdScriptInvokeFunctionPKN7android12renderscript7ContextEPNS0_6ScriptEjPKvj", 0, RenderScriptRuntime::eModuleKindDriver, nullptr}, -    {"rsdScriptSetGlobalVar", "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_6ScriptEjPvj", 0, RenderScriptRuntime::eModuleKindDriver, &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar1}, +    { +        "rsdScriptInit", //name +        "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_7ScriptCEPKcS7_PKhjj", // symbol name 32 bit +        "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_7ScriptCEPKcS7_PKhmj", // symbol name 64 bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        &lldb_private::RenderScriptRuntime::CaptureScriptInit1 // handler +    }, +    { +        "rsdScriptInvokeForEach", // name +        "_Z22rsdScriptInvokeForEachPKN7android12renderscript7ContextEPNS0_6ScriptEjPKNS0_10AllocationEPS6_PKvjPK12RsScriptCall", // symbol name 32bit +        "_Z22rsdScriptInvokeForEachPKN7android12renderscript7ContextEPNS0_6ScriptEjPKNS0_10AllocationEPS6_PKvmPK12RsScriptCall", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        nullptr // handler +    }, +    { +        "rsdScriptInvokeForEachMulti", // name +        "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall", // symbol name 32bit +        "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0_6ScriptEjPPKNS0_10AllocationEmPS6_PKvmPK12RsScriptCall", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        nullptr // handler +    }, +    { +        "rsdScriptInvokeFunction", // name +        "_Z23rsdScriptInvokeFunctionPKN7android12renderscript7ContextEPNS0_6ScriptEjPKvj", // symbol name 32bit +        "_Z23rsdScriptInvokeFunctionPKN7android12renderscript7ContextEPNS0_6ScriptEjPKvm", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        nullptr // handler +    }, +    { +        "rsdScriptSetGlobalVar", // name +        "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_6ScriptEjPvj", // symbol name 32bit +        "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_6ScriptEjPvm", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar1 // handler +    },      //rsdAllocation -    {"rsdAllocationInit", "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_10AllocationEb", 0, RenderScriptRuntime::eModuleKindDriver, &lldb_private::RenderScriptRuntime::CaptureAllocationInit1}, -    {"rsdAllocationRead2D", "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_10AllocationEjjj23RsAllocationCubemapFacejjPvjj", 0, RenderScriptRuntime::eModuleKindDriver, nullptr}, +    { +        "rsdAllocationInit", // name +        "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_10AllocationEb", // symbol name 32bit +        "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_10AllocationEb", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        &lldb_private::RenderScriptRuntime::CaptureAllocationInit1 // handler +    }, +    { +        "rsdAllocationRead2D", //name +        "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_10AllocationEjjj23RsAllocationCubemapFacejjPvjj", // symbol name 32bit +        "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_10AllocationEjjj23RsAllocationCubemapFacejjPvmm", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        nullptr // handler +    }, +    { +        "rsdAllocationDestroy", // name +        "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_10AllocationE", // symbol name 32bit +        "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_10AllocationE", // symbol name 64bit +        0, // version +        RenderScriptRuntime::eModuleKindDriver, // type +        &lldb_private::RenderScriptRuntime::CaptureAllocationDestroy // handler +    },  }; -const size_t RenderScriptRuntime::s_runtimeHookCount = sizeof(s_runtimeHookDefns)/sizeof(s_runtimeHookDefns[0]); +const size_t RenderScriptRuntime::s_runtimeHookCount = sizeof(s_runtimeHookDefns)/sizeof(s_runtimeHookDefns[0]);  bool  RenderScriptRuntime::HookCallback(void *baton, StoppointCallbackContext *ctx, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) @@ -193,13 +644,12 @@ RenderScriptRuntime::HookCallback(void *baton, StoppointCallbackContext *ctx, ll      return false;  } -  void   RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& context)  {      Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); -    if(log) +    if (log)          log->Printf ("RenderScriptRuntime::HookCallback - '%s' .", hook_info->defn->name);      if (hook_info->defn->grabber)  @@ -208,56 +658,241 @@ RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& cont      }  } -  bool -RenderScriptRuntime::GetArg32Simple(ExecutionContext& context, uint32_t arg, uint32_t *data) +RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint64_t *data)  { -    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +    // Get a positional integer argument. +    // Given an ExecutionContext, ``context`` which should be a RenderScript +    // frame, get the value of the positional argument ``arg`` and save its value +    // to the address pointed to by ``data``. +    // returns true on success, false otherwise. +    // If unsuccessful, the value pointed to by ``data`` is undefined. Otherwise, +    // ``data`` will be set to the value of the the given ``arg``. +    // NOTE: only natural width integer arguments for the machine are supported. +    // Behaviour with non primitive arguments is undefined.      if (!data)          return false; +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));      Error error;      RegisterContext* reg_ctx = context.GetRegisterContext();      Process* process = context.GetProcessPtr(); +    bool success = false; // return value -    if (context.GetTargetPtr()->GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86)  +    if (!context.GetTargetPtr())      { -        uint64_t sp = reg_ctx->GetSP(); +        if (log) +            log->Printf("RenderScriptRuntime::GetArgSimple - Invalid target"); + +        return false; +    } + +    switch (context.GetTargetPtr()->GetArchitecture().GetMachine()) +    { +        case llvm::Triple::ArchType::x86:          { +            uint64_t sp = reg_ctx->GetSP();              uint32_t offset = (1 + arg) * sizeof(uint32_t); -            process->ReadMemory(sp + offset, data, sizeof(uint32_t), error); -            if(error.Fail()) +            uint32_t result = 0; +            process->ReadMemory(sp + offset, &result, sizeof(uint32_t), error); +            if (error.Fail())              { -                if(log) -                    log->Printf ("RenderScriptRuntime:: GetArg32Simple - error reading X86 stack: %s.", error.AsCString());      +                if (log) +                    log->Printf("RenderScriptRuntime::GetArgSimple - error reading X86 stack: %s.", error.AsCString());              } +            else +            { +                *data = result; +                success = true; +            } + +            break;          } -    } -    else if (context.GetTargetPtr()->GetArchitecture().GetMachine() == llvm::Triple::ArchType::arm)  -    { -        if (arg < 4) +        case llvm::Triple::ArchType::x86_64:          { -            const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); +            // amd64 has 6 integer registers, and 8 XMM registers for parameter passing. +            // Surplus args are spilled onto the stack. +            // rdi, rsi, rdx, rcx, r8, r9, (zmm0 - 7 for vectors) +            // ref: AMD64 ABI Draft 0.99.6 – October 7, 2013 – 10:35; Figure 3.4. Retrieved from +            // http://www.x86-64.org/documentation/abi.pdf +            if (arg > 5) +            { +                if (log) +                    log->Warning("X86_64 register spill is not supported."); +                break; +            } +            const char * regnames[] = {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}; +            assert((sizeof(regnames) / sizeof(const char *)) > arg); +            const RegisterInfo *rArg = reg_ctx->GetRegisterInfoByName(regnames[arg]);              RegisterValue rVal; -            reg_ctx->ReadRegister(rArg, rVal); -            (*data) = rVal.GetAsUInt32(); +            success = reg_ctx->ReadRegister(rArg, rVal); +            if (success) +            { +                *data = rVal.GetAsUInt64(0u, &success); +            } +            else +            { +                if (log) +                    log->Printf("RenderScriptRuntime::GetArgSimple - error reading x86_64 register: %d.", arg); +            } +            break;          } -        else +        case llvm::Triple::ArchType::arm:          { -            uint64_t sp = reg_ctx->GetSP(); +            // arm 32 bit +            if (arg < 4)              { +                const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); +                RegisterValue rVal; +                success = reg_ctx->ReadRegister(rArg, rVal); +                if (success) +                { +                    (*data) = rVal.GetAsUInt32(0u, &success); +                } +                else +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM register: %d.", arg); +                } +            } +            else +            { +                uint64_t sp = reg_ctx->GetSP();                  uint32_t offset = (arg-4) * sizeof(uint32_t);                  process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); -                if(error.Fail()) +                if (error.Fail()) +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM stack: %s.", error.AsCString()); +                } +                else                  { -                    if(log) -                        log->Printf ("RenderScriptRuntime:: GetArg32Simple - error reading ARM stack: %s.", error.AsCString());      +                    success = true;                  }              } -        }    + +            break; +        } +        case llvm::Triple::ArchType::aarch64: +        { +            // arm 64 bit +            // first 8 arguments are in the registers +            if (arg < 8) +            { +                const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); +                RegisterValue rVal; +                success = reg_ctx->ReadRegister(rArg, rVal); +                if (success) +                { +                    *data = rVal.GetAsUInt64(0u, &success); +                } +                else +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple() - AARCH64 - Error while reading the argument #%d", arg); +                } +            } +            else +            { +                // @TODO: need to find the argument in the stack +                if (log) +                    log->Printf("RenderScriptRuntime::GetArgSimple - AARCH64 - FOR #ARG >= 8 NOT IMPLEMENTED YET. Argument number: %d", arg); +            } +            break; +        } +        case llvm::Triple::ArchType::mipsel: +        { + +            // read from the registers +            if (arg < 4){ +                const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); +                RegisterValue rVal; +                success = reg_ctx->ReadRegister(rArg, rVal); +                if (success) +                { +                    *data = rVal.GetAsUInt64(0u, &success); +                } +                else +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple() - Mips - Error while reading the argument #%d", arg); +                } + +            } + +            // read from the stack +            else +            { +                uint64_t sp = reg_ctx->GetSP(); +                uint32_t offset = arg * sizeof(uint32_t); +                process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); +                if (error.Fail()) +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple - error reading Mips stack: %s.", error.AsCString()); +                } +                else +                { +                    success = true; +                } +            } + +            break; +        } +        case llvm::Triple::ArchType::mips64el: +        { +            // read from the registers +            if (arg < 8) +            { +                const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); +                RegisterValue rVal; +                success = reg_ctx->ReadRegister(rArg, rVal); +                if (success) +                { +                    (*data) = rVal.GetAsUInt64(0u, &success); +                } +                else +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading the argument #%d", arg); +                } +            } + +            // read from the stack +            else +            { +                uint64_t sp = reg_ctx->GetSP(); +                uint32_t offset = (arg - 8) * sizeof(uint64_t); +                process->ReadMemory(sp + offset, &data, sizeof(uint64_t), error); +                if (error.Fail()) +                { +                    if (log) +                        log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading Mips64 stack: %s.", error.AsCString()); +                } +                else +                { +                    success = true; +                } +            } + +            break; +        } +        default: +        { +            // invalid architecture +            if (log) +                log->Printf("RenderScriptRuntime::GetArgSimple - Architecture not supported"); + +        }      } -    return true; + +    if (!success) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetArgSimple - failed to get argument at index %" PRIu32, arg); +    } +    return success;  }  void  @@ -267,35 +902,38 @@ RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionConte      //Context, Script, int, data, length -    Error error; -     -    uint32_t rs_context_u32 = 0U; -    uint32_t rs_script_u32 = 0U; -    uint32_t rs_id_u32 = 0U; -    uint32_t rs_data_u32 = 0U; -    uint32_t rs_length_u32 = 0U; +    uint64_t rs_context_u64 = 0U; +    uint64_t rs_script_u64 = 0U; +    uint64_t rs_id_u64 = 0U; +    uint64_t rs_data_u64 = 0U; +    uint64_t rs_length_u64 = 0U; -    std::string resname; -    std::string cachedir; +    bool success = +        GetArgSimple(context, 0, &rs_context_u64) && +        GetArgSimple(context, 1, &rs_script_u64) && +        GetArgSimple(context, 2, &rs_id_u64) && +        GetArgSimple(context, 3, &rs_data_u64) && +        GetArgSimple(context, 4, &rs_length_u64); -    GetArg32Simple(context, 0, &rs_context_u32); -    GetArg32Simple(context, 1, &rs_script_u32); -    GetArg32Simple(context, 2, &rs_id_u32); -    GetArg32Simple(context, 3, &rs_data_u32); -    GetArg32Simple(context, 4, &rs_length_u32); +    if (!success) +    { +        if (log) +            log->Printf("RenderScriptRuntime::CaptureSetGlobalVar1 - Error while reading the function parameters"); +        return; +    } -    if(log) +    if (log)      {          log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64 ":%" PRIu64 "bytes.", -                        (uint64_t)rs_context_u32, (uint64_t)rs_script_u32, (uint64_t)rs_id_u32, (uint64_t)rs_data_u32, (uint64_t)rs_length_u32); +                        rs_context_u64, rs_script_u64, rs_id_u64, rs_data_u64, rs_length_u64); -        addr_t script_addr =  (addr_t)rs_script_u32; +        addr_t script_addr =  (addr_t)rs_script_u64;          if (m_scriptMappings.find( script_addr ) != m_scriptMappings.end())          {              auto rsm = m_scriptMappings[script_addr]; -            if (rs_id_u32 < rsm->m_globals.size()) +            if (rs_id_u64 < rsm->m_globals.size())              { -                auto rsg = rsm->m_globals[rs_id_u32]; +                auto rsg = rsm->m_globals[rs_id_u64];                  log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(),                                   rsm->m_module->GetFileSpec().GetFilename().AsCString());              } @@ -310,19 +948,65 @@ RenderScriptRuntime::CaptureAllocationInit1(RuntimeHook* hook_info, ExecutionCon      //Context, Alloc, bool -    Error error; -     -    uint32_t rs_context_u32 = 0U; -    uint32_t rs_alloc_u32 = 0U; -    uint32_t rs_forceZero_u32 = 0U; - -    GetArg32Simple(context, 0, &rs_context_u32); -    GetArg32Simple(context, 1, &rs_alloc_u32); -    GetArg32Simple(context, 2, &rs_forceZero_u32); -         -    if(log) +    uint64_t rs_context_u64 = 0U; +    uint64_t rs_alloc_u64 = 0U; +    uint64_t rs_forceZero_u64 = 0U; + +    bool success = +        GetArgSimple(context, 0, &rs_context_u64) && +        GetArgSimple(context, 1, &rs_alloc_u64) && +        GetArgSimple(context, 2, &rs_forceZero_u64); +    if (!success) // error case +    { +        if (log) +            log->Printf("RenderScriptRuntime::CaptureAllocationInit1 - Error while reading the function parameters"); +        return; // abort +    } + +    if (log)          log->Printf ("RenderScriptRuntime::CaptureAllocationInit1 - 0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 " .", -                        (uint64_t)rs_context_u32, (uint64_t)rs_alloc_u32, (uint64_t)rs_forceZero_u32); +                        rs_context_u64, rs_alloc_u64, rs_forceZero_u64); + +    AllocationDetails* alloc = LookUpAllocation(rs_alloc_u64, true); +    if (alloc) +        alloc->context = rs_context_u64; +} + +void +RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook* hook_info, ExecutionContext& context) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    // Context, Alloc +    uint64_t rs_context_u64 = 0U; +    uint64_t rs_alloc_u64 = 0U; + +    bool success = GetArgSimple(context, 0, &rs_context_u64) && GetArgSimple(context, 1, &rs_alloc_u64); +    if (!success) // error case +    { +        if (log) +            log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Error while reading the function parameters"); +        return; // abort +    } + +    if (log) +        log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - 0x%" PRIx64 ", 0x%" PRIx64 ".", +                    rs_context_u64, rs_alloc_u64); + +    for (auto iter = m_allocations.begin(); iter != m_allocations.end(); ++iter) +    { +        auto& allocation_ap = *iter; // get the unique pointer +        if (allocation_ap->address.isValid() && *allocation_ap->address.get() == rs_alloc_u64) +        { +            m_allocations.erase(iter); +            if (log) +                log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Deleted allocation entry"); +            return; +        } +    } + +    if (log) +        log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Couldn't find destroyed allocation");  }  void  @@ -334,64 +1018,72 @@ RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext      Error error;      Process* process = context.GetProcessPtr(); -    uint32_t rs_context_u32 = 0U; -    uint32_t rs_script_u32 = 0U; -    uint32_t rs_resnameptr_u32 = 0U; -    uint32_t rs_cachedirptr_u32 = 0U; +    uint64_t rs_context_u64 = 0U; +    uint64_t rs_script_u64 = 0U; +    uint64_t rs_resnameptr_u64 = 0U; +    uint64_t rs_cachedirptr_u64 = 0U;      std::string resname;      std::string cachedir; -    GetArg32Simple(context, 0, &rs_context_u32); -    GetArg32Simple(context, 1, &rs_script_u32); -    GetArg32Simple(context, 2, &rs_resnameptr_u32); -    GetArg32Simple(context, 3, &rs_cachedirptr_u32); +    // read the function parameters +    bool success = +        GetArgSimple(context, 0, &rs_context_u64) && +        GetArgSimple(context, 1, &rs_script_u64) && +        GetArgSimple(context, 2, &rs_resnameptr_u64) && +        GetArgSimple(context, 3, &rs_cachedirptr_u64); -    process->ReadCStringFromMemory((lldb::addr_t)rs_resnameptr_u32, resname, error); +    if (!success) +    { +        if (log) +            log->Printf("RenderScriptRuntime::CaptureScriptInit1 - Error while reading the function parameters"); +        return; +    } + +    process->ReadCStringFromMemory((lldb::addr_t)rs_resnameptr_u64, resname, error);      if (error.Fail())      { -        if(log) +        if (log)              log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading resname: %s.", error.AsCString());      } -    process->ReadCStringFromMemory((lldb::addr_t)rs_cachedirptr_u32, cachedir, error); +    process->ReadCStringFromMemory((lldb::addr_t)rs_cachedirptr_u64, cachedir, error);      if (error.Fail())      { -        if(log) +        if (log)              log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString());           }      if (log)          log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .", -                     (uint64_t)rs_context_u32, (uint64_t)rs_script_u32, resname.c_str(), cachedir.c_str()); +                     rs_context_u64, rs_script_u64, resname.c_str(), cachedir.c_str());      if (resname.size() > 0)      {          StreamString strm;          strm.Printf("librs.%s.so", resname.c_str()); -        ScriptDetails script; -        script.cachedir = cachedir; -        script.resname = resname; -        script.scriptDyLib.assign(strm.GetData()); -        script.script = rs_script_u32; -        script.context = rs_context_u32; - -        m_scripts.push_back(script); +        ScriptDetails* script = LookUpScript(rs_script_u64, true); +        if (script) +        { +            script->type = ScriptDetails::eScriptC; +            script->cacheDir = cachedir; +            script->resName = resname; +            script->scriptDyLib = strm.GetData(); +            script->context = addr_t(rs_context_u64); +        }          if (log)              log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - '%s' tagged with context 0x%" PRIx64 " and script 0x%" PRIx64 ".", -                         strm.GetData(), (uint64_t)rs_context_u32, (uint64_t)rs_script_u32); +                         strm.GetData(), rs_context_u64, rs_script_u64);      }       else if (log)      {          log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - resource name invalid, Script not tagged");      } -  } -  void  RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind)  { @@ -402,16 +1094,24 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind)          return;      } -    if ((GetProcess()->GetTarget().GetArchitecture().GetMachine() != llvm::Triple::ArchType::x86) -        && (GetProcess()->GetTarget().GetArchitecture().GetMachine() != llvm::Triple::ArchType::arm)) +    Target &target = GetProcess()->GetTarget(); +    llvm::Triple::ArchType targetArchType = target.GetArchitecture().GetMachine(); + +    if (targetArchType != llvm::Triple::ArchType::x86 +        && targetArchType != llvm::Triple::ArchType::arm +        && targetArchType != llvm::Triple::ArchType::aarch64 +        && targetArchType != llvm::Triple::ArchType::mipsel +        && targetArchType != llvm::Triple::ArchType::mips64el +        && targetArchType != llvm::Triple::ArchType::x86_64 +    )      {          if (log) -            log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to hook runtime. Only X86, ARM supported currently."); +            log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to hook runtime. Only X86, ARM, Mips supported currently.");          return;      } -    Target &target = GetProcess()->GetTarget(); +    uint32_t archByteSize = target.GetArchitecture().GetAddressByteSize();      for (size_t idx = 0; idx < s_runtimeHookCount; idx++)      { @@ -420,16 +1120,29 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind)              continue;          } -        const Symbol *sym = module->FindFirstSymbolWithNameAndType(ConstString(hook_defn->symbol_name), eSymbolTypeCode); +        const char* symbol_name = (archByteSize == 4) ? hook_defn->symbol_name_m32 : hook_defn->symbol_name_m64; + +        const Symbol *sym = module->FindFirstSymbolWithNameAndType(ConstString(symbol_name), eSymbolTypeCode); +        if (!sym){ +            if (log){ +                log->Printf("RenderScriptRuntime::LoadRuntimeHooks - ERROR: Symbol '%s' related to the function %s not found", symbol_name, hook_defn->name); +            } +            continue; +        }          addr_t addr = sym->GetLoadAddress(&target);          if (addr == LLDB_INVALID_ADDRESS)          { -            if(log) +            if (log)                  log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.",  -                             hook_defn->name, hook_defn->symbol_name); +                             hook_defn->name, symbol_name);              continue;          } +        else +        { +            if (log) +                log->Printf("RenderScriptRuntime::LoadRuntimeHooks - Function %s, address resolved at 0x%" PRIx64, hook_defn->name, addr); +        }          RuntimeHookSP hook(new RuntimeHook());          hook->address = addr; @@ -455,35 +1168,1082 @@ RenderScriptRuntime::FixupScriptDetails(RSModuleDescriptorSP rsmodule_sp)      const ModuleSP module = rsmodule_sp->m_module;      const FileSpec& file = module->GetPlatformFileSpec(); -     -    for (const auto &rs_script : m_scripts) + +    // Iterate over all of the scripts that we currently know of. +    // Note: We cant push or pop to m_scripts here or it may invalidate rs_script. +    for (const auto & rs_script : m_scripts) +    { +        // Extract the expected .so file path for this script. +        std::string dylib; +        if (!rs_script->scriptDyLib.get(dylib)) +            continue; + +        // Only proceed if the module that has loaded corresponds to this script. +        if (file.GetFilename() != ConstString(dylib.c_str())) +            continue; + +        // Obtain the script address which we use as a key. +        lldb::addr_t script; +        if (!rs_script->script.get(script)) +            continue; + +        // If we have a script mapping for the current script. +        if (m_scriptMappings.find(script) != m_scriptMappings.end()) +        { +            // if the module we have stored is different to the one we just received. +            if (m_scriptMappings[script] != rsmodule_sp) +            { +                if (log) +                    log->Printf ("RenderScriptRuntime::FixupScriptDetails - Error: script %" PRIx64 " wants reassigned to new rsmodule '%s'.", +                                    (uint64_t)script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); +            } +        } +        // We don't have a script mapping for the current script. +        else +        { +            // Obtain the script resource name. +            std::string resName; +            if (rs_script->resName.get(resName)) +                // Set the modules resource name. +                rsmodule_sp->m_resname = resName; +            // Add Script/Module pair to map. +            m_scriptMappings[script] = rsmodule_sp; +            if (log) +                log->Printf ("RenderScriptRuntime::FixupScriptDetails - script %" PRIx64 " associated with rsmodule '%s'.", +                                (uint64_t)script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); +        } +    } +} + +// Uses the Target API to evaluate the expression passed as a parameter to the function +// The result of that expression is returned an unsigned 64 bit int, via the result* paramter. +// Function returns true on success, and false on failure +bool +RenderScriptRuntime::EvalRSExpression(const char* expression, StackFrame* frame_ptr, uint64_t* result) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +    if (log) +        log->Printf("RenderScriptRuntime::EvalRSExpression(%s)", expression); + +    ValueObjectSP expr_result; +    // Perform the actual expression evaluation +    GetProcess()->GetTarget().EvaluateExpression(expression, frame_ptr, expr_result); + +    if (!expr_result) +    { +       if (log) +           log->Printf("RenderScriptRuntime::EvalRSExpression -  Error: Couldn't evaluate expression"); +       return false; +    } + +    // The result of the expression is invalid +    if (!expr_result->GetError().Success()) +    { +        Error err = expr_result->GetError(); +        if (err.GetError() == UserExpression::kNoResult) // Expression returned void, so this is actually a success +        { +            if (log) +                log->Printf("RenderScriptRuntime::EvalRSExpression - Expression returned void"); + +            result = nullptr; +            return true; +        } + +        if (log) +            log->Printf("RenderScriptRuntime::EvalRSExpression - Error evaluating expression result: %s", err.AsCString()); +        return false; +    } + +    bool success = false; +    *result = expr_result->GetValueAsUnsigned(0, &success); // We only read the result as an unsigned int. + +    if (!success) +    { +       if (log) +           log->Printf("RenderScriptRuntime::EvalRSExpression -  Error: Couldn't convert expression result to unsigned int"); +       return false; +    } + +    return true; +} + +namespace // anonymous +{ +    // max length of an expanded expression +    const int jit_max_expr_size = 768; + +    // Format strings containing the expressions we may need to evaluate. +    const char runtimeExpressions[][256] = +    { +     // Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap) +     "(int*)_Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocationCubemapFace(0x%lx, %u, %u, %u, 0, 0)", + +     // Type* rsaAllocationGetType(Context*, Allocation*) +     "(void*)rsaAllocationGetType(0x%lx, 0x%lx)", + +     // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) +     // Pack the data in the following way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ; +     // mHal.state.lodCount; mHal.state.faces; mElement; into typeData +     // Need to specify 32 or 64 bit for uint_t since this differs between devices +     "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[0]", // X dim +     "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[1]", // Y dim +     "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[2]", // Z dim +     "uint%u_t data[6]; (void*)rsaTypeGetNativeData(0x%lx, 0x%lx, data, 6); data[5]", // Element ptr + +     // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size) +     // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into elemData +     "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[0]", // Type +     "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[1]", // Kind +     "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[3]", // Vector Size +     "uint32_t data[5]; (void*)rsaElementGetNativeData(0x%lx, 0x%lx, data, 5); data[4]", // Field Count + +      // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t *ids, const char **names, +      // size_t *arraySizes, uint32_t dataSize) +      // Needed for Allocations of structs to gather details about fields/Subelements +     "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +     "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); ids[%u]",     // Element* of field + +     "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +     "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); names[%u]",   // Name of field + +     "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +     "(void*)rsaElementGetSubElements(0x%lx, 0x%lx, ids, names, arr_size, %u); arr_size[%u]" // Array size of field +    }; + + +    // Temporary workaround for MIPS, until the compiler emits the JAL instruction when invoking directly the function. +    // At the moment, when evaluating an expression involving a function call, the LLVM codegen for Mips  emits a JAL +    // instruction, which is able to jump in the range +/- 128MB with respect to the current program counter ($pc). If +    // the requested function happens to reside outside the above region, the function address will be truncated and the +    // function invocation will fail. This is a problem in the RS plugin as we rely on the RS API to probe the number and +    // the nature of allocations. A proper solution in the MIPS compiler is currently being investigated. As temporary +    // work around for this context, we'll invoke the RS API through function pointers, which cause the compiler to emit a +    // register based JALR instruction. +    const char runtimeExpressions_mips[][512] = +    { +    // Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap) +    "int* (*f) (void*, int, int, int, int, int) = (int* (*) (void*, int, int, int, int, int)) " +        "_Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocationCubemapFace; " +        "(int*) f((void*) 0x%lx, %u, %u, %u, 0, 0)", + +    // Type* rsaAllocationGetType(Context*, Allocation*) +    "void* (*f) (void*, void*) = (void* (*) (void*, void*)) rsaAllocationGetType; (void*) f((void*) 0x%lx, (void*) 0x%lx)", + +    // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) +    // Pack the data in the following way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ; +    // mHal.state.lodCount; mHal.state.faces; mElement; into typeData +    // Need to specify 32 or 64 bit for uint_t since this differs between devices +    "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " +        "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[0]", +    "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " +        "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[1]", +    "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " +        "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[2]", +    "uint%u_t data[6]; void* (*f)(void*, void*, uintptr_t*, uint32_t) = (void* (*)(void*, void*, uintptr_t*, uint32_t)) " +        "rsaTypeGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 6); data[5]", + +    // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size) +    // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into elemData +    "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " +        "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[0]", // Type +    "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " +        "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[1]", // Kind +    "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " +        "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[3]", // Vector size +    "uint32_t data[5]; void* (*f)(void*, void*, uint32_t*, uint32_t) = (void* (*)(void*, void*, uint32_t*, uint32_t)) " +        "rsaElementGetNativeData; (void*) f((void*) 0x%lx, (void*) 0x%lx, data, 5); data[4]", // Field count + +    // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t *ids, const char **names, +    // size_t *arraySizes, uint32_t dataSize) +    // Needed for Allocations of structs to gather details about fields/Subelements +   "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +        "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " +        "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" +        "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" +        "ids[%u]", // Element* of field +   "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +        "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " +        "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" +        "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" +        "names[%u]", // Name of field +   "void* ids[%u]; const char* names[%u]; size_t arr_size[%u];" +        "void* (*f) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t) = " +        "(void* (*) (void*, void*, uintptr_t*, const char**, size_t*, uint32_t)) rsaElementGetSubElements;" +        "(void*) f((void*) 0x%lx, (void*) 0x%lx, (uintptr_t*) ids, names, arr_size, (uint32_t) %u);" +        "arr_size[%u]" // Array size of field +    }; + +} // end of the anonymous namespace + + +// Retrieve the string to JIT for the given expression +const char* +RenderScriptRuntime::JITTemplate(ExpressionStrings e) +{ +    // be nice to your Mips friend when adding new expression strings +    static_assert(sizeof(runtimeExpressions)/sizeof(runtimeExpressions[0]) == +            sizeof(runtimeExpressions_mips)/sizeof(runtimeExpressions_mips[0]), +            "#runtimeExpressions != #runtimeExpressions_mips"); + +    assert((e >= eExprGetOffsetPtr && e <= eExprSubelementsArrSize) && +           "Expression string out of bounds"); + +    llvm::Triple::ArchType arch = GetTargetRef().GetArchitecture().GetMachine(); + +    // mips JAL workaround +    if(arch == llvm::Triple::ArchType::mips64el || arch == llvm::Triple::ArchType::mipsel) +        return runtimeExpressions_mips[e]; +    else +        return runtimeExpressions[e]; +} + + +// JITs the RS runtime for the internal data pointer of an allocation. +// Is passed x,y,z coordinates for the pointer to a specific element. +// Then sets the data_ptr member in Allocation with the result. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITDataPointer(AllocationDetails* allocation, StackFrame* frame_ptr, +                                    unsigned int x, unsigned int y, unsigned int z) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!allocation->address.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITDataPointer - Failed to find allocation details"); +        return false; +    } + +    const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); +    char buffer[jit_max_expr_size]; + +    int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), x, y, z); +    if (chars_written < 0) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); +        return false; +    } +    else if (chars_written >= jit_max_expr_size) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITDataPointer - Expression too long"); +        return false; +    } + +    uint64_t result = 0; +    if (!EvalRSExpression(buffer, frame_ptr, &result)) +        return false; + +    addr_t mem_ptr = static_cast<lldb::addr_t>(result); +    allocation->data_ptr = mem_ptr; + +    return true; +} + +// JITs the RS runtime for the internal pointer to the RS Type of an allocation +// Then sets the type_ptr member in Allocation with the result. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITTypePointer(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!allocation->address.isValid() || !allocation->context.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITTypePointer - Failed to find allocation details"); +        return false; +    } + +    const char* expr_cstr = JITTemplate(eExprAllocGetType); +    char buffer[jit_max_expr_size]; + +    int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->context.get(), *allocation->address.get()); +    if (chars_written < 0) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); +        return false; +    } +    else if (chars_written >= jit_max_expr_size) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITTypePointer - Expression too long"); +        return false; +    } + +    uint64_t result = 0; +    if (!EvalRSExpression(buffer, frame_ptr, &result)) +        return false; + +    addr_t type_ptr = static_cast<lldb::addr_t>(result); +    allocation->type_ptr = type_ptr; + +    return true; +} + +// JITs the RS runtime for information about the dimensions and type of an allocation +// Then sets dimension and element_ptr members in Allocation with the result. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITTypePacked(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!allocation->type_ptr.isValid() || !allocation->context.isValid())      { -        if (file.GetFilename() == ConstString(rs_script.scriptDyLib.c_str())) +        if (log) +            log->Printf("RenderScriptRuntime::JITTypePacked - Failed to find allocation details"); +        return false; +    } + +    // Expression is different depending on if device is 32 or 64 bit +    uint32_t archByteSize = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); +    const unsigned int bits = archByteSize == 4 ? 32 : 64; + +    // We want 4 elements from packed data +    const unsigned int num_exprs = 4; +    assert(num_exprs == (eExprTypeElemPtr - eExprTypeDimX + 1) && "Invalid number of expressions"); + +    char buffer[num_exprs][jit_max_expr_size]; +    uint64_t results[num_exprs]; + +    for (unsigned int i = 0; i < num_exprs; ++i) +    { +        const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprTypeDimX + i)); +        int chars_written = snprintf(buffer[i], jit_max_expr_size, expr_cstr, bits, +                                     *allocation->context.get(), *allocation->type_ptr.get()); +        if (chars_written < 0) +        { +            if (log) +                log->Printf("RenderScriptRuntime::JITDataPointer - Encoding error in snprintf()"); +            return false; +        } +        else if (chars_written >= jit_max_expr_size) +        { +            if (log) +                log->Printf("RenderScriptRuntime::JITTypePacked - Expression too long"); +            return false; +        } + +        // Perform expression evaluation +        if (!EvalRSExpression(buffer[i], frame_ptr, &results[i])) +            return false; +    } + +    // Assign results to allocation members +    AllocationDetails::Dimension dims; +    dims.dim_1 = static_cast<uint32_t>(results[0]); +    dims.dim_2 = static_cast<uint32_t>(results[1]); +    dims.dim_3 = static_cast<uint32_t>(results[2]); +    allocation->dimension = dims; + +    addr_t elem_ptr = static_cast<lldb::addr_t>(results[3]); +    allocation->element.element_ptr = elem_ptr; + +    if (log) +        log->Printf("RenderScriptRuntime::JITTypePacked - dims (%u, %u, %u) Element*: 0x%" PRIx64, +                    dims.dim_1, dims.dim_2, dims.dim_3, elem_ptr); + +    return true; +} + +// JITs the RS runtime for information about the Element of an allocation +// Then sets type, type_vec_size, field_count and type_kind members in Element with the result. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITElementPacked(Element& elem, const lldb::addr_t context, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!elem.element_ptr.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITElementPacked - Failed to find allocation details"); +        return false; +    } + +    // We want 4 elements from packed data +    const unsigned int num_exprs = 4; +    assert(num_exprs == (eExprElementFieldCount - eExprElementType + 1) && "Invalid number of expressions"); + +    char buffer[num_exprs][jit_max_expr_size]; +    uint64_t results[num_exprs]; + +    for (unsigned int i = 0; i < num_exprs; i++) +    { +        const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprElementType + i)); +        int chars_written = snprintf(buffer[i], jit_max_expr_size, expr_cstr, context, *elem.element_ptr.get()); +        if (chars_written < 0)          { -            if (m_scriptMappings.find( rs_script.script ) != m_scriptMappings.end()) +            if (log) +                log->Printf("RenderScriptRuntime::JITElementPacked - Encoding error in snprintf()"); +            return false; +        } +        else if (chars_written >= jit_max_expr_size) +        { +            if (log) +                log->Printf("RenderScriptRuntime::JITElementPacked - Expression too long"); +            return false; +        } + +        // Perform expression evaluation +        if (!EvalRSExpression(buffer[i], frame_ptr, &results[i])) +            return false; +    } + +    // Assign results to allocation members +    elem.type = static_cast<RenderScriptRuntime::Element::DataType>(results[0]); +    elem.type_kind = static_cast<RenderScriptRuntime::Element::DataKind>(results[1]); +    elem.type_vec_size = static_cast<uint32_t>(results[2]); +    elem.field_count = static_cast<uint32_t>(results[3]); + +    if (log) +        log->Printf("RenderScriptRuntime::JITElementPacked - data type %u, pixel type %u, vector size %u, field count %u", +                    *elem.type.get(), *elem.type_kind.get(), *elem.type_vec_size.get(), *elem.field_count.get()); + +    // If this Element has subelements then JIT rsaElementGetSubElements() for details about its fields +    if (*elem.field_count.get() > 0 && !JITSubelements(elem, context, frame_ptr)) +        return false; + +    return true; +} + +// JITs the RS runtime for information about the subelements/fields of a struct allocation +// This is necessary for infering the struct type so we can pretty print the allocation's contents. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITSubelements(Element& elem, const lldb::addr_t context, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!elem.element_ptr.isValid() || !elem.field_count.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITSubelements - Failed to find allocation details"); +        return false; +    } + +    const short num_exprs = 3; +    assert(num_exprs == (eExprSubelementsArrSize - eExprSubelementsId + 1) && "Invalid number of expressions"); + +    char expr_buffer[jit_max_expr_size]; +    uint64_t results; + +    // Iterate over struct fields. +    const uint32_t field_count = *elem.field_count.get(); +    for (unsigned int field_index = 0; field_index < field_count; ++field_index) +    { +        Element child; +        for (unsigned int expr_index = 0; expr_index < num_exprs; ++expr_index) +        { +            const char* expr_cstr = JITTemplate((ExpressionStrings) (eExprSubelementsId + expr_index)); +            int chars_written = snprintf(expr_buffer, jit_max_expr_size, expr_cstr, +                                         field_count, field_count, field_count, +                                         context, *elem.element_ptr.get(), field_count, field_index); +            if (chars_written < 0) +            { +                if (log) +                    log->Printf("RenderScriptRuntime::JITSubelements - Encoding error in snprintf()"); +                return false; +            } +            else if (chars_written >= jit_max_expr_size)              { -                if (m_scriptMappings[rs_script.script] != rsmodule_sp) +                if (log) +                    log->Printf("RenderScriptRuntime::JITSubelements - Expression too long"); +                return false; +            } + +            // Perform expression evaluation +            if (!EvalRSExpression(expr_buffer, frame_ptr, &results)) +                return false; + +            if (log) +                log->Printf("RenderScriptRuntime::JITSubelements - Expr result 0x%" PRIx64, results); + +            switch(expr_index) +            { +                case 0: // Element* of child +                    child.element_ptr = static_cast<addr_t>(results); +                    break; +                case 1: // Name of child                  { -                    if (log) +                    lldb::addr_t address = static_cast<addr_t>(results); +                    Error err; +                    std::string name; +                    GetProcess()->ReadCStringFromMemory(address, name, err); +                    if (!err.Fail()) +                        child.type_name = ConstString(name); +                    else                      { -                        log->Printf ("RenderScriptRuntime::FixupScriptDetails - Error: script %" PRIx64 " wants reassigned to new rsmodule '%s'.",  -                                     (uint64_t)rs_script.script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); +                        if (log) +                            log->Printf("RenderScriptRuntime::JITSubelements - Warning: Couldn't read field name");                      } +                    break;                  } +                case 2: // Array size of child +                    child.array_size = static_cast<uint32_t>(results); +                    break;              } -            else +        } + +        // We need to recursively JIT each Element field of the struct since +        // structs can be nested inside structs. +        if (!JITElementPacked(child, context, frame_ptr)) +            return false; +        elem.children.push_back(child); +    } + +    // Try to infer the name of the struct type so we can pretty print the allocation contents. +    FindStructTypeName(elem, frame_ptr); + +    return true; +} + +// JITs the RS runtime for the address of the last element in the allocation. +// The `elem_size` paramter represents the size of a single element, including padding. +// Which is needed as an offset from the last element pointer. +// Using this offset minus the starting address we can calculate the size of the allocation. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITAllocationSize(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!allocation->address.isValid() || !allocation->dimension.isValid() +        || !allocation->data_ptr.isValid() || !allocation->element.datum_size.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationSize - Failed to find allocation details"); +        return false; +    } + +    // Find dimensions +    unsigned int dim_x = allocation->dimension.get()->dim_1; +    unsigned int dim_y = allocation->dimension.get()->dim_2; +    unsigned int dim_z = allocation->dimension.get()->dim_3; + +    // Our plan of jitting the last element address doesn't seem to work for struct Allocations +    // Instead try to infer the size ourselves without any inter element padding. +    if (allocation->element.children.size() > 0) +    { +        if (dim_x == 0) dim_x = 1; +        if (dim_y == 0) dim_y = 1; +        if (dim_z == 0) dim_z = 1; + +        allocation->size = dim_x * dim_y * dim_z * *allocation->element.datum_size.get(); + +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationSize - Infered size of struct allocation %u", *allocation->size.get()); + +        return true; +    } + +    const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); +    char buffer[jit_max_expr_size]; + +    // Calculate last element +    dim_x = dim_x == 0 ? 0 : dim_x - 1; +    dim_y = dim_y == 0 ? 0 : dim_y - 1; +    dim_z = dim_z == 0 ? 0 : dim_z - 1; + +    int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), +                                 dim_x, dim_y, dim_z); +    if (chars_written < 0) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationSize - Encoding error in snprintf()"); +        return false; +    } +    else if (chars_written >= jit_max_expr_size) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationSize - Expression too long"); +        return false; +    } + +    uint64_t result = 0; +    if (!EvalRSExpression(buffer, frame_ptr, &result)) +        return false; + +    addr_t mem_ptr = static_cast<lldb::addr_t>(result); +    // Find pointer to last element and add on size of an element +    allocation->size = static_cast<uint32_t>(mem_ptr - *allocation->data_ptr.get()) + *allocation->element.datum_size.get(); + +    return true; +} + +// JITs the RS runtime for information about the stride between rows in the allocation. +// This is done to detect padding, since allocated memory is 16-byte aligned. +// Returns true on success, false otherwise +bool +RenderScriptRuntime::JITAllocationStride(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!allocation->address.isValid() || !allocation->data_ptr.isValid()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationStride - Failed to find allocation details"); +        return false; +    } + +    const char* expr_cstr = JITTemplate(eExprGetOffsetPtr); +    char buffer[jit_max_expr_size]; + +    int chars_written = snprintf(buffer, jit_max_expr_size, expr_cstr, *allocation->address.get(), +                                 0, 1, 0); +    if (chars_written < 0) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationStride - Encoding error in snprintf()"); +        return false; +    } +    else if (chars_written >= jit_max_expr_size) +    { +        if (log) +            log->Printf("RenderScriptRuntime::JITAllocationStride - Expression too long"); +        return false; +    } + +    uint64_t result = 0; +    if (!EvalRSExpression(buffer, frame_ptr, &result)) +        return false; + +    addr_t mem_ptr = static_cast<lldb::addr_t>(result); +    allocation->stride = static_cast<uint32_t>(mem_ptr - *allocation->data_ptr.get()); + +    return true; +} + +// JIT all the current runtime info regarding an allocation +bool +RenderScriptRuntime::RefreshAllocation(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    // GetOffsetPointer() +    if (!JITDataPointer(allocation, frame_ptr)) +        return false; + +    // rsaAllocationGetType() +    if (!JITTypePointer(allocation, frame_ptr)) +        return false; + +    // rsaTypeGetNativeData() +    if (!JITTypePacked(allocation, frame_ptr)) +        return false; + +    // rsaElementGetNativeData() +    if (!JITElementPacked(allocation->element, *allocation->context.get(), frame_ptr)) +        return false; + +    // Sets the datum_size member in Element +    SetElementSize(allocation->element); + +    // Use GetOffsetPointer() to infer size of the allocation +    if (!JITAllocationSize(allocation, frame_ptr)) +        return false; + +    return true; +} + +// Function attempts to set the type_name member of the paramaterised Element object. +// This string should be the name of the struct type the Element represents. +// We need this string for pretty printing the Element to users. +void +RenderScriptRuntime::FindStructTypeName(Element& elem, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    if (!elem.type_name.IsEmpty()) // Name already set +        return; +    else +        elem.type_name = Element::GetFallbackStructName(); // Default type name if we don't succeed + +    // Find all the global variables from the script rs modules +    VariableList variable_list; +    for (auto module_sp : m_rsmodules) +        module_sp->m_module->FindGlobalVariables(RegularExpression("."), true, UINT32_MAX, variable_list); + +    // Iterate over all the global variables looking for one with a matching type to the Element. +    // We make the assumption a match exists since there needs to be a global variable to reflect the +    // struct type back into java host code. +    for (uint32_t var_index = 0; var_index < variable_list.GetSize(); ++var_index) +    { +        const VariableSP var_sp(variable_list.GetVariableAtIndex(var_index)); +        if (!var_sp) +           continue; + +        ValueObjectSP valobj_sp = ValueObjectVariable::Create(frame_ptr, var_sp); +        if (!valobj_sp) +            continue; + +        // Find the number of variable fields. +        // If it has no fields, or more fields than our Element, then it can't be the struct we're looking for. +        // Don't check for equality since RS can add extra struct members for padding. +        size_t num_children = valobj_sp->GetNumChildren(); +        if (num_children > elem.children.size() || num_children == 0) +            continue; + +        // Iterate over children looking for members with matching field names. +        // If all the field names match, this is likely the struct we want. +        // +        //   TODO: This could be made more robust by also checking children data sizes, or array size +        bool found = true; +        for (size_t child_index = 0; child_index < num_children; ++child_index) +        { +            ValueObjectSP child = valobj_sp->GetChildAtIndex(child_index, true); +            if (!child || (child->GetName() != elem.children[child_index].type_name))              { -                m_scriptMappings[rs_script.script] = rsmodule_sp; -                rsmodule_sp->m_resname = rs_script.resname; -                if (log) -                { -                    log->Printf ("RenderScriptRuntime::FixupScriptDetails - script %" PRIx64 " associated with rsmodule '%s'.",  -                                    (uint64_t)rs_script.script, rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString()); -                } +                found = false; +                break; +            } +        } + +        // RS can add extra struct members for padding in the format '#rs_padding_[0-9]+' +        if (found && num_children < elem.children.size()) +        { +            const unsigned int size_diff = elem.children.size() - num_children; +            if (log) +                log->Printf("RenderScriptRuntime::FindStructTypeName - %u padding struct entries", size_diff); + +            for (unsigned int padding_index = 0; padding_index < size_diff; ++padding_index) +            { +                const ConstString& name = elem.children[num_children + padding_index].type_name; +                if (strcmp(name.AsCString(), "#rs_padding") < 0) +                    found = false;              }          } + +        // We've found a global var with matching type +        if (found) +        { +            // Dereference since our Element type isn't a pointer. +            if (valobj_sp->IsPointerType()) +            { +                Error err; +                ValueObjectSP deref_valobj = valobj_sp->Dereference(err); +                if (!err.Fail()) +                    valobj_sp = deref_valobj; +            } + +            // Save name of variable in Element. +            elem.type_name = valobj_sp->GetTypeName(); +            if (log) +                log->Printf("RenderScriptRuntime::FindStructTypeName - Element name set to %s", elem.type_name.AsCString()); + +            return; +        }      } -     +} + +// Function sets the datum_size member of Element. Representing the size of a single instance including padding. +// Assumes the relevant allocation information has already been jitted. +void +RenderScriptRuntime::SetElementSize(Element& elem) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +    const Element::DataType type = *elem.type.get(); +    assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT +                                                   && "Invalid allocation type"); + +    const unsigned int vec_size = *elem.type_vec_size.get(); +    unsigned int data_size = 0; +    unsigned int padding = 0; + +    // Element is of a struct type, calculate size recursively. +    if ((type == Element::RS_TYPE_NONE) && (elem.children.size() > 0)) +    { +        for (Element& child : elem.children) +        { +            SetElementSize(child); +            const unsigned int array_size = child.array_size.isValid() ? *child.array_size.get() : 1; +            data_size += *child.datum_size.get() * array_size; +        } +    } +    else if (type == Element::RS_TYPE_UNSIGNED_5_6_5 || type == Element::RS_TYPE_UNSIGNED_5_5_5_1 || +             type == Element::RS_TYPE_UNSIGNED_4_4_4_4) // These have been packed already +    { +        data_size = AllocationDetails::RSTypeToFormat[type][eElementSize]; +    } +    else if (type < Element::RS_TYPE_ELEMENT) +    { +        data_size = vec_size * AllocationDetails::RSTypeToFormat[type][eElementSize]; +        if (vec_size == 3) +            padding = AllocationDetails::RSTypeToFormat[type][eElementSize]; +    } +    else +        data_size = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); + +    elem.padding = padding; +    elem.datum_size = data_size + padding; +    if (log) +        log->Printf("RenderScriptRuntime::SetElementSize - element size set to %u", data_size + padding); +} + +// Given an allocation, this function copies the allocation contents from device into a buffer on the heap. +// Returning a shared pointer to the buffer containing the data. +std::shared_ptr<uint8_t> +RenderScriptRuntime::GetAllocationData(AllocationDetails* allocation, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    // JIT all the allocation details +    if (allocation->shouldRefresh()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetAllocationData - Allocation details not calculated yet, jitting info"); + +        if (!RefreshAllocation(allocation, frame_ptr)) +        { +            if (log) +                log->Printf("RenderScriptRuntime::GetAllocationData - Couldn't JIT allocation details"); +            return nullptr; +        } +    } + +    assert(allocation->data_ptr.isValid() && allocation->element.type.isValid() && allocation->element.type_vec_size.isValid() +           && allocation->size.isValid() && "Allocation information not available"); + +    // Allocate a buffer to copy data into +    const unsigned int size = *allocation->size.get(); +    std::shared_ptr<uint8_t> buffer(new uint8_t[size]); +    if (!buffer) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetAllocationData - Couldn't allocate a %u byte buffer", size); +        return nullptr; +    } + +    // Read the inferior memory +    Error error; +    lldb::addr_t data_ptr = *allocation->data_ptr.get(); +    GetProcess()->ReadMemory(data_ptr, buffer.get(), size, error); +    if (error.Fail()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetAllocationData - '%s' Couldn't read %u bytes of allocation data from 0x%" PRIx64, +                        error.AsCString(), size, data_ptr); +        return nullptr; +    } + +    return buffer; +} + +// Function copies data from a binary file into an allocation. +// There is a header at the start of the file, FileHeader, before the data content itself. +// Information from this header is used to display warnings to the user about incompatabilities +bool +RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const char* filename, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    // Find allocation with the given id +    AllocationDetails* alloc = FindAllocByID(strm, alloc_id); +    if (!alloc) +        return false; + +    if (log) +        log->Printf("RenderScriptRuntime::LoadAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); + +    // JIT all the allocation details +    if (alloc->shouldRefresh()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::LoadAllocation - Allocation details not calculated yet, jitting info"); + +        if (!RefreshAllocation(alloc, frame_ptr)) +        { +            if (log) +                log->Printf("RenderScriptRuntime::LoadAllocation - Couldn't JIT allocation details"); +            return false; +        } +    } + +    assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() && alloc->element.type_vec_size.isValid() +           && alloc->size.isValid() && alloc->element.datum_size.isValid() && "Allocation information not available"); + +    // Check we can read from file +    FileSpec file(filename, true); +    if (!file.Exists()) +    { +        strm.Printf("Error: File %s does not exist", filename); +        strm.EOL(); +        return false; +    } + +    if (!file.Readable()) +    { +        strm.Printf("Error: File %s does not have readable permissions", filename); +        strm.EOL(); +        return false; +    } + +    // Read file into data buffer +    DataBufferSP data_sp(file.ReadFileContents()); + +    // Cast start of buffer to FileHeader and use pointer to read metadata +    void* file_buffer = data_sp->GetBytes(); +    const AllocationDetails::FileHeader* head = static_cast<AllocationDetails::FileHeader*>(file_buffer); + +    // Advance buffer past header +    file_buffer = static_cast<uint8_t*>(file_buffer) + head->hdr_size; + +    if (log) +        log->Printf("RenderScriptRuntime::LoadAllocation - header type %u, element size %u", +                    head->type, head->element_size); + +    // Check if the target allocation and file both have the same number of bytes for an Element +    if (*alloc->element.datum_size.get() != head->element_size) +    { +        strm.Printf("Warning: Mismatched Element sizes - file %u bytes, allocation %u bytes", +                    head->element_size, *alloc->element.datum_size.get()); +        strm.EOL(); +    } + +    // Check if the target allocation and file both have the same integral type +    const unsigned int type = static_cast<unsigned int>(*alloc->element.type.get()); +    if (type != head->type) +    { +        // Enum value isn't monotonous, so doesn't always index RsDataTypeToString array +        unsigned int printable_target_type_index = type; +        unsigned int printable_head_type_index = head->type; +        if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT) +            printable_target_type_index = static_cast<Element::DataType>( +                                         (type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); + +        if (head->type >= Element::RS_TYPE_ELEMENT && head->type <= Element::RS_TYPE_FONT) +            printable_head_type_index = static_cast<Element::DataType>( +                                        (head->type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1); + +        const char* file_type_cstr = AllocationDetails::RsDataTypeToString[printable_head_type_index][0]; +        const char* target_type_cstr = AllocationDetails::RsDataTypeToString[printable_target_type_index][0]; + +        strm.Printf("Warning: Mismatched Types - file '%s' type, allocation '%s' type", +                    file_type_cstr, target_type_cstr); +        strm.EOL(); +    } + +    // Calculate size of allocation data in file +    size_t length = data_sp->GetByteSize() - head->hdr_size; + +    // Check if the target allocation and file both have the same total data size. +    const unsigned int alloc_size = *alloc->size.get(); +    if (alloc_size != length) +    { +        strm.Printf("Warning: Mismatched allocation sizes - file 0x%" PRIx64 " bytes, allocation 0x%x bytes", +                    (uint64_t) length, alloc_size); +        strm.EOL(); +        length = alloc_size < length ? alloc_size : length; // Set length to copy to minimum +    } + +    // Copy file data from our buffer into the target allocation. +    lldb::addr_t alloc_data = *alloc->data_ptr.get(); +    Error error; +    size_t bytes_written = GetProcess()->WriteMemory(alloc_data, file_buffer, length, error); +    if (!error.Success() || bytes_written != length) +    { +        strm.Printf("Error: Couldn't write data to allocation %s", error.AsCString()); +        strm.EOL(); +        return false; +    } + +    strm.Printf("Contents of file '%s' read into allocation %u", filename, alloc->id); +    strm.EOL(); + +    return true; +} + +// Function copies allocation contents into a binary file. +// This file can then be loaded later into a different allocation. +// There is a header, FileHeader, before the allocation data containing meta-data. +bool +RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const char* filename, StackFrame* frame_ptr) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    // Find allocation with the given id +    AllocationDetails* alloc = FindAllocByID(strm, alloc_id); +    if (!alloc) +        return false; + +    if (log) +        log->Printf("RenderScriptRuntime::SaveAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); + +     // JIT all the allocation details +    if (alloc->shouldRefresh()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::SaveAllocation - Allocation details not calculated yet, jitting info"); + +        if (!RefreshAllocation(alloc, frame_ptr)) +        { +            if (log) +                log->Printf("RenderScriptRuntime::SaveAllocation - Couldn't JIT allocation details"); +            return false; +        } +    } + +    assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() && alloc->element.type_vec_size.isValid() && alloc->element.datum_size.get() +           && alloc->element.type_kind.isValid() && alloc->dimension.isValid() && "Allocation information not available"); + +    // Check we can create writable file +    FileSpec file_spec(filename, true); +    File file(file_spec, File::eOpenOptionWrite | File::eOpenOptionCanCreate | File::eOpenOptionTruncate); +    if (!file) +    { +        strm.Printf("Error: Failed to open '%s' for writing", filename); +        strm.EOL(); +        return false; +    } + +    // Read allocation into buffer of heap memory +    const std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr); +    if (!buffer) +    { +        strm.Printf("Error: Couldn't read allocation data into buffer"); +        strm.EOL(); +        return false; +    } + +    // Create the file header +    AllocationDetails::FileHeader head; +    head.ident[0] = 'R'; head.ident[1] = 'S'; head.ident[2] = 'A'; head.ident[3] = 'D'; +    head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader)); +    head.type = static_cast<uint16_t>(*alloc->element.type.get()); +    head.kind = static_cast<uint32_t>(*alloc->element.type_kind.get()); +    head.dims[0] = static_cast<uint32_t>(alloc->dimension.get()->dim_1); +    head.dims[1] = static_cast<uint32_t>(alloc->dimension.get()->dim_2); +    head.dims[2] = static_cast<uint32_t>(alloc->dimension.get()->dim_3); +    head.element_size = static_cast<uint32_t>(*alloc->element.datum_size.get()); + +    // Write the file header +    size_t num_bytes = sizeof(AllocationDetails::FileHeader); +    Error err = file.Write(static_cast<const void*>(&head), num_bytes); +    if (!err.Success()) +    { +        strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); +        strm.EOL(); +        return false; +    } + +    // Write allocation data to file +    num_bytes = static_cast<size_t>(*alloc->size.get()); +    if (log) +        log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, buffer.get()); + +    err = file.Write(buffer.get(), num_bytes); +    if (!err.Success()) +    { +        strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename); +        strm.EOL(); +        return false; +    } + +    strm.Printf("Allocation written to file '%s'", filename); +    strm.EOL(); +    return true;  }  bool @@ -496,7 +2256,14 @@ RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp)          for (const auto &rs_module : m_rsmodules)          {              if (rs_module->m_module == module_sp) +            { +                // Check if the user has enabled automatically breaking on +                // all RS kernels. +                if (m_breakAllKernels) +                    BreakOnModuleKernels(rs_module); +                  return false; +            }          }          bool module_loaded = false;          switch (GetModuleKind(module_sp)) @@ -585,7 +2352,6 @@ RenderScriptRuntime::Update()      }  } -  // The maximum line length of an .rs.info packet  #define MAXLINE 500 @@ -609,12 +2375,12 @@ RSModuleDescriptor::ParseRSInfo()          std::string info((const char *)buffer->GetBytes());          std::vector<std::string> info_lines; -        size_t lpos = info.find_first_of("\n"); +        size_t lpos = info.find('\n');          while (lpos != std::string::npos)          {              info_lines.push_back(info.substr(0, lpos));              info = info.substr(lpos + 1); -            lpos = info.find_first_of("\n"); +            lpos = info.find('\n');          }          size_t offset = 0;          while (offset < info_lines.size()) @@ -710,7 +2476,6 @@ RenderScriptRuntime::Status(Stream &strm) const              strm.Indent(b.second->defn->name);              strm.EOL();          } -        strm.EOL();      }       else      { @@ -728,15 +2493,21 @@ RenderScriptRuntime::DumpContexts(Stream &strm) const      std::map<addr_t, uint64_t> contextReferences; -    for (const auto &script : m_scripts) +    // Iterate over all of the currently discovered scripts. +    // Note: We cant push or pop from m_scripts inside this loop or it may invalidate script. +    for (const auto & script : m_scripts)      { -        if (contextReferences.find(script.context) != contextReferences.end()) +        if (!script->context.isValid()) +            continue; +        lldb::addr_t context = *script->context; + +        if (contextReferences.find(context) != contextReferences.end())          { -            contextReferences[script.context]++; +            contextReferences[context]++;          }          else          { -            contextReferences[script.context] = 1; +            contextReferences[context] = 1;          }      } @@ -767,69 +2538,502 @@ RenderScriptRuntime::DumpKernels(Stream &strm) const      strm.IndentLess();  } -void  -RenderScriptRuntime::AttemptBreakpointAtKernelName(Stream &strm, const char* name, Error& error) +RenderScriptRuntime::AllocationDetails* +RenderScriptRuntime::FindAllocByID(Stream &strm, const uint32_t alloc_id)  { -    if (!name)  +    AllocationDetails* alloc = nullptr; + +    // See if we can find allocation using id as an index; +    if (alloc_id <= m_allocations.size() && alloc_id != 0 +        && m_allocations[alloc_id-1]->id == alloc_id)      { -        error.SetErrorString("invalid kernel name"); -        return; +        alloc = m_allocations[alloc_id-1].get(); +        return alloc;      } -    bool kernels_found; -    ConstString kernel_name(name); -    for (const auto &module : m_rsmodules) +    // Fallback to searching +    for (const auto & a : m_allocations)      { -        for (const auto &kernel : module->m_kernels) +       if (a->id == alloc_id) +       { +           alloc = a.get(); +           break; +       } +    } + +    if (alloc == nullptr) +    { +        strm.Printf("Error: Couldn't find allocation with id matching %u", alloc_id); +        strm.EOL(); +    } + +    return alloc; +} + +// Prints the contents of an allocation to the output stream, which may be a file +bool +RenderScriptRuntime::DumpAllocation(Stream &strm, StackFrame* frame_ptr, const uint32_t id) +{ +    Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +    // Check we can find the desired allocation +    AllocationDetails* alloc = FindAllocByID(strm, id); +    if (!alloc) +        return false; // FindAllocByID() will print error message for us here + +    if (log) +        log->Printf("RenderScriptRuntime::DumpAllocation - Found allocation 0x%" PRIx64, *alloc->address.get()); + +    // Check we have information about the allocation, if not calculate it +    if (alloc->shouldRefresh()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::DumpAllocation - Allocation details not calculated yet, jitting info"); + +        // JIT all the allocation information +        if (!RefreshAllocation(alloc, frame_ptr))          { -            if (kernel.m_name == kernel_name) -            { -                //Attempt to set a breakpoint on this symbol, within the module library -                //If it's not found, it's likely debug info is unavailable - set a -                //breakpoint on <name>.expand and emit a warning. +            strm.Printf("Error: Couldn't JIT allocation details"); +            strm.EOL(); +            return false; +        } +    } + +    // Establish format and size of each data element +    const unsigned int vec_size = *alloc->element.type_vec_size.get(); +    const Element::DataType type = *alloc->element.type.get(); + +    assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT +                                                   && "Invalid allocation type"); + +    lldb::Format format; +    if (type >= Element::RS_TYPE_ELEMENT) +        format = eFormatHex; +    else +        format = vec_size == 1 ? static_cast<lldb::Format>(AllocationDetails::RSTypeToFormat[type][eFormatSingle]) +                               : static_cast<lldb::Format>(AllocationDetails::RSTypeToFormat[type][eFormatVector]); + +    const unsigned int data_size = *alloc->element.datum_size.get(); + +    if (log) +        log->Printf("RenderScriptRuntime::DumpAllocation - Element size %u bytes, including padding", data_size); + +    // Allocate a buffer to copy data into +    std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr); +    if (!buffer) +    { +        strm.Printf("Error: Couldn't read allocation data"); +        strm.EOL(); +        return false; +    } + +    // Calculate stride between rows as there may be padding at end of rows since +    // allocated memory is 16-byte aligned +    if (!alloc->stride.isValid()) +    { +        if (alloc->dimension.get()->dim_2 == 0) // We only have one dimension +            alloc->stride = 0; +        else if (!JITAllocationStride(alloc, frame_ptr)) +        { +            strm.Printf("Error: Couldn't calculate allocation row stride"); +            strm.EOL(); +            return false; +        } +    } +    const unsigned int stride = *alloc->stride.get(); +    const unsigned int size = *alloc->size.get(); // Size of whole allocation +    const unsigned int padding = alloc->element.padding.isValid() ? *alloc->element.padding.get() : 0; +    if (log) +        log->Printf("RenderScriptRuntime::DumpAllocation - stride %u bytes, size %u bytes, padding %u", stride, size, padding); + +    // Find dimensions used to index loops, so need to be non-zero +    unsigned int dim_x = alloc->dimension.get()->dim_1; +    dim_x = dim_x == 0 ? 1 : dim_x; -                const Symbol* kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(kernel_name, eSymbolTypeCode); +    unsigned int dim_y = alloc->dimension.get()->dim_2; +    dim_y = dim_y == 0 ? 1 : dim_y; -                if (!kernel_sym) +    unsigned int dim_z = alloc->dimension.get()->dim_3; +    dim_z = dim_z == 0 ? 1 : dim_z; + +    // Use data extractor to format output +    const uint32_t archByteSize = GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); +    DataExtractor alloc_data(buffer.get(), size, GetProcess()->GetByteOrder(), archByteSize); + +    unsigned int offset = 0;   // Offset in buffer to next element to be printed +    unsigned int prev_row = 0; // Offset to the start of the previous row + +    // Iterate over allocation dimensions, printing results to user +    strm.Printf("Data (X, Y, Z):"); +    for (unsigned int z = 0; z < dim_z; ++z) +    { +        for (unsigned int y = 0; y < dim_y; ++y) +        { +            // Use stride to index start of next row. +            if (!(y==0 && z==0)) +                offset = prev_row + stride; +            prev_row = offset; + +            // Print each element in the row individually +            for (unsigned int x = 0; x < dim_x; ++x) +            { +                strm.Printf("\n(%u, %u, %u) = ", x, y, z); +                if ((type == Element::RS_TYPE_NONE) && (alloc->element.children.size() > 0) && +                    (alloc->element.type_name != Element::GetFallbackStructName()))                  { -                    std::string kernel_name_expanded(name); -                    kernel_name_expanded.append(".expand"); -                    kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode); +                    // Here we are dumping an Element of struct type. +                    // This is done using expression evaluation with the name of the struct type and pointer to element. -                    if (kernel_sym) -                    { -                        strm.Printf("Kernel '%s' could not be found, but expansion exists. ", name);  -                        strm.Printf("Breakpoint placed on expanded kernel. Have you compiled in debug mode?"); -                        strm.EOL(); -                    } -                    else +                    // Don't print the name of the resulting expression, since this will be '$[0-9]+' +                    DumpValueObjectOptions expr_options; +                    expr_options.SetHideName(true); + +                    // Setup expression as derefrencing a pointer cast to element address. +                    char expr_char_buffer[jit_max_expr_size]; +                    int chars_written = snprintf(expr_char_buffer, jit_max_expr_size, "*(%s*) 0x%" PRIx64, +                                        alloc->element.type_name.AsCString(), *alloc->data_ptr.get() + offset); + +                    if (chars_written < 0 || chars_written >= jit_max_expr_size)                      { -                        error.SetErrorStringWithFormat("Could not locate symbols for loaded kernel '%s'.", name); -                        return; +                        if (log) +                            log->Printf("RenderScriptRuntime::DumpAllocation- Error in snprintf()"); +                        continue;                      } -                } -                addr_t bp_addr = kernel_sym->GetLoadAddress(&GetProcess()->GetTarget()); -                if (bp_addr == LLDB_INVALID_ADDRESS) +                    // Evaluate expression +                    ValueObjectSP expr_result; +                    GetProcess()->GetTarget().EvaluateExpression(expr_char_buffer, frame_ptr, expr_result); + +                    // Print the results to our stream. +                    expr_result->Dump(strm, expr_options); +                } +                else                  { -                    error.SetErrorStringWithFormat("Could not locate load address for symbols of kernel '%s'.", name); -                    return; +                    alloc_data.Dump(&strm, offset, format, data_size - padding, 1, 1, LLDB_INVALID_ADDRESS, 0, 0);                  } +                offset += data_size; +            } +        } +    } +    strm.EOL(); + +    return true; +} + +// Prints infomation regarding all the currently loaded allocations. +// These details are gathered by jitting the runtime, which has as latency. +void +RenderScriptRuntime::ListAllocations(Stream &strm, StackFrame* frame_ptr, bool recompute) +{ +    strm.Printf("RenderScript Allocations:"); +    strm.EOL(); +    strm.IndentMore(); + +    for (auto &alloc : m_allocations) +    { +        // JIT the allocation info if we haven't done it, or the user forces us to. +        bool do_refresh = alloc->shouldRefresh() || recompute; + +        // JIT current allocation information +        if (do_refresh && !RefreshAllocation(alloc.get(), frame_ptr)) +        { +            strm.Printf("Error: Couldn't evaluate details for allocation %u\n", alloc->id); +            continue; +        } -                BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint(bp_addr, false, false); -                strm.Printf("Breakpoint %" PRIu64 ": kernel '%s' within script '%s'", (uint64_t)bp->GetID(), name, module->m_resname.c_str()); -                strm.EOL(); +        strm.Printf("%u:\n",alloc->id); +        strm.IndentMore(); -                kernels_found = true; +        strm.Indent("Context: "); +        if (!alloc->context.isValid()) +            strm.Printf("unknown\n"); +        else +            strm.Printf("0x%" PRIx64 "\n", *alloc->context.get()); + +        strm.Indent("Address: "); +        if (!alloc->address.isValid()) +            strm.Printf("unknown\n"); +        else +            strm.Printf("0x%" PRIx64 "\n", *alloc->address.get()); + +        strm.Indent("Data pointer: "); +        if (!alloc->data_ptr.isValid()) +            strm.Printf("unknown\n"); +        else +            strm.Printf("0x%" PRIx64 "\n", *alloc->data_ptr.get()); + +        strm.Indent("Dimensions: "); +        if (!alloc->dimension.isValid()) +            strm.Printf("unknown\n"); +        else +            strm.Printf("(%d, %d, %d)\n", alloc->dimension.get()->dim_1, +                                          alloc->dimension.get()->dim_2, +                                          alloc->dimension.get()->dim_3); + +        strm.Indent("Data Type: "); +        if (!alloc->element.type.isValid() || !alloc->element.type_vec_size.isValid()) +            strm.Printf("unknown\n"); +        else +        { +            const int vector_size = *alloc->element.type_vec_size.get(); +            Element::DataType type = *alloc->element.type.get(); + +            if (!alloc->element.type_name.IsEmpty()) +                strm.Printf("%s\n", alloc->element.type_name.AsCString()); +            else +            { +                // Enum value isn't monotonous, so doesn't always index RsDataTypeToString array +                if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT) +                    type = static_cast<Element::DataType>((type - Element::RS_TYPE_ELEMENT) +  Element::RS_TYPE_MATRIX_2X2 + 1); + +                if (type >= (sizeof(AllocationDetails::RsDataTypeToString) / sizeof(AllocationDetails::RsDataTypeToString[0])) +                    || vector_size > 4 || vector_size < 1) +                    strm.Printf("invalid type\n"); +                else +                    strm.Printf("%s\n", AllocationDetails::RsDataTypeToString[static_cast<unsigned int>(type)][vector_size-1]);              }          } + +        strm.Indent("Data Kind: "); +        if (!alloc->element.type_kind.isValid()) +            strm.Printf("unknown\n"); +        else +        { +            const Element::DataKind kind = *alloc->element.type_kind.get(); +            if (kind < Element::RS_KIND_USER || kind > Element::RS_KIND_PIXEL_YUV) +                strm.Printf("invalid kind\n"); +            else +                strm.Printf("%s\n", AllocationDetails::RsDataKindToString[static_cast<unsigned int>(kind)]); +        } + +        strm.EOL(); +        strm.IndentLess();      } +    strm.IndentLess(); +} -    if (!kernels_found) +// Set breakpoints on every kernel found in RS module +void +RenderScriptRuntime::BreakOnModuleKernels(const RSModuleDescriptorSP rsmodule_sp) +{ +    for (const auto &kernel : rsmodule_sp->m_kernels)      { -        error.SetErrorString("kernel name not found"); +        // Don't set breakpoint on 'root' kernel +        if (strcmp(kernel.m_name.AsCString(), "root") == 0) +            continue; + +        CreateKernelBreakpoint(kernel.m_name);      } -    return; +} + +// Method is internally called by the 'kernel breakpoint all' command to +// enable or disable breaking on all kernels. +// +// When do_break is true we want to enable this functionality. +// When do_break is false we want to disable it. +void +RenderScriptRuntime::SetBreakAllKernels(bool do_break, TargetSP target) +{ +    Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +    InitSearchFilter(target); + +    // Set breakpoints on all the kernels +    if (do_break && !m_breakAllKernels) +    { +        m_breakAllKernels = true; + +        for (const auto &module : m_rsmodules) +            BreakOnModuleKernels(module); + +        if (log) +            log->Printf("RenderScriptRuntime::SetBreakAllKernels(True)" +                        "- breakpoints set on all currently loaded kernels"); +    } +    else if (!do_break && m_breakAllKernels) // Breakpoints won't be set on any new kernels. +    { +        m_breakAllKernels = false; + +        if (log) +            log->Printf("RenderScriptRuntime::SetBreakAllKernels(False) - breakpoints no longer automatically set"); +    } +} + +// Given the name of a kernel this function creates a breakpoint using our +// own breakpoint resolver, and returns the Breakpoint shared pointer. +BreakpointSP +RenderScriptRuntime::CreateKernelBreakpoint(const ConstString& name) +{ +    Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +    if (!m_filtersp) +    { +        if (log) +            log->Printf("RenderScriptRuntime::CreateKernelBreakpoint - Error: No breakpoint search filter set"); +        return nullptr; +    } + +    BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name)); +    BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint(m_filtersp, resolver_sp, false, false, false); + +    // Give RS breakpoints a specific name, so the user can manipulate them as a group. +    Error err; +    if (!bp->AddName("RenderScriptKernel", err) && log) +        log->Printf("RenderScriptRuntime::CreateKernelBreakpoint: Error setting break name, %s", err.AsCString()); + +    return bp; +} + +// Given an expression for a variable this function tries to calculate the variable's value. +// If this is possible it returns true and sets the uint64_t parameter to the variables unsigned value. +// Otherwise function returns false. +bool +RenderScriptRuntime::GetFrameVarAsUnsigned(const StackFrameSP frame_sp, const char* var_name, uint64_t& val) +{ +    Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +    Error error; +    VariableSP var_sp; + +    // Find variable in stack frame +    ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath(var_name, +                                                                       eNoDynamicValues, +                                                                       StackFrame::eExpressionPathOptionCheckPtrVsMember | +                                                                       StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, +                                                                       var_sp, +                                                                       error)); +    if (!error.Success()) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't find '%s' in frame", var_name); + +        return false; +    } + +    // Find the unsigned int value for the variable +    bool success = false; +    val = value_sp->GetValueAsUnsigned(0, &success); +    if (!success) +    { +        if (log) +            log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't parse '%s' as an unsigned int", var_name); + +        return false; +    } + +    return true; +} + +// Callback when a kernel breakpoint hits and we're looking for a specific coordinate. +// Baton parameter contains a pointer to the target coordinate we want to break on. +// Function then checks the .expand frame for the current coordinate and breaks to user if it matches. +// Parameter 'break_id' is the id of the Breakpoint which made the callback. +// Parameter 'break_loc_id' is the id for the BreakpointLocation which was hit, +// a single logical breakpoint can have multiple addresses. +bool +RenderScriptRuntime::KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx, +                                         user_id_t break_id, user_id_t break_loc_id) +{ +    Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +    assert(baton && "Error: null baton in conditional kernel breakpoint callback"); + +    // Coordinate we want to stop on +    const int* target_coord = static_cast<const int*>(baton); + +    if (log) +        log->Printf("RenderScriptRuntime::KernelBreakpointHit - Break ID %" PRIu64 ", target coord (%d, %d, %d)", +                    break_id, target_coord[0], target_coord[1], target_coord[2]); + +    // Go up one stack frame to .expand kernel +    ExecutionContext context(ctx->exe_ctx_ref); +    ThreadSP thread_sp = context.GetThreadSP(); +    if (!thread_sp->SetSelectedFrameByIndex(1)) +    { +        if (log) +            log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't go up stack frame"); + +       return false; +    } + +    StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); +    if (!frame_sp) +    { +        if (log) +            log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't select .expand stack frame"); + +        return false; +    } + +    // Get values for variables in .expand frame that tell us the current kernel invocation +    const char* coord_expressions[] = {"rsIndex", "p->current.y", "p->current.z"}; +    uint64_t current_coord[3] = {0, 0, 0}; + +    for(int i = 0; i < 3; ++i) +    { +        if (!GetFrameVarAsUnsigned(frame_sp, coord_expressions[i], current_coord[i])) +            return false; + +        if (log) +            log->Printf("RenderScriptRuntime::KernelBreakpointHit, %s = %" PRIu64, coord_expressions[i], current_coord[i]); +    } + +    // Check if the current kernel invocation coordinate matches our target coordinate +    if (current_coord[0] == static_cast<uint64_t>(target_coord[0]) && +        current_coord[1] == static_cast<uint64_t>(target_coord[1]) && +        current_coord[2] == static_cast<uint64_t>(target_coord[2])) +    { +        if (log) +             log->Printf("RenderScriptRuntime::KernelBreakpointHit, BREAKING %" PRIu64 ", %" PRIu64 ", %" PRIu64, +                         current_coord[0], current_coord[1], current_coord[2]); + +        BreakpointSP breakpoint_sp = context.GetTargetPtr()->GetBreakpointByID(break_id); +        assert(breakpoint_sp != nullptr && "Error: Couldn't find breakpoint matching break id for callback"); +        breakpoint_sp->SetEnabled(false); // Optimise since conditional breakpoint should only be hit once. +        return true; +    } + +    // No match on coordinate +    return false; +} + +// Tries to set a breakpoint on the start of a kernel, resolved using the kernel name. +// Argument 'coords', represents a three dimensional coordinate which can be used to specify +// a single kernel instance to break on. If this is set then we add a callback to the breakpoint. +void +RenderScriptRuntime::PlaceBreakpointOnKernel(Stream &strm, const char* name, const std::array<int,3> coords, +                                             Error& error, TargetSP target) +{ +    if (!name) +    { +        error.SetErrorString("invalid kernel name"); +        return; +    } + +    InitSearchFilter(target); + +    ConstString kernel_name(name); +    BreakpointSP bp = CreateKernelBreakpoint(kernel_name); + +    // We have a conditional breakpoint on a specific coordinate +    if (coords[0] != -1) +    { +        strm.Printf("Conditional kernel breakpoint on coordinate %d, %d, %d", coords[0], coords[1], coords[2]); +        strm.EOL(); + +        // Allocate memory for the baton, and copy over coordinate +        int* baton = new int[3]; +        baton[0] = coords[0]; baton[1] = coords[1]; baton[2] = coords[2]; + +        // Create a callback that will be invoked everytime the breakpoint is hit. +        // The baton object passed to the handler is the target coordinate we want to break on. +        bp->SetCallback(KernelBreakpointHit, baton, true); + +        // Store a shared pointer to the baton, so the memory will eventually be cleaned up after destruction +        m_conditional_breaks[bp->GetID()] = std::shared_ptr<int>(baton); +    } + +    if (bp) +        bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false);  }  void @@ -845,12 +3049,49 @@ RenderScriptRuntime::DumpModules(Stream &strm) const      strm.IndentLess();  } +RenderScriptRuntime::ScriptDetails* +RenderScriptRuntime::LookUpScript(addr_t address, bool create) +{ +    for (const auto & s : m_scripts) +    { +        if (s->script.isValid()) +            if (*s->script == address) +                return s.get(); +    } +    if (create) +    { +        std::unique_ptr<ScriptDetails> s(new ScriptDetails); +        s->script = address; +        m_scripts.push_back(std::move(s)); +        return m_scripts.back().get(); +    } +    return nullptr; +} + +RenderScriptRuntime::AllocationDetails* +RenderScriptRuntime::LookUpAllocation(addr_t address, bool create) +{ +    for (const auto & a : m_allocations) +    { +        if (a->address.isValid()) +            if (*a->address == address) +                return a.get(); +    } +    if (create) +    { +        std::unique_ptr<AllocationDetails> a(new AllocationDetails); +        a->address = address; +        m_allocations.push_back(std::move(a)); +        return m_allocations.back().get(); +    } +    return nullptr; +} +  void  RSModuleDescriptor::Dump(Stream &strm) const  {      strm.Indent();      m_module->GetFileSpec().Dump(&strm); -    m_module->ParseAllDebugSymbols();      if(m_module->GetNumCompileUnits())      {          strm.Indent("Debug info loaded."); @@ -931,8 +3172,7 @@ RSKernelDescriptor::Dump(Stream &strm) const  class CommandObjectRenderScriptRuntimeModuleProbe : public CommandObjectParsed  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeModuleProbe(CommandInterpreter &interpreter)          : CommandObjectParsed(interpreter, "renderscript module probe",                                "Initiates a Probe of all loaded modules for kernels and other renderscript objects.", @@ -941,10 +3181,10 @@ class CommandObjectRenderScriptRuntimeModuleProbe : public CommandObjectParsed      {      } -    ~CommandObjectRenderScriptRuntimeModuleProbe() {} +    ~CommandObjectRenderScriptRuntimeModuleProbe() override = default;      bool -    DoExecute(Args &command, CommandReturnObject &result) +    DoExecute(Args &command, CommandReturnObject &result) override      {          const size_t argc = command.GetArgumentCount();          if (argc == 0) @@ -970,8 +3210,7 @@ class CommandObjectRenderScriptRuntimeModuleProbe : public CommandObjectParsed  class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeModuleDump(CommandInterpreter &interpreter)          : CommandObjectParsed(interpreter, "renderscript module dump",                                "Dumps renderscript specific information for all modules.", "renderscript module dump", @@ -979,10 +3218,10 @@ class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed      {      } -    ~CommandObjectRenderScriptRuntimeModuleDump() {} +    ~CommandObjectRenderScriptRuntimeModuleDump() override = default;      bool -    DoExecute(Args &command, CommandReturnObject &result) +    DoExecute(Args &command, CommandReturnObject &result) override      {          RenderScriptRuntime *runtime =              (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); @@ -994,8 +3233,7 @@ class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed  class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeModule(CommandInterpreter &interpreter)          : CommandObjectMultiword(interpreter, "renderscript module", "Commands that deal with renderscript modules.",                                   NULL) @@ -1004,13 +3242,12 @@ class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword          LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleDump(interpreter)));      } -    ~CommandObjectRenderScriptRuntimeModule() {} +    ~CommandObjectRenderScriptRuntimeModule() override = default;  };  class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeKernelList(CommandInterpreter &interpreter)          : CommandObjectParsed(interpreter, "renderscript kernel list",                                "Lists renderscript kernel names and associated script resources.", "renderscript kernel list", @@ -1018,10 +3255,10 @@ class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed      {      } -    ~CommandObjectRenderScriptRuntimeKernelList() {} +    ~CommandObjectRenderScriptRuntimeKernelList() override = default;      bool -    DoExecute(Args &command, CommandReturnObject &result) +    DoExecute(Args &command, CommandReturnObject &result) override      {          RenderScriptRuntime *runtime =              (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); @@ -1031,52 +3268,213 @@ class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed      }  }; -class CommandObjectRenderScriptRuntimeKernelBreakpoint : public CommandObjectParsed +class CommandObjectRenderScriptRuntimeKernelBreakpointSet : public CommandObjectParsed  { -  private: -  public: -    CommandObjectRenderScriptRuntimeKernelBreakpoint(CommandInterpreter &interpreter) -        : CommandObjectParsed(interpreter, "renderscript kernel breakpoint", -                              "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint", -                              eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) +public: +    CommandObjectRenderScriptRuntimeKernelBreakpointSet(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript kernel breakpoint set", +                              "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint set <kernel_name> [-c x,y,z]", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options(interpreter)      {      } -    ~CommandObjectRenderScriptRuntimeKernelBreakpoint() {} +    ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() override = default; -    bool -    DoExecute(Args &command, CommandReturnObject &result) +    Options* +    GetOptions() override      { -        const size_t argc = command.GetArgumentCount(); -        if (argc == 1) +        return &m_options; +    } + +    class CommandOptions : public Options +    { +    public: +        CommandOptions(CommandInterpreter &interpreter) : Options(interpreter)          { -            RenderScriptRuntime *runtime = -                (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); +        } + +        ~CommandOptions() override = default; +        Error +        SetOptionValue(uint32_t option_idx, const char *option_arg) override +        {              Error error; -            runtime->AttemptBreakpointAtKernelName(result.GetOutputStream(), command.GetArgumentAtIndex(0), error); +            const int short_option = m_getopt_table[option_idx].val; -            if (error.Success()) +            switch (short_option)              { -                result.AppendMessage("Breakpoint(s) created"); -                result.SetStatus(eReturnStatusSuccessFinishResult); -                return true; +                case 'c': +                    if (!ParseCoordinate(option_arg)) +                        error.SetErrorStringWithFormat("Couldn't parse coordinate '%s', should be in format 'x,y,z'.", option_arg); +                    break; +                default: +                    error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +                    break;              } +            return error; +        } + +        // -c takes an argument of the form 'num[,num][,num]'. +        // Where 'id_cstr' is this argument with the whitespace trimmed. +        // Missing coordinates are defaulted to zero. +        bool +        ParseCoordinate(const char* id_cstr) +        { +            RegularExpression regex; +            RegularExpression::Match regex_match(3); + +            bool matched = false; +            if(regex.Compile("^([0-9]+),([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) +                matched = true; +            else if(regex.Compile("^([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) +                matched = true; +            else if(regex.Compile("^([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) +                matched = true; +            for(uint32_t i = 0; i < 3; i++) +            { +                std::string group; +                if(regex_match.GetMatchAtIndex(id_cstr, i + 1, group)) +                    m_coord[i] = (uint32_t)strtoul(group.c_str(), NULL, 0); +                else +                    m_coord[i] = 0; +            } +            return matched; +        } + +        void +        OptionParsingStarting() override +        { +            // -1 means the -c option hasn't been set +            m_coord[0] = -1; +            m_coord[1] = -1; +            m_coord[2] = -1; +        } + +        const OptionDefinition* +        GetDefinitions() override +        { +            return g_option_table; +        } + +        static OptionDefinition g_option_table[]; +        std::array<int,3> m_coord; +    }; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        const size_t argc = command.GetArgumentCount(); +        if (argc < 1) +        { +            result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name, and an optional coordinate.", m_cmd_name.c_str());              result.SetStatus(eReturnStatusFailed); -            result.AppendErrorWithFormat("Error: %s", error.AsCString());              return false;          } -        result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name", m_cmd_name.c_str()); +        RenderScriptRuntime *runtime = +                (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); + +        Error error; +        runtime->PlaceBreakpointOnKernel(result.GetOutputStream(), command.GetArgumentAtIndex(0), m_options.m_coord, +                                         error, m_exe_ctx.GetTargetSP()); + +        if (error.Success()) +        { +            result.AppendMessage("Breakpoint(s) created"); +            result.SetStatus(eReturnStatusSuccessFinishResult); +            return true; +        }          result.SetStatus(eReturnStatusFailed); +        result.AppendErrorWithFormat("Error: %s", error.AsCString());          return false;      } + +private: +    CommandOptions m_options; +}; + +OptionDefinition +CommandObjectRenderScriptRuntimeKernelBreakpointSet::CommandOptions::g_option_table[] = +{ +    { LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeValue, +      "Set a breakpoint on a single invocation of the kernel with specified coordinate.\n" +      "Coordinate takes the form 'x[,y][,z] where x,y,z are positive integers representing kernel dimensions. " +      "Any unset dimensions will be defaulted to zero."}, +    { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectRenderScriptRuntimeKernelBreakpointAll : public CommandObjectParsed +{ +public: +    CommandObjectRenderScriptRuntimeKernelBreakpointAll(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript kernel breakpoint all", +                              "Automatically sets a breakpoint on all renderscript kernels that are or will be loaded.\n" +                              "Disabling option means breakpoints will no longer be set on any kernels loaded in the future, " +                              "but does not remove currently set breakpoints.", +                              "renderscript kernel breakpoint all <enable/disable>", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) +    { +    } + +    ~CommandObjectRenderScriptRuntimeKernelBreakpointAll() override = default; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        const size_t argc = command.GetArgumentCount(); +        if (argc != 1) +        { +            result.AppendErrorWithFormat("'%s' takes 1 argument of 'enable' or 'disable'", m_cmd_name.c_str()); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        RenderScriptRuntime *runtime = +          static_cast<RenderScriptRuntime *>(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + +        bool do_break = false; +        const char* argument = command.GetArgumentAtIndex(0); +        if (strcmp(argument, "enable") == 0) +        { +            do_break = true; +            result.AppendMessage("Breakpoints will be set on all kernels."); +        } +        else if (strcmp(argument, "disable") == 0) +        { +            do_break = false; +            result.AppendMessage("Breakpoints will not be set on any new kernels."); +        } +        else +        { +            result.AppendErrorWithFormat("Argument must be either 'enable' or 'disable'"); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        runtime->SetBreakAllKernels(do_break, m_exe_ctx.GetTargetSP()); + +        result.SetStatus(eReturnStatusSuccessFinishResult); +        return true; +    } +}; + +class CommandObjectRenderScriptRuntimeKernelBreakpoint : public CommandObjectMultiword +{ +public: +    CommandObjectRenderScriptRuntimeKernelBreakpoint(CommandInterpreter &interpreter) +        : CommandObjectMultiword(interpreter, "renderscript kernel", "Commands that generate breakpoints on renderscript kernels.", +                                 nullptr) +    { +        LoadSubCommand("set", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointSet(interpreter))); +        LoadSubCommand("all", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointAll(interpreter))); +    } + +    ~CommandObjectRenderScriptRuntimeKernelBreakpoint() override = default;  };  class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeKernel(CommandInterpreter &interpreter)          : CommandObjectMultiword(interpreter, "renderscript kernel", "Commands that deal with renderscript kernels.",                                   NULL) @@ -1085,13 +3483,12 @@ class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword          LoadSubCommand("breakpoint", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpoint(interpreter)));      } -    ~CommandObjectRenderScriptRuntimeKernel() {} +    ~CommandObjectRenderScriptRuntimeKernel() override = default;  };  class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeContextDump(CommandInterpreter &interpreter)          : CommandObjectParsed(interpreter, "renderscript context dump",                                "Dumps renderscript context information.", "renderscript context dump", @@ -1099,10 +3496,10 @@ class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed      {      } -    ~CommandObjectRenderScriptRuntimeContextDump() {} +    ~CommandObjectRenderScriptRuntimeContextDump() override = default;      bool -    DoExecute(Args &command, CommandReturnObject &result) +    DoExecute(Args &command, CommandReturnObject &result) override      {          RenderScriptRuntime *runtime =              (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); @@ -1114,8 +3511,7 @@ class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed  class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeContext(CommandInterpreter &interpreter)          : CommandObjectMultiword(interpreter, "renderscript context", "Commands that deal with renderscript contexts.",                                   NULL) @@ -1123,13 +3519,345 @@ class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword          LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeContextDump(interpreter)));      } -    ~CommandObjectRenderScriptRuntimeContext() {} +    ~CommandObjectRenderScriptRuntimeContext() override = default; +}; + +class CommandObjectRenderScriptRuntimeAllocationDump : public CommandObjectParsed +{ +public: +    CommandObjectRenderScriptRuntimeAllocationDump(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript allocation dump", +                              "Displays the contents of a particular allocation", "renderscript allocation dump <ID>", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched), m_options(interpreter) +    { +    } + +    ~CommandObjectRenderScriptRuntimeAllocationDump() override = default; + +    Options* +    GetOptions() override +    { +        return &m_options; +    } + +    class CommandOptions : public Options +    { +    public: +        CommandOptions(CommandInterpreter &interpreter) : Options(interpreter) +        { +        } + +        ~CommandOptions() override = default; + +        Error +        SetOptionValue(uint32_t option_idx, const char *option_arg) override +        { +            Error error; +            const int short_option = m_getopt_table[option_idx].val; + +            switch (short_option) +            { +                case 'f': +                    m_outfile.SetFile(option_arg, true); +                    if (m_outfile.Exists()) +                    { +                        m_outfile.Clear(); +                        error.SetErrorStringWithFormat("file already exists: '%s'", option_arg); +                    } +                    break; +                default: +                    error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +                    break; +            } +            return error; +        } + +        void +        OptionParsingStarting() override +        { +            m_outfile.Clear(); +        } + +        const OptionDefinition* +        GetDefinitions() override +        { +            return g_option_table; +        } + +        static OptionDefinition g_option_table[]; +        FileSpec m_outfile; +    }; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        const size_t argc = command.GetArgumentCount(); +        if (argc < 1) +        { +            result.AppendErrorWithFormat("'%s' takes 1 argument, an allocation ID. As well as an optional -f argument", +                                         m_cmd_name.c_str()); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        RenderScriptRuntime *runtime = +          static_cast<RenderScriptRuntime *>(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + +        const char* id_cstr = command.GetArgumentAtIndex(0); +        bool convert_complete = false; +        const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); +        if (!convert_complete) +        { +            result.AppendErrorWithFormat("invalid allocation id argument '%s'", id_cstr); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        Stream* output_strm = nullptr; +        StreamFile outfile_stream; +        const FileSpec &outfile_spec = m_options.m_outfile; // Dump allocation to file instead +        if (outfile_spec) +        { +            // Open output file +            char path[256]; +            outfile_spec.GetPath(path, sizeof(path)); +            if (outfile_stream.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate).Success()) +            { +                output_strm = &outfile_stream; +                result.GetOutputStream().Printf("Results written to '%s'", path); +                result.GetOutputStream().EOL(); +            } +            else +            { +                result.AppendErrorWithFormat("Couldn't open file '%s'", path); +                result.SetStatus(eReturnStatusFailed); +                return false; +            } +        } +        else +            output_strm = &result.GetOutputStream(); + +        assert(output_strm != nullptr); +        bool success = runtime->DumpAllocation(*output_strm, m_exe_ctx.GetFramePtr(), id); + +        if (success) +            result.SetStatus(eReturnStatusSuccessFinishResult); +        else +            result.SetStatus(eReturnStatusFailed); + +        return true; +    } + +private: +    CommandOptions m_options; +}; + +OptionDefinition +CommandObjectRenderScriptRuntimeAllocationDump::CommandOptions::g_option_table[] = +{ +    { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, +      "Print results to specified file instead of command line."}, +    { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectRenderScriptRuntimeAllocationList : public CommandObjectParsed +{ +public: +    CommandObjectRenderScriptRuntimeAllocationList(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript allocation list", +                              "List renderscript allocations and their information.", "renderscript allocation list", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched), m_options(interpreter) +    { +    } + +    ~CommandObjectRenderScriptRuntimeAllocationList() override = default; + +    Options* +    GetOptions() override +    { +        return &m_options; +    } + +    class CommandOptions : public Options +    { +    public: +        CommandOptions(CommandInterpreter &interpreter) : Options(interpreter), m_refresh(false) +        { +        } + +        ~CommandOptions() override = default; + +        Error +        SetOptionValue(uint32_t option_idx, const char *option_arg) override +        { +            Error error; +            const int short_option = m_getopt_table[option_idx].val; + +            switch (short_option) +            { +                case 'r': +                    m_refresh = true; +                    break; +                default: +                    error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +                    break; +            } +            return error; +        } + +        void +        OptionParsingStarting() override +        { +            m_refresh = false; +        } + +        const OptionDefinition* +        GetDefinitions() override +        { +            return g_option_table; +        } + +        static OptionDefinition g_option_table[]; +        bool m_refresh; +    }; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        RenderScriptRuntime *runtime = +          static_cast<RenderScriptRuntime *>(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); +        runtime->ListAllocations(result.GetOutputStream(), m_exe_ctx.GetFramePtr(), m_options.m_refresh); +        result.SetStatus(eReturnStatusSuccessFinishResult); +        return true; +    } + +private: +    CommandOptions m_options; +}; + +OptionDefinition +CommandObjectRenderScriptRuntimeAllocationList::CommandOptions::g_option_table[] = +{ +    { LLDB_OPT_SET_1, false, "refresh", 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, +      "Recompute allocation details."}, +    { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectRenderScriptRuntimeAllocationLoad : public CommandObjectParsed +{ +public: +    CommandObjectRenderScriptRuntimeAllocationLoad(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript allocation load", +                              "Loads renderscript allocation contents from a file.", "renderscript allocation load <ID> <filename>", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched) +    { +    } + +    ~CommandObjectRenderScriptRuntimeAllocationLoad() override = default; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        const size_t argc = command.GetArgumentCount(); +        if (argc != 2) +        { +            result.AppendErrorWithFormat("'%s' takes 2 arguments, an allocation ID and filename to read from.", m_cmd_name.c_str()); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        RenderScriptRuntime *runtime = +          static_cast<RenderScriptRuntime *>(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + +        const char* id_cstr = command.GetArgumentAtIndex(0); +        bool convert_complete = false; +        const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); +        if (!convert_complete) +        { +            result.AppendErrorWithFormat ("invalid allocation id argument '%s'", id_cstr); +            result.SetStatus (eReturnStatusFailed); +            return false; +        } + +        const char* filename = command.GetArgumentAtIndex(1); +        bool success = runtime->LoadAllocation(result.GetOutputStream(), id, filename, m_exe_ctx.GetFramePtr()); + +        if (success) +            result.SetStatus(eReturnStatusSuccessFinishResult); +        else +            result.SetStatus(eReturnStatusFailed); + +        return true; +    } +}; + +class CommandObjectRenderScriptRuntimeAllocationSave : public CommandObjectParsed +{ +public: +    CommandObjectRenderScriptRuntimeAllocationSave(CommandInterpreter &interpreter) +        : CommandObjectParsed(interpreter, "renderscript allocation save", +                              "Write renderscript allocation contents to a file.", "renderscript allocation save <ID> <filename>", +                              eCommandRequiresProcess | eCommandProcessMustBeLaunched) +    { +    } + +    ~CommandObjectRenderScriptRuntimeAllocationSave() override = default; + +    bool +    DoExecute(Args &command, CommandReturnObject &result) override +    { +        const size_t argc = command.GetArgumentCount(); +        if (argc != 2) +        { +            result.AppendErrorWithFormat("'%s' takes 2 arguments, an allocation ID and filename to read from.", m_cmd_name.c_str()); +            result.SetStatus(eReturnStatusFailed); +            return false; +        } + +        RenderScriptRuntime *runtime = +          static_cast<RenderScriptRuntime *>(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + +        const char* id_cstr = command.GetArgumentAtIndex(0); +        bool convert_complete = false; +        const uint32_t id = StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &convert_complete); +        if (!convert_complete) +        { +            result.AppendErrorWithFormat ("invalid allocation id argument '%s'", id_cstr); +            result.SetStatus (eReturnStatusFailed); +            return false; +        } + +        const char* filename = command.GetArgumentAtIndex(1); +        bool success = runtime->SaveAllocation(result.GetOutputStream(), id, filename, m_exe_ctx.GetFramePtr()); + +        if (success) +            result.SetStatus(eReturnStatusSuccessFinishResult); +        else +            result.SetStatus(eReturnStatusFailed); + +        return true; +    } +}; + +class CommandObjectRenderScriptRuntimeAllocation : public CommandObjectMultiword +{ +public: +    CommandObjectRenderScriptRuntimeAllocation(CommandInterpreter &interpreter) +        : CommandObjectMultiword(interpreter, "renderscript allocation", "Commands that deal with renderscript allocations.", +                                 NULL) +    { +        LoadSubCommand("list", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationList(interpreter))); +        LoadSubCommand("dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationDump(interpreter))); +        LoadSubCommand("save", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationSave(interpreter))); +        LoadSubCommand("load", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationLoad(interpreter))); +    } + +    ~CommandObjectRenderScriptRuntimeAllocation() override = default;  };  class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed  { -  private: -  public: +public:      CommandObjectRenderScriptRuntimeStatus(CommandInterpreter &interpreter)          : CommandObjectParsed(interpreter, "renderscript status",                                "Displays current renderscript runtime status.", "renderscript status", @@ -1137,10 +3865,10 @@ class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed      {      } -    ~CommandObjectRenderScriptRuntimeStatus() {} +    ~CommandObjectRenderScriptRuntimeStatus() override = default;      bool -    DoExecute(Args &command, CommandReturnObject &result) +    DoExecute(Args &command, CommandReturnObject &result) override      {          RenderScriptRuntime *runtime =              (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); @@ -1152,7 +3880,7 @@ class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed  class CommandObjectRenderScriptRuntime : public CommandObjectMultiword  { -  public: +public:      CommandObjectRenderScriptRuntime(CommandInterpreter &interpreter)          : CommandObjectMultiword(interpreter, "renderscript", "A set of commands for operating on renderscript.",                                   "renderscript <subcommand> [<subcommand-options>]") @@ -1161,9 +3889,10 @@ class CommandObjectRenderScriptRuntime : public CommandObjectMultiword          LoadSubCommand("status", CommandObjectSP(new CommandObjectRenderScriptRuntimeStatus(interpreter)));          LoadSubCommand("kernel", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernel(interpreter)));          LoadSubCommand("context", CommandObjectSP(new CommandObjectRenderScriptRuntimeContext(interpreter))); +        LoadSubCommand("allocation", CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocation(interpreter)));      } -    ~CommandObjectRenderScriptRuntime() {} +    ~CommandObjectRenderScriptRuntime() override = default;  };  void @@ -1173,7 +3902,8 @@ RenderScriptRuntime::Initiate()  }  RenderScriptRuntime::RenderScriptRuntime(Process *process) -    : lldb_private::CPPLanguageRuntime(process), m_initiated(false), m_debuggerPresentFlagged(false) +    : lldb_private::CPPLanguageRuntime(process), m_initiated(false), m_debuggerPresentFlagged(false), +      m_breakAllKernels(false)  {      ModulesDidLoad(process->GetTarget().GetImages());  } @@ -1189,3 +3919,4 @@ RenderScriptRuntime::GetCommandObject(lldb_private::CommandInterpreter& interpre      return command_object;  } +RenderScriptRuntime::~RenderScriptRuntime() = default; | 
